ARX-M theme and bookwork check skipper for sparx maths

skip bookwork check and see answer ONCE INPUT entered on sparx

目前为 2023-07-01 提交的版本。查看 最新版本

// ==UserScript==
// @name         ARX-M theme and bookwork check skipper for sparx maths
// @namespace    http://tampermonkey.net/
// @version      1.19.7
// @liscence     MIT
// @description  skip bookwork check and see answer ONCE INPUT entered on sparx
// @author       ben
// @match        https://www.sparxmaths.uk/student
// @icon         https://www.wgu.edu/content/dam/web-sites/blog-newsroom/blog/images/national/2019/august/grey-hat-hacking.jpg
// @grant        none
// ==/UserScript==

console.log("Script is running :)");

const sleep = ms => {
    return new Promise(resolve => setTimeout(resolve, ms))
}

// THEMES
const themes = {
    1: {
        'darkest': '#212529',
        'dark':  '#343a40',
        'light':  '#495057',
        'lightest': '#6c757d',
    },

    2: {
        'darkest': '#7f5539',
        'dark':  '#9c6644',
        'light':  '#b08968',
        'lightest': '#ddb892',
    },

    3: {
        'darkest': '#241E92',
        'dark':  '#5432D3',
        'light':  '#7B6CF6',
        'lightest': '#E5A5FF',
    },

    4: {
        'darkest': '#10002b',
        'dark':  '#3c096c',
        'light':  '#5a189a',
        'lightest': '#9d4edd',
    },

    5: {
        'darkest': '#001233',
        'dark':  '#001845',
        'light':  '#023e7d',
        'lightest': '#0466c8',
    },

    6: {
        'darkest': '#590d22',
        'dark':  '#800f2f',
        'light':  '#a4133c',
        'lightest': '#ff4d6d',
    },

    7: {
        'darkest': '#000000',
        'dark':  '#3D0000',
        'light':  '#950101',
        'lightest': '#EC0000',
    },

    8: {
        'darkest': '#132a13',
        'dark':  '#31572c',
        'light':  '#4f772d',
        'lightest': '#90a955',
    },

    9: {
        'darkest': '#1a001c',
        'dark':  '#250327',
        'light':  '#2f0631',
        'lightest': '#3a093c',
    }
}

const themeStyles = '.themes-container{display:flex;align-items:center;justify-content:center;flex-direction:column;width:1px}.themes-container ul{border:2px solid #fff;padding:0;display:flex;flex-direction:column;width:0%}.themes-container ul li{list-style-type:none;display:flex;flex-direction:row}.themes-container ul li div{background-color:orange;height:0px;width:25%}.themes-container ul li:hover{cursor:pointer;filter:brightness(80%)}@media (max-width: 1000px){.themes-container{width:60vw}}';
const darkModeStyles = ':root{--orange:#F46815;--grey:#F8F8F7;--dark-grey:#E9E9E9;--darkest:#241E92;--dark:#5432D3;--light:#7B6CF6;--lightest:#E5A5FF}.um-login-container,.package-container,.view-body,.main-view{background:var(--darkest)!important}.entry-area-bubble .text,.answer .markdown.text-container{color:var(--darkest)!important}.package-filter-list,.revision-homework-button-container,.revision-topic-page,.package-list > div > span > ul > div,.rewards-section.insights-lifetime-totals,.rewards-section-header,.rewards-section-content,.rewards-progress-levels,.rewards-faqs .accordion-element-header{background:var(--dark)!important}.package-list > div > span > ul > div,.rewards-section{border-color:var(--dark)!important}.um-header,.package-heading,.footer-cookie-banner-container,.footer-container,.revision-tab,.revision-task,.revision-strand-button,.revision-strand-page,.revision-homework-button,.activity-feed-day,.status-bar,.status-bar-label-text,.btn-menu-item,.question-only,.answer-only,.question-text,.skill-delivery-view .view-body,.wac-text-container .bookwork-code,.insights-lifetime-total,.rewards-section-row,.rewards-progress-level:hover{background:var(--light)!important}.revision-tabs{border-bottom:var(--light)!important}.btn-menu-item,.package-heading,.revision-homework-button{border:1px solid var(--light)!important}.wac-text-container .bookwork-code,.accordion-element-header,.activity-feed-work,.dummytaskitem{border:none!important}.um-login-box__content,.revision-tab.revision-tab-active,.accordion-element-header,.activity-feed-work,.status-bar-menu-item,.status-bar-menu-button,.revision-task-item,#answer-wac-box,.choice-wac-options{background:var(--lightest)!important}.status-bar-menu-button{border:solid var(--lightest)!important}.status-bar-menu-item{border-color:var(--lightest)!important}.school-selector,.revision-strand-button,.accordion-element-header,.revision-substrand-extra,.activity-feed-day > h2,.activity-feed-work,.activity-feed-work-counts,.revision-location-stream,.btn-menu-item,.revision-topic-page,.revision-homework-button,.package-heading,.question-text > div > .text,.result-inner h2,.result-inner .result-subtitle-prominent,.result-inner h1.incorrect,.text-container,.answer-part > div > .text,.wac-header-container,.wac-text,.minigame-description > div,.rewards-section-header-title,.insights-lifetime-total,.rewards-section-row,.rewards-progress-level-label-text{color:var(--grey)!important}.answer-markup.choice-wac-option.choice.choice-answer-markup,.choice-text{background:var(--grey)!important}.revision-location-stream,.revision-strand-button{border-color:var(--grey)!important}.active{border:1px solid var(--grey)!important}.package-filter-arrow{border-left-color:var(--grey)!important}.package-list > div > span > ul > div > .task-title{color:var(--dark-grey)!important}.selected .text,.choice-wac-option.selected,.rewards-progress-level:hover .rewards-progress-level-label-text{color:var(--orange)!important}.rewards-section-header{border-radius:0!important}.revision-strand-icon{filter:grayscale(100%) brightness(5)!important}.status-bar-menu-item:not(:first-child):before{left:-3px!important;width:110%!important;border-bottom:1px solid var(--darkest)!important}.status-bar-menu-item-img{filter:grayscale(100%) brightness(5)!important}.taskitem > .icon{filter:brightness(5)!important}.status-bar-menu-item:hover > .status-bar-menu-item-img{filter:none!important}';

// Katex
katexCSS = document.createElement('link');
katexCSS.href = "https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.css";
katexCSS.rel = "stylesheet"
document.head.appendChild(katexCSS);

katexJS = document.createElement('script');
katexJS.src = 'https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.js';
document.head.appendChild(katexJS);

// CSS
document.head.insertAdjacentHTML('beforeend','<style>' + themeStyles + '</style>');
document.head.insertAdjacentHTML('beforeend','<style>' + darkModeStyles + '</style>');

// Colour palette
document.documentElement.style.setProperty('--darkest', themes[1]['darkest']);
document.documentElement.style.setProperty('--dark', themes[1]['dark']);
document.documentElement.style.setProperty('--light', themes[1]['light']);
document.documentElement.style.setProperty('--lightest', themes[1]['lightest']);

const grey = '#f8f8f7';
const orange = '#f46815';

console.log(JSON.parse(localStorage.getItem('sparx-data')));

const mutationObserver = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
        main();
    });
});

mutationObserver.observe(document.documentElement, {
    attributeFilter: [ "class" ],
    characterData: true,
    childList: true,
    subtree: true,
    characterDataOldValue: true
});

// DATABASE

const appName = 'data-pddjp';
const apiKey = 'EfQF4RWfsTK3roinoSSe8p7BsOImNRWTlSP4yYoW1Q87C44m8wzlA8BUyZLWkK3K';
const mongoDatabaseURL = `https://data.mongodb-api.com/app/${appName}/endpoint/data/v1/action/`;
let authorisedToken = null;
let userDocumentId = null;
let userName = null;
let userData = null;
let question = null;
let answer = null;

async function checkUser() {
    let response = await contactDatabase('find', 'users', 'user-data');

    // Initialise the 'users' collection if it doesn't exist
    if (response.documents.length == 0) {
        await contactDatabase('insertOne', 'users', 'user-data', {users: {}});
        response = await contactDatabase('find', 'users', 'user-data');
    }

    const documentId = response.documents[0]._id;
    const users = response.documents[0].users;
    userDocumentId = response.documents[0].users[userName];

    // Check if the ID points to an existing document in the 'answers' database
    response = await contactDatabase('findOne', 'answers', 'user-data', { "_id": { "$oid": users[userName] }});

    if (!userName in users) {
        // console.log(`${userName} is not in the database`);

        // Create a new document for the user's answers in the 'answers' database
        newDocumentId = await (await contactDatabase('insertOne', 'answers', 'user-data', {})).insertedId;
        // console.log(newDocumentId);

        users[userName] = newDocumentId;

        contactDatabase('updateOne', 'users', 'user-data', [{"users": users}, documentId]);

        return
    } else if (await( await contactDatabase('findOne', 'answers', 'user-data', { "_id": { "$oid": userDocumentId }})).document === null) {
        // console.log(`${userName} document is not in the 'answers' database`);

        // Create a new document for the user's answers in the 'answers' database
        newDocumentId = await (await contactDatabase('insertOne', 'answers', 'user-data', {answers: {}})).insertedId;
        // console.log(newDocumentId);

        users[userName] = newDocumentId;

        contactDatabase('updateOne', 'users', 'user-data', [{"users": users}, documentId]);
    } else {
        console.log(`${userName} is already in the database`);
        console.log(`${userName}'s document ID: ${users[userName]}`)
        userData = await (await contactDatabase('findOne', 'answers', 'user-data', { "_id": { "$oid": userDocumentId }})).document.answers;
        console.log(userData);
    }
}

async function authorise() {
    if (authorisedToken === null) {
      let jsondata = {
        'key': apiKey
      }

      let settings = {
        "async": true,
        "crossDomain": true,
        "method": "POST",
        "headers": {
          'Content-Type': 'application/json'
        },
        'processData': false,
        body: JSON.stringify(jsondata)
      }

      let response = await (await fetch(`https://realm.mongodb.com/api/client/v2.0/app/${appName}/auth/providers/api-key/login`, settings)).json();
      authorisedToken = response.access_token;
    }
    return authorisedToken;
}

async function contactDatabase(action, database, collection, content=false) {
    let token = await authorise();

    const jsonData = {
        'database': database,
        'collection': collection,
        'dataSource': 'Sparx',
    }

    if (content !== false && action == 'updateOne') {
        jsonData.filter = { "_id": { "$oid": content[1] } };
        jsonData.update = content[0];
    } else if (content !== false && action == 'findOne') {
        jsonData.filter = content;
    } else if (content !== false) {
        jsonData.document = content;
    }

    const settings = {
        "async": true,
        "crossDomain": true,
        "method": "POST",
        headers: {
            'Authorization': 'Bearer ' + token,
            'Content-Type': 'application/json'
        },
        'processData': false,
        body: JSON.stringify(jsonData)
    }

    response = await (await fetch(mongoDatabaseURL + action, settings)).json();

    return response
}

async function main() {
    if (document.querySelector('.package-container') !== null) { showThemes(); }

    // Display stored answer
    let correctStatusElement = document.querySelector('.page.result .result-inner .correct');
    if (correctStatusElement !== null && document.querySelector('#shown-answer') === null) {
        try {
            const bookworkCodeElement = document.querySelector('.bookwork-code');
            let bookworkCode = bookworkCodeElement.textContent;
            bookworkCode = bookworkCode.replace("Bookwork code: ", '');

            const sparxData = JSON.parse(localStorage.getItem('sparx-data'));
            let answer = sparxData[bookworkCode];
            answer = answer.map(element => element.replace('\n', ''));
            answer = answer.map(element => element.replace(/\\\\/g, '\\'));

            // Show saved answer
            if (answer.every(hasSource)) {
                const divNode = document.createElement('div');
                const imageNode = document.createElement('img');
                imageNode.src = answer.toString();
                imageNode.setAttribute('id', 'shown-answer');
                imageNode.style['height'] = "4rem";

                divNode.appendChild(imageNode);
                divNode.style['margin-bottom'] = '20px';
                divNode.style['margin-top'] = '20px';
                divNode.style.color = grey;
                document.querySelector('.location-title').append(divNode);
            } else {
                let answers = answer.join(', ');

                const textNode = document.createElement('span');
                textNode.textContent = `Answer: ${answers}`;
                textNode.style['margin'] = '0.3rem 1rem 0 0';
                textNode.style['font-size'] = '2.5rem';
                textNode.style['color'] = 'white';
                textNode.setAttribute('id', 'shown-answer');

                const divNode = document.createElement('div');
                divNode.style['display'] = 'flex';
                divNode.style['align-items'] = 'center';
                divNode.style['justify-content'] = 'center';

                divNode.appendChild(textNode);
                divNode.style['margin-bottom'] = '20px';
                divNode.style['margin-top'] = '20px';
                document.querySelector('.result-inner').append(divNode);

                katex.render(answers, document.getElementById('shown-answer'), {
                    throwOnError: false
                });
            }
        } catch(err) {console.log(err)}
    }

    // Display correct bookwork code
    const bookworkCodeElement = document.querySelector('.wac-text-container .bookwork-code');
    if (bookworkCodeElement !== null && document.querySelector('#custom-answer') === null) {
        try {
            let bookworkCode = bookworkCodeElement.textContent;
            bookworkCode = bookworkCode.replace("Bookwork code: ", '');
            console.log(bookworkCode);

            const sparxData = JSON.parse(localStorage.getItem('sparx-data'));
            let bookworkAnswer = sparxData[bookworkCode];
            bookworkAnswer = bookworkAnswer.map(element => element.replace('\n', ''));
            bookworkAnswer = bookworkAnswer.map(element => element.replace(/\\\\/g, '\\'));
            console.log(bookworkAnswer);

            // Show saved answer
            if (!bookworkAnswer.every(hasCurly)) {
                const textNode = document.createElement('b');
                const divNode = document.createElement('div');
                textNode.innerText = `Answer: ${bookworkAnswer.join(', ')}`;
                textNode.setAttribute('id', 'custom-answer');

                divNode.appendChild(textNode);
                divNode.style['margin-bottom'] = '20px';
                divNode.style.color = grey;
                document.querySelector('.wac-text-container').append(divNode);
            } else if (bookworkAnswer.every(hasSource)) {
                const divNode = document.createElement('div');
                const imageNode = document.createElement('img');
                imageNode.src = bookworkAnswer.toString();
                imageNode.setAttribute('id', 'custom-answer');
                imageNode.style['width'] = "50%";

                divNode.appendChild(imageNode);
                divNode.style['margin-bottom'] = '20px';
                divNode.style.color = grey;
                document.querySelector('.wac-text-container').append(divNode);
            } else {
                let answers = bookworkAnswer.join(', ');
                const textNode = document.createElement('span');
                textNode.textContent = `Answer: ${answers}`;
                textNode.style['margin'] = '0.3rem 1rem 0 0';
                textNode.style['color'] = 'white';

                const divNode = document.createElement('div');
                divNode.style['display'] = 'flex';
                divNode.style['align-items'] = 'center';
                divNode.style['justify-content'] = 'center';
                textNode.setAttribute('id', 'custom-answer');
                textNode.style['height'] = '3rem';

                divNode.appendChild(textNode);
                divNode.style['margin-bottom'] = '20px';
                document.querySelector('.wac-text-container').append(divNode);

                katex.render(answers, document.getElementById('custom-answer'), {
                    throwOnError: false
                });
            }

            // Get choices
            answerOptions = document.querySelectorAll('.choice-wac-options .item');
            if (answerOptions !== null) {
                for (let i = 0; i < answerOptions.length; i++) {
                    answerOption = answerOptions[i].textContent;
                    let similarityCount = 0
                    for (let a = 0; a < bookworkAnswer.length; a++) {
                        if (answerOption.includes(bookworkAnswer[a])) {similarityCount++;}
                    }
                    if (similarityCount == bookworkAnswer.length){
                        answerOptions[i].style.border = '5px solid var(--light)';
                    }
                    let answerArray = bookworkAnswer.join('');
                    answerArray = answerArray.split('');
                    uniques = answerArray.unique();
                    answerOptions[i].querySelector('.answer-markup.choice-wac-option').style.border = '5px solid var(--orange)';
                    for (let u = 0; u < uniques.length; u++) {
                        if (!(answerOption.includes(uniques[u]))) {
                            answerOptions[i].querySelector('.answer-markup.choice-wac-option').style.border = 'none';
                        }
                    }
                }
            }
        } catch (err) {console.log(err)}
    }

    let questionContainer = document.querySelector('.question');
    if (questionContainer !== null) {
        question = questionContainer.innerText;
        questionImages = document.querySelectorAll('[data-test-target="image-img"]');
        console.log(questionImages);

        if (questionImages.length == 0 | questionImages[0]) { return }
        else {
            questionImages.forEach(function(image) {
                question += `$$ ${image.currentSrc}`;
            })
        }
    }
}


// SUBMITS ANSWER
async function sendAnswerToDatabase() {
    await sleep(500);

    let correctStatusElement = document.querySelector('.page.result .result-inner .correct');
    console.log(correctStatusElement);
    if (correctStatusElement !== null) {
        if (question in userData) {
            console.log("Question already in database");
            return;
        }

        // Update MongoDB database if answer is correct
        try {
            console.log("POSTing answer data to database")
            userData[question] = answer;
            contactDatabase('updateOne', 'answers', 'user-data', [{"answers": userData}, userDocumentId]);
        } catch(err) {console.log(err)}
    }
}

document.addEventListener("click", function(e) {
    if(e.target) {
        try {
            if (
                (e.target.id == "skill-delivery-submit-button" && e.target.innerText == "Submit") ||
                (e.target.className == "button-text" && e.target.textContent == "Submit") ||
                (e.target.className == "button-icon button-icon-right" && e.target.parentElement.innerText == "Submit") ||
                (e.target.parentElement.className == "button-icon button-icon-right" && e.target.parentElement.parentElement.innerText == "Submit") ||
                (e.target.parentElement.parentElement.className == "button-icon button-icon-right" && e.target.parentElement.parentElement.parentElement.innerText == "Submit")) {

                const bookworkCodeElement = document.querySelector('.bookwork-code');
                let bookworkCode = bookworkCodeElement.textContent;
                bookworkCode = bookworkCode.replace("Bookwork code: ", '');

                answer = getInput(bookworkCode);
                updateDatabase(bookworkCode, answer);

                sendAnswerToDatabase();
            }
        } catch(err) {}
    }
});

document.addEventListener("keypress", function(event) {
    if (event.key === "Enter") {
        const submitButton = document.querySelector('#skill-delivery-submit-button');
        if (submitButton !== null) {
            const bookworkCodeElement = document.querySelector('.bookwork-code');
            let bookworkCode = bookworkCodeElement.textContent;
            bookworkCode = bookworkCode.replace("Bookwork code: ", '');

            answer = getInput(bookworkCode);
            updateDatabase(bookworkCode, answer);

            sendAnswerToDatabase();
        }
    }
});


// FUNCTIONS

Array.prototype.unique = function() {
    var arr = [];
    for (var i = 0; i < this.length; i++) {
      if (!arr.includes(this[i]) && isDigit(this[i])) {
        arr.push(this[i]);
      }
    }
    return arr;
}

function isDigit(c) {
    return c >= '0' && c <= '9';
}

function updateDatabase(bookworkCode, answer) {
    console.log("Updating database");

    if (localStorage.getItem('sparx-data') === null) {
        const defaultJSON = {"Placeholder": 0};
        localStorage.setItem('sparx-data', JSON.stringify(defaultJSON));
    }

    let sparxData = JSON.parse(localStorage.getItem('sparx-data'));
    sparxData[bookworkCode] = answer;

    console.log("New value: ", sparxData);

    localStorage.setItem('sparx-data', JSON.stringify(sparxData));

    console.log("Database updated");
}

function hasCurly(answer) {
    if (answer.toString().includes('{')  && answer.toString().includes('}')) {
        return true
    } else { return false}
}

function hasSource(answer) {
    if (answer.toString().includes('https') || answer.toString().includes('http')) {
        return true
    } else { return false}
}

function showThemes() {
    if (!(document.querySelector('.themes-container') === null)) { return }

    const container = document.querySelector('.package-container');

    let textNode = document.createElement('b');
    let themesContainer = document.createElement('section');
    let themesList = document.createElement('ul');
    textNode.innerText = 'Themes';
    themesContainer.setAttribute('class', 'themes-container');

    for (const [_, theme] of Object.entries(themes)) {
        let themeNode = document.createElement('li');
        for (const [_, colour] of Object.entries(theme)) {
            let colourNode = document.createElement('div');
            colourNode.style['background-color'] = colour;
            themeNode.appendChild(colourNode);
        };
        themeNode.addEventListener('click', function() {
            console.log("You clicked a theme");
            document.documentElement.style.setProperty('--darkest', theme['darkest']);
            document.documentElement.style.setProperty('--dark', theme['dark']);
            document.documentElement.style.setProperty('--light', theme['light']);
            document.documentElement.style.setProperty('--lightest', theme['lightest']);
        });
        themesList.appendChild(themeNode);
    };

    themesContainer.appendChild(textNode);
    themesContainer.appendChild(themesList);
    container.append(themesContainer);
}

function getInput() {
    let answerData = [];

    // Get input value
    const keypadInputs = document.querySelectorAll('.number-input');
    if (keypadInputs !== null) {
        for (let i = 0; i < keypadInputs.length; i++) {
            inputValue = keypadInputs[i].attributes[10].value;
            answerData.push(inputValue);
        }
    }

    // Get choice selected
    const chosen = document.querySelectorAll('.choice.selected, .gap-card.selected');
    if (chosen !== null) {
        for (let i = 0; i < chosen.length; i++) {
            choice = chosen[i].textContent;
            if (choice.includes('{') && choice.includes('}')) {
                let innerChoice = choice.substring(
                    choice.indexOf("{"),
                    choice.lastIndexOf("}") + 1
                );
                answerData.push(innerChoice);
            } else if (choice.includes('image')) {
                const imageElement = chosen[i].querySelector('[data-test-target="image-img"]');
                const source = imageElement.currentSrc;
                answerData.push(source.toString());
            } else {
                answerData.push(choice);
            }
        }
    }

    // Get cards selected
    const cards = document.querySelectorAll('.slots.horizontal .katex');
    if (cards !== null) {
        for (let i = 0; i < cards.length; i++) {
            card = cards[i].textContent;
            if (card.includes('{') && card.includes('}')) {
                let innerCard = card.substring(
                    card.indexOf("{"),
                    card.lastIndexOf("}") + 1
                );
                answerData.push(innerCard);
            } else {
                answerData.push(card);
            }
        }
    }

    // Get fraction cards selected
    const slotsFraction = document.querySelector('.slots.fraction');
    if (slotsFraction !== null) {
        const slotsFractions = document.querySelectorAll('.slots.fraction .slot');
        let innerFraction = [];
        for (let i = 0; i < slotsFractions.length; i++) {
            fractionText = slotsFractions[i].textContent;
            let fractionSubstring = fractionText.substring(
                fractionText.indexOf("{"),
                fractionText.lastIndexOf("}") + 1
            );
            console.log(fractionSubstring);
            innerFraction.push(fractionSubstring);
        }
        fraction = `\\frac${innerFraction[0]}${innerFraction[1]}`;
        answerData.push(fraction);
    }

    // Get cards selected
    const slotsElement = document.querySelectorAll('.slots .slot .katex');
    if (slotsElement !== null) {
        for (let i = 0; i < slotsElement.length; i++) {
            slotCard = slotsElement[i].textContent;
            if (slotCard.includes('{') && slotCard.includes('}')) {
                let innerCard = slotCard.substring(
                    slotCard.indexOf("{"),
                    slotCard.lastIndexOf("}") + 1
                );
                answerData.push(innerCard);
            } else {
                answerData.push(card);
            }
        }
    }

    return answerData
}

async function credits() {
    await sleep(200);
    console.clear();
    console.log.apply(console, ["%c Thanks for using ARX-M ","color: #fff; background: #2080ff; padding:5px 0;"])
    console.log.apply(console, ["%c Designed and Developed by ben and the arx group %c\ud83d\ude80 ","color: #fff; background: #2080ff; padding:5px 0;","color: #fff; background: #242424; padding:5px 0 5px 5px;"])
}

main();

checkUser();

credits();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址