Quiz House Answer Local Database

After every round, correct answer is saving to local database. You can use that later with "Pick Correct Answer" button. You can also save image answers.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Quiz House Answer Local Database
// @namespace    http://tampermonkey.net/
// @version      1.0.4b
// @description  After every round, correct answer is saving to local database. You can use that later with "Pick Correct Answer" button. You can also save image answers.
// @author       Meffiu
// @match        https://app.quizhouse.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=quizhouse.com
// @grant        GM_setValue
// @grant        GM_getValue
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    let lastQuestion = '',
        instantPickEnabled = false,
        pickRandomAnswer = false,
        instantPickInterval = null,
        playAgainInterval = null,
        alertOffset = 0;

    function initDatabase() {
        let data = loadData('qh_answers');
        let dataImg = loadData('qh_answers_imgs');
        console.log(data);

        if (!data) {
            data = {};
            saveData('qh_answers', data);
        }
        if (!dataImg) {
            dataImg = {};
            saveData('qh_answers_imgs', dataImg)
        }
    }

    function createInstantPickButton() {
        const instantPickButton = document.createElement('button');
        instantPickButton.textContent = 'Instant Pick Correct Answer';
        instantPickButton.style.backgroundColor = 'red';
        instantPickButton.style.color = 'white';
        instantPickButton.style.marginTop = '10px';
        instantPickButton.style.padding = '5px';
        instantPickButton.style.borderRadius = '5px';
        instantPickButton.style.fontSize = '12px';
        instantPickButton.style.marginRight = '5px';
        instantPickButton.addEventListener('click', function() {
            instantPickEnabled = !instantPickEnabled;
            instantPickButton.style.backgroundColor = instantPickEnabled ? 'green' : 'red';
            if (instantPickEnabled) {
                instantPickAnswer();
                updateStatsBox();
            } else {
                clearInterval(instantPickInterval);
            }
        });
        return instantPickButton;
    }

    function createCheckButton() {
        const checkButton = document.createElement('button');
        checkButton.textContent = 'Pick Correct Answer';
        checkButton.style.backgroundColor = 'green';
        checkButton.style.color = 'white';
        checkButton.style.marginTop = '10px';
        checkButton.style.padding = '5px';
        checkButton.style.borderRadius = '5px';
        checkButton.style.fontSize = '12px';
        checkButton.addEventListener('click', checkQuestion);
        return checkButton;
    }

    function createAutoPlayAgainButton() {
        const autoPlayAgainButton = document.createElement('button');
        autoPlayAgainButton.textContent = 'Auto Play Again';
        autoPlayAgainButton.style.backgroundColor = 'red';
        autoPlayAgainButton.style.color = 'white';
        autoPlayAgainButton.style.marginTop = '10px';
        autoPlayAgainButton.style.padding = '5px';
        autoPlayAgainButton.style.borderRadius = '5px';
        autoPlayAgainButton.style.fontSize = '12px';
        autoPlayAgainButton.style.marginRight = '5px';
        let autoPlayAgainEnabled = false;
        autoPlayAgainButton.addEventListener('click', function() {
            autoPlayAgainEnabled = !autoPlayAgainEnabled;
            autoPlayAgainButton.style.backgroundColor = autoPlayAgainEnabled ? 'green' : 'red';
            if (autoPlayAgainEnabled) {
                autoPlayAgain();
            } else {
                clearInterval(playAgainInterval);
            }
        });
        return autoPlayAgainButton;
    }

    function createPickRandomAnswerButton() {
        const pickRandomAnswerButton = document.createElement('button');
        pickRandomAnswerButton.textContent = 'Pick Random Answer If Correct Not Found';
        pickRandomAnswerButton.style.backgroundColor = 'red';
        pickRandomAnswerButton.style.color = 'white';
        pickRandomAnswerButton.style.marginTop = '10px';
        pickRandomAnswerButton.style.padding = '5px';
        pickRandomAnswerButton.style.borderRadius = '5px';
        pickRandomAnswerButton.style.fontSize = '12px';
        pickRandomAnswerButton.addEventListener('click', function() {
            pickRandomAnswer = !pickRandomAnswer;
            pickRandomAnswerButton.style.backgroundColor = pickRandomAnswer ? 'green' : 'red';
        });
        return pickRandomAnswerButton;
    }

    function createToggleButton() {
        const toggleButton = document.createElement('button');
        toggleButton.textContent = 'Toggle StatsBox';
        toggleButton.style.backgroundColor = 'blue';
        toggleButton.style.color = 'white';
        toggleButton.style.marginTop = '10px';
        toggleButton.style.padding = '5px';
        toggleButton.style.borderRadius = '5px';
        toggleButton.style.fontSize = '12px';
        toggleButton.style.display = 'block';
        toggleButton.addEventListener('click', function() {
            const statsBox = document.getElementById('statsBox');
            if (statsBox.style.display === 'none') {
                statsBox.style.display = 'block';
            } else {
                statsBox.style.display = 'none';
            }
        });
        return toggleButton;
    }

    function autoPlayAgain() {
        playAgainInterval = setInterval(() => {
            const button = document.querySelector('button.css-1tom5sh');
            if (button) {
                button.click();
            }
        }, 3000);
    }

    function instantPickAnswer() {
        instantPickInterval = setInterval(() => {
            checkQuestion();
        }, 1000);
    }

    function updateStatsBox() {
        const data = loadData('qh_answers');
        const dataImg = loadData('qh_answers_imgs');

        let totalAnswers = 0;
        for (let key in data) {
            totalAnswers += data[key].length;
        }

        let totalImgAnswers = 0;
        for (let key in dataImg) {
            totalImgAnswers += dataImg[key].length;
        }

        let statsContainer = document.getElementById('statsContainer');
        if (!statsContainer) {
            statsContainer = document.createElement('div');
            statsContainer.id = 'statsContainer';
            statsContainer.style.position = 'fixed';
            statsContainer.style.bottom = '20px';
            statsContainer.style.left = '20px';
            document.body.appendChild(statsContainer);
        }

        let buttonsContainer = document.getElementById('buttonsContainer');
        if (!buttonsContainer) {
            buttonsContainer = document.createElement('div');
            buttonsContainer.id = 'buttonsContainer';
            buttonsContainer.style.display = 'flex';
            statsContainer.appendChild(buttonsContainer);
        }

        let secondButtonsContainer = document.getElementById('secondButtonsContainer');
        if (!secondButtonsContainer) {
            secondButtonsContainer = document.createElement('div');
            secondButtonsContainer.id = 'secondButtonsContainer';
            secondButtonsContainer.style.display = 'flex';
            statsContainer.appendChild(secondButtonsContainer);
        }

        let instantPickButton = document.getElementById('instantPickButton');
        if (!instantPickButton) {
            instantPickButton = createInstantPickButton();
            instantPickButton.id = 'instantPickButton';
            buttonsContainer.appendChild(instantPickButton);
        }

        let checkButton = document.getElementById('checkButton');
        if (!checkButton) {
            checkButton = createCheckButton();
            checkButton.id = 'checkButton';
            buttonsContainer.appendChild(checkButton);
        }

        checkButton.style.display = instantPickEnabled ? 'none' : 'block';

        let autoPlayAgainButton = document.getElementById('autoPlayAgainButton');
        if (!autoPlayAgainButton) {
            autoPlayAgainButton = createAutoPlayAgainButton();
            autoPlayAgainButton.id = 'autoPlayAgainButton';
            secondButtonsContainer.appendChild(autoPlayAgainButton);
        }

        let pickRandomAnswerButton = document.getElementById('pickRandomAnswerButton');
        if (!pickRandomAnswerButton) {
            pickRandomAnswerButton = createPickRandomAnswerButton();
            pickRandomAnswerButton.id = 'pickRandomAnswerButton';
            secondButtonsContainer.appendChild(pickRandomAnswerButton);
        }

        pickRandomAnswerButton.style.display = instantPickEnabled ? 'block' : 'none';

        let toggleButton = document.getElementById('toggleButton');
        if (!toggleButton) {
            toggleButton = createToggleButton();
            toggleButton.id = 'toggleButton';
            statsContainer.appendChild(toggleButton);
        }

        let statsBox = document.getElementById('statsBox');
        if (!statsBox) {
            statsBox = document.createElement('div');
            statsBox.id = 'statsBox';
            statsBox.style.padding = '10px';
            statsBox.style.color = 'white';
            statsBox.style.backgroundColor = 'black';
            statsContainer.appendChild(statsBox);
        }
        statsBox.innerHTML = `QH Answer Local Database v${GM_info.script.version}<br>Answers: ${totalAnswers}<br>Image Answers: ${totalImgAnswers}`;
    }

    function showAlert(message, color, duration) {
        const alertBox = document.createElement('div');

        const formattedMessage = message.replace(/\n/g, '<br>');

        alertBox.innerHTML = formattedMessage;
        alertBox.style.position = 'fixed';
        alertBox.style.bottom = `${20 + alertOffset}px`;
        alertBox.style.right = '20px';
        alertBox.style.padding = '10px';
        alertBox.style.color = 'white';
        alertBox.style.backgroundColor = color;
        alertBox.style.maxWidth = '400px';
        alertBox.style.whiteSpace = 'nowrap';
        alertBox.style.overflow = 'auto';

        document.body.appendChild(alertBox);

        setTimeout(() => {
            document.body.removeChild(alertBox);
            alertOffset -= 50;
        }, duration);

        alertOffset += 50;
    }

    function saveData(key, data) {
        GM_setValue(key, JSON.stringify(data));
    }

    function loadData(key) {
        const data = GM_getValue(key, null);
        return data ? JSON.parse(data) : null;
    }

    function saveQuestion() {
        const data = loadData('qh_answers');
        const dataImg = loadData('qh_answers_imgs')
        const question = document.querySelector('div.css-219n91 p').textContent;
        const answer = document.querySelector('div.css-1wxx39n')?.textContent || document.querySelector('div.css-91izyb')?.textContent;
        const image = document.querySelector('img.css-18a88ps')?.getAttribute('src');

        if (!question || !answer) {
            return showAlert('Could not extract question or answer!', 'red', 3000);
        }

        if (image) {
            dataImg[question] = dataImg[question] || [];
            if (!dataImg[question]?.some(i => i.src === image)) {
                dataImg[question].push({ src: image, answer: answer });
                saveData('qh_answers_imgs', dataImg);
                showAlert(`Saved answer "${answer}" to question "${question}"`, 'green', 3000);
            }
        }

        else {
            data[question] = data[question] || [];
            if (!data[question].includes(answer)) {
                data[question].push(answer);
                saveData('qh_answers', data);
                showAlert(`Saved answer "${answer}" to question "${question}"`, 'green', 3000);
            }
        }

        updateStatsBox();
    }

    function checkQuestion() {
        const data = loadData('qh_answers');
        const dataImg = loadData('qh_answers_imgs');
        const question = document.querySelector('div.css-219n91 p').textContent;
        const image = document.querySelector('img.css-18a88ps')?.getAttribute('src');

        if (image) {
            if (dataImg[question]) {
                const imgAnswer = dataImg[question].find(i => i.src === image)?.answer;
                if (imgAnswer) {
                    const divs = Array.from(document.querySelectorAll('div'));
                    const targetDiv = divs.find(div => div.innerText === imgAnswer);
                    if (targetDiv) {
                        return targetDiv.click();
                    } else {
                        showAlert(`No div with the saved answer found!\n\nAnswer: ${imgAnswer}`, 'red', 5000);
                    }
                } else {
                    showAlert('No answer to that question with image in database!', 'red', 3000);
                }
            } else {
                showAlert('No data for this question in database!', 'red', 3000);
            }
        }

        else if (data[question] && data[question].length > 0) {
            const divs = Array.from(document.querySelectorAll('div'));
            let targetDiv = null;
            for (let i = 0; i < data[question].length; i++) {
                targetDiv = divs.find(div => div.innerText === data[question][i]);
                if (targetDiv) {
                    return targetDiv.click();
                }
            }
            if (!targetDiv) {
                showAlert(`No answer to that question in database!`, 'red', 3000);
            }
        } else {
            showAlert('No answer to that question in database!', 'red', 3000);
        }

        if (pickRandomAnswer) {
            const parentDiv = document.querySelector('.css-12zg2fx') || document.querySelector('.css-1dkyqax');
            if (parentDiv) {
                const divs = Array.from(parentDiv.querySelectorAll('div'));
                if (divs.length > 0) {
                    const randomDiv = divs[Math.floor(Math.random() * divs.length)];
                    randomDiv.click();
                }
            }
        }
    }

    const qhObserver = new MutationObserver((qhMutationsList, qhObserver) => {
        for (let mutation of qhMutationsList) {
            if(mutation.type === 'attributes') {
                if(mutation.attributeName === 'class') {
                    const targetElement = mutation.target;
                    if(targetElement.classList.contains('css-1wxx39n') || targetElement.classList.contains('css-91izyb')) {
                        saveQuestion();
                    }
                }
            }
        }
    });
    const qhConfig = { attributes: true, attributeFilter: ['class'], subtree: true };
    qhObserver.observe(document, qhConfig);

    initDatabase();

    updateStatsBox();

    showAlert('Quiz House Answer Local Database loaded!\n\nClick that green "Pick Corrent Answer" button to guess answer to actual question.', 'green', 8000);
})();