Bunpro: Explain incorrect answer

Uses LLM to explain why an answer is wrong

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

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


})()