Quiz House Answer Local Database

Correct answer saver.

当前为 2024-05-27 提交的版本,查看 最新版本

// ==UserScript==
// @name         Quiz House Answer Local Database
// @namespace    http://tampermonkey.net/
// @version      1.0.2a
// @description  Correct answer saver.
// @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;

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

        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.addEventListener('click', function() {
            instantPickEnabled = !instantPickEnabled;
            instantPickButton.style.backgroundColor = instantPickEnabled ? 'green' : 'red';
            updateStatsBox();
        });
        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 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 updateStatsBox() {
        const data = loadData('qh_answers');
        const dataImg = loadData('qh_answers_imgs');

        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 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 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: ${Object.keys(data).length}<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 = '20px';
        alertBox.style.right = '20px';
        alertBox.style.padding = '10px';
        alertBox.style.color = 'white';
        alertBox.style.backgroundColor = color;

        document.body.appendChild(alertBox);

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

    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 if (!data[question]) {
            data[question] = 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) {
                        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]) {
            const divs = Array.from(document.querySelectorAll('div'));
            const targetDiv = divs.find(div => div.innerText === data[question]);
            if (targetDiv) {
                targetDiv.click();
            } else {
                showAlert(`No div with the saved answer found!\n\nAnswer: ${data[question]}`, 'red', 5000);
            }
        } else {
            showAlert('No answer to that question in database!', 'red', 3000);
        }
    }

    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);

    setInterval(() => {
        const questionElement = document.querySelector('div.css-219n91 p');
        if (questionElement && instantPickEnabled) {
            const currentQuestion = questionElement.textContent;
            if (currentQuestion !== lastQuestion) {
                checkQuestion();
                lastQuestion = currentQuestion;
            }
        }
    }, 1000);

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