Hack Wayground

Hack Wayground/quizizz

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey, το Greasemonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

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

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Userscripts για να εγκαταστήσετε αυτόν τον κώδικα.

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

Θα χρειαστεί να εγκαταστήσετε μια επέκταση διαχείρισης κώδικα χρήστη για να εγκαταστήσετε αυτόν τον κώδικα.

(Έχω ήδη έναν διαχειριστή κώδικα χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

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

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

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

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

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

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

(Έχω ήδη έναν διαχειριστή στυλ χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

// ==UserScript==
// @name         Hack Wayground
// @author       Trần Bảo Ngọc
// @description  Hack Wayground/quizizz
// @namespace    http://tampermonkey.net/
// @match        https://wayground.com/*
// @grant        GM_xmlhttpRequest
// @grant        unsafeWindow
// @grant        GM_addStyle
// @run-at       document-end
// @icon         https://blackarch.org/images/logo/ba-logo.png
// @version      4.0
// ==/UserScript==
(function() {
    'use strict';
    const BL = ['playerExited','playerResumed','infractionType','extensionDetected','windowResizeDetected','rightClickDetected','pasteDetected'],
          blocked = d => typeof d === 'string' && BL.some(k => d.includes(k)),
          _fetch = window.fetch, _xhrSend = XMLHttpRequest.prototype.send, _parse = JSON.parse;

    window.fetch = async function(...a) {
        return a[1]?.body && blocked(a[1].body) ? new Response('{"success":true}', {status:200}) : _fetch.apply(this, a);
    };
    XMLHttpRequest.prototype.send = function(b) {
        if (blocked(b)) {
            Object.defineProperties(this, {readyState:{value:4,configurable:1},status:{value:200,configurable:1}});
            return this.onreadystatechange?.();
        }
        _xhrSend.apply(this, arguments);
    };
    JSON.parse = function(...a) {
        const r = _parse.apply(this, a);
        if (r?.type === 'RN_APP_STATE_CHANGE' && r.value === 'background') r.value = 'foreground';
        return r;
    };

    const stop = e => e.stopImmediatePropagation();
    'visibilitychange blur mouseleave pagehide resize contextmenu copy paste fullscreenchange webkitfullscreenchange'
        .split(' ').forEach(e => (window.addEventListener(e, stop, !0), document.addEventListener(e, stop, !0)));

    const def = (o, p, v) => { try { Object.getOwnPropertyDescriptor(o, p)?.configurable !== !1 && Object.defineProperty(o, p, {get:()=>v, configurable:!0}) } catch{} },
          el = () => document.documentElement;
    for (const o of [Document.prototype, document]) {
        def(o, 'visibilityState', 'visible'); def(o, 'hidden', !1);
        def(o, 'fullscreenElement', el); def(o, 'webkitFullscreenElement', el);
    }
    window.onblur = document.onblur = null;
    document.hasFocus = () => !0;

    window.addEventListener('keydown', e => {
        if (e.key === 'F2') (document.fullscreenElement ? document.exitFullscreen() : document.documentElement.requestFullscreen()).catch(() => {});
    }, !0);
    GM_addStyle(`
        #solver-panel{position:fixed;bottom:20px;left:20px;z-index:999999;padding:12px;background:rgba(26,27,30,.85);backdrop-filter:blur(10px);border-radius:16px;box-shadow:0 8px 30px rgba(0,0,0,.4);min-width:260px;max-width:320px;border:1px solid rgba(255,255,255,.1)}
        #solver-status{color:#fff;font-size:15px;font-weight:600;margin-bottom:10px;transition:.3s;text-align:left;word-wrap:break-word;white-space:normal}
        #pin-container{display:flex;gap:8px}
        #pin-input{flex:1;border:1px solid rgba(255,255,255,.2);background:rgba(0,0,0,.3);color:#fff;border-radius:8px;padding:8px 12px;font-size:14px;outline:0;text-align:center;transition:.2s}
        #pin-input:focus{border-color:#a78bfa}
        #load-btn{background:linear-gradient(135deg,#a78bfa,#8b5cf6);border:0;border-radius:8px;color:#fff;font-weight:600;padding:0 20px;cursor:pointer;transition:.2s}
        #load-btn:hover{transform:scale(1.05)}
        #load-btn:disabled{cursor:not-allowed;background:#555}
        *{user-select:text!important;-webkit-user-select:text!important}
    `);

    const cache = new Map();
    let lastQid = '';
    const clean = t => t?.replace(/<\/?p>/g, '').trim().replace(/\s+/g, ' ') || '';

    const $ = s => document.querySelector(s);
    const $$ = s => [...document.querySelectorAll(s)];

    function findGamePin() {
        const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
        let node;
        while (node = walker.nextNode()) {
            const m = node.nodeValue.trim().match(/\b(\d{4})\s(\d{4})\b/);
            if (m && node.parentElement?.offsetParent !== null) return m[0].replace(/\s/g, '');
        }
        return null;
    }

    async function fetchAnswers(pin, status) {
        status.textContent = '🌀 Đang tải đáp án...';
        status.style.color = '#fff';
        try {
            const res = await _fetch(`https://api.quizit.online/quizizz?pin=${pin}`);
            if (!res.ok) throw new Error(`API: ${res.status}`);
            const data = await res.json();
            const list = data.answers || data.data?.answers;
            if (!list?.length) throw new Error('Không có đáp án từ API');

            for (const item of list) {
                const id = item.id || item._id;
                if (!id) continue;
                if (item.type === 'OPEN') { cache.set(id, '📝 Tự luận'); continue; }
                if (item.type === 'MSQ' && Array.isArray(item.answers)) {
                    const ans = item.answers.map(a => clean(a.text)).filter(Boolean);
                    if (ans.length) cache.set(id, ans);
                } else {
                    const ans = clean(item.answers?.[0]?.text);
                    if (ans) cache.set(id, ans);
                }
            }
            if (!cache.size) throw new Error('Đáp án rỗng');
            return true;
        } catch (e) {
            status.textContent = `❌ ${e.message}`;
            status.style.color = '#ff5555';
            return false;
        }
    }

    function getQuestion() {
        const container = $('[data-quesid]');
        if (!container) return null;
        const qid = container.dataset.quesid;
        const options = $$('.option.is-selectable').map(el => ({
            text: clean(el.querySelector('.option-text-inner, .text-container')?.innerText),
            element: el,
        }));
        if (options.length) return { qid, type: 'CHOICE', options };
        if ($('input.question-input, textarea.question-input, input[type="text"], textarea'))
            return { qid, type: 'BLANK' };
        return null;
    }

    function autoSubmit() {
        setTimeout(() => {
            const btn = $$('button').find(b => b.innerText.trim().toLowerCase() === 'submit')
                     || $('.submit-button-wrapper button, button.submit-btn');
            if (btn && !btn.disabled) btn.click();
        }, 350);
    }

    function solve(answer, q) {
        if (q.type === 'CHOICE') {
            const targets = Array.isArray(answer) ? answer : [answer];
            targets.forEach(t => {
                const opt = q.options.find(o => o.text === t);
                if (opt) { opt.element.style.border = '4px solid #00FF00'; opt.element.click(); }
            });
            if (Array.isArray(answer)) autoSubmit();
        } else if (q.type === 'BLANK') {
            const input = $('input.question-input, textarea.question-input, input[type="text"], textarea');
            if (input) {
                input.value = answer;
                input.dispatchEvent(new Event('input', { bubbles: true }));
                autoSubmit();
            }
        }
    }

    function mainSolver(status) {
        if (!cache.size) return;
        const q = getQuestion();
        if (!q?.qid) return;
        const ans = cache.get(q.qid);
        if (ans) {
            const display = Array.isArray(ans) ? ans.join('<br>') : ans;
            status.innerHTML = `💡 Đáp án:<div style="margin-top:5px;color:#50fa7b;font-weight:400">${display}</div>`;
            if (typeof ans === 'string' && ans.startsWith('📝')) return;
            solve(ans, q);
        } else {
            status.textContent = '❓ Không tìm thấy đáp án';
            status.style.color = '#ff5555';
        }
    }

    function startObserver(status) {
        new MutationObserver(() => {
            const qid = $('[data-quesid]')?.dataset.quesid;
            if (qid && qid !== lastQid) { lastQid = qid; setTimeout(() => mainSolver(status), 500); }
        }).observe(document.body, { childList: !0, subtree: !0 });
    }

    function init() {
        if ($('#solver-panel')) return;
        document.body.insertAdjacentHTML('beforeend', `
            <div id="solver-panel">
                <div id="solver-status">🔎 Đang tìm Room Code...</div>
                <div id="pin-container">
                    <input type="text" id="pin-input" placeholder="Chờ chút...">
                    <button id="load-btn">Tải</button>
                </div>
            </div>`);

        const btn = $('#load-btn'), input = $('#pin-input'), status = $('#solver-status'), pinBox = $('#pin-container');

        const handleLoad = async () => {
            const pin = input.value.trim().replace(/\s/g, '');
            if (!pin) return;
            btn.disabled = input.disabled = true;
            if (await fetchAnswers(pin, status)) {
                pinBox.style.display = 'none';
                status.textContent = '🚀 Sẵn sàng! Đang chờ câu hỏi...';
                status.style.color = '#fff';
                startObserver(status);
            } else btn.disabled = input.disabled = false;
        };

        btn.addEventListener('click', handleLoad);
        input.addEventListener('keydown', e => e.key === 'Enter' && handleLoad());

        const finder = setInterval(() => {
            const pin = findGamePin();
            if (pin) {
                clearInterval(finder);
                input.value = pin;
                status.textContent = '✅ Đã tìm thấy Room Code';
                status.style.color = '#50fa7b';
                setTimeout(handleLoad, 500);
            }
        }, 1000);
        setTimeout(() => clearInterval(finder), 20000);
    }

    window.addEventListener('load', () => setTimeout(init, 1000));
})();