Hack Wayground

Hack Wayground/quizizz

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

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

Tendrás que instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Tendrás que instalar una extensión como Tampermonkey antes de poder instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==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));
})();