Bunpro: Explain incorrect answer

Uses LLM to explain why an answer is wrong

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         Bunpro: Explain incorrect answer
// @namespace    http://tampermonkey.net/
// @version      0.0.1
// @description  Uses LLM to explain why an answer is wrong
// @author       Jacob
// @include      *bunpro.jp/reviews*
// @include      *bunpro.jp/cram*
// @exclude      *bunpro.jp/dashboard*
// @exclude      *community.bunpro.jp*
// @grant        none
// @license MIT
// ==/UserScript==

;(function () {
    const API_URI = "https://api.openai.com/v1/responses";
    const MODEL = "gpt-5.2";

    let apiKey = localStorage.getItem('OPENAI_API_KEY');

    if (!apiKey || apiKey.length < 10 ) {
        apiKey = prompt("Please input Secret API key (will store in local.storage)", "sk-");
        localStorage.setItem('OPENAI_API_KEY', apiKey)
    }

    async function callChatGPT(prompt) {
        const response = await fetch(API_URI, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
                "Authorization": "Bearer " + apiKey
            },
            body: JSON.stringify({
                model: MODEL,
                input: prompt
            })
        });

        if (!response.ok) {
            throw new Error(`HTTP error! status: ${response.status}`);
        }

        const data = await response.json();
        return data.output?.[0]?.content?.[0]?.text ?? "";
    }

    function waitForElm(selector) {
        return new Promise(resolve => {
            if (document.querySelector(selector)) {
                return resolve(document.querySelector(selector));
            }

            const observer = new MutationObserver(mutations => {
                if (document.querySelector(selector)) {
                    observer.disconnect();
                    resolve(document.querySelector(selector));
                }
            });

            observer.observe(document.body, {
                childList: true,
                subtree: true
            });
        });
    }


    waitForElm('#js-tour-quiz-question').then(function () {
        console.log("Adding Event Listeners")

        const answer_input_el = document.getElementById('js-manual-input');
        const submit_answer_button_el = document.querySelector(".InputManual__button");

        // Listen for submission, either by pressing return or clicking the submit answer button
        answer_input_el.addEventListener('keydown', function (event) {
            if (event.which == 13) {
                showExplanationIfWrong()
            }
        })

        submit_answer_button_el.addEventListener('click', function () {
            showExplanationIfWrong()
        })
    });



    //opens the info if you get the item wrong
    function showExplanationIfWrong() {
        resetExplanation();
       
        setTimeout(function() {
            if(document.querySelector('.bp-quiz-console--incorrect'))
            {
                document.getElementById("ai_explanation").innerText = "Loading explanation...";
                const full_response = document.querySelector('.bp-quiz-question.relative > div > button').parentNode.innerText;
                const user_fragment = document.querySelector('.bp-quiz-question.relative > div > button').innerText;
                const target_sentence = document.querySelector('.QuestionSentenceTransNuance').innerText;

                console.log(user_fragment);

                let prompt = `Explain why the "${user_fragment}" in "${full_response}" is incorrect when translating ${target_sentence}. Ignore the fact that the incorrect translation may use hiragana/katakana instead of kanji. Just explain, do not be sycophantic. Be concise, use two sentences maximum. Explain in English. Answer using English language. Do answer in Japanese. The user does not understand Japanese fluently, so answer in English.`;

                callChatGPT(prompt)
                    .then(result => {
                    document.getElementById("ai_explanation").style = "";
                    document.getElementById("ai_explanation").innerText = result.replaceAll('*', '');
                })
                    .catch(error => {
                    console.error("Error:", error);
                });
            }
        }, 500);
    }

    function resetExplanation() {
        let explanationElement = document.getElementById("ai_explanation");

        if (!explanationElement) {
            explanationElement = document.createElement('div')
            explanationElement.id = "ai_explanation";
            document.getElementById('js-tour-quiz-question').appendChild(explanationElement)
        }

        explanationElement.innerText = "";
        explanationElement.style = "font-style: italic";
    }


})()