Uses LLM to explain why an answer is wrong
// ==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";
}
})()