Greasy Fork is available in English.

Bunpro: Explain incorrect answer

Uses LLM to explain why an answer is wrong

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


})()