您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Add a reward system to jpdb that motivate you to study
// ==UserScript== // @name Jpdb stonks // @namespace http://tampermonkey.net/ // @version 0.0.4 // @description Add a reward system to jpdb that motivate you to study // @author Calonca // @match https://jpdb.io/* // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw== // @require http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @license GPLv2 // @namespace https://greasyfork.org/users/956173-calonca // ==/UserScript== const done_num_key = "revsDone"; const done_session_num_key = "revsDoneSession"; const done_correct_session_num_key = "revsDoneCorrect"; const can_get_reward_key = "hasGotReward"; const coins_tot_key = "coins_total"; const coins_session_key = "coins_session"; const last_daily_chest_key = "last_daily_chest_date"; const last_session_accuracy_key = "last_session_accuracy"; const reward_interval = 25; const bundle_review_cards = 25; const review_card_reward_likelihood = 0.05; //debug values // const reward_interval = 2; // const bundle_review_cards = 2; // const review_card_reward_likelihood = 1; const review_card_reward = 11; const review_card_cost = 10; let style = document.createElement("style"); style.type = "text/css"; const debug = false; (function() { 'use strict'; //Set values in first script usage if (GM_getValue(done_num_key)==null){ GM_setValue(done_num_key,0) } if (GM_getValue(can_get_reward_key)==null){ GM_setValue(can_get_reward_key,false) } if (GM_getValue(coins_tot_key)==null){ GM_setValue(coins_tot_key,0) } if (GM_getValue(coins_session_key)==null){ GM_setValue(coins_session_key,0) } if (GM_getValue(done_session_num_key)==null){ GM_setValue(done_session_num_key,0) } if (GM_getValue(last_session_accuracy_key)==null){ GM_setValue(last_session_accuracy_key,0) } if (GM_getValue(done_correct_session_num_key)==null){ GM_setValue(done_correct_session_num_key,0) } if (GM_getValue(last_daily_chest_key)==null){ let date = new Date(); //set last daily chest to yesterday date.setDate(date.getDate()-1); GM_setValue(last_daily_chest_key,date.toDateString()) } //if page url is review page if (window.location.href.includes("review")){ on_review_page(); } })(); function on_review_page(){ function getPercentage(done, remaining){ if (debug) return "50%"; return (100*((done % reward_interval)/reward_interval)).toFixed(2)+"%"+", earned coins: "+GM_getValue(coins_session_key); } function getDoneAndRemainingString(done, remaining){ return " done: "+done+" | "+"remaining: "+remaining; } //Progress bar let outerBar = document.createElement("div"); outerBar.className = "outerBar"; let innerBar = document.createElement("div"); innerBar.className = "innerBar"; outerBar.appendChild(innerBar); let learnNavItem = document.querySelectorAll(".nav-item")[0] let span = learnNavItem.childNodes[1]; let revNum = span.textContent; function onShowOptions(){ print("showing options"); } outerBar.onclick = onShowOptions; let showAnswerButton = document.getElementById("show-answer"); if (showAnswerButton){ waitForKeyElements ( "#grade-3" , afterShowingGradeButtons ); } //returns the number of coins given by the chest function random_chest_reward(){ const max_reward = 50; const min_reward = 20; return Math.floor(Math.random() * (max_reward - min_reward + 1) + min_reward); } //returns the number of coins given by the chest function daily_chest_reward(){ const max_reward = 200; const min_reward = 100; return Math.floor(Math.random() * (max_reward - min_reward + 1) + min_reward); } //Add elements to the document updateBar(); if (document.getElementsByClassName("main-row")[0]){//Doing review addBar(); }else {//Continue or finish screen let parent = document.getElementsByClassName("container bugfix")[0]; let importantText = document.getElementsByTagName('h5')[0] if (importantText.innerHTML == "Good job! You've finished all of your due cards!"){//Finish screen let resetText = document.createElement("h5"); resetText.innerHTML = "Progress bar has been reset"; parent.insertBefore(resetText,parent.childNodes[1]); } } if (GM_getValue(done_session_num_key) >= reward_interval){ let buy_screen = createBuyScreen(); let container = document.getElementsByClassName("container bugfix")[0]; //replace component of class container bugfix with buy screen container.parentElement.replaceChild(buy_screen,container); }else if (Math.random() < review_card_reward_likelihood && GM_getValue(can_get_reward_key)) {//add a reward if random is greater than 0.5 let daily_chest = false; //if the last daily chest was not today let date = new Date(); if (GM_getValue(last_daily_chest_key) != date.toDateString()){ if (Math.random() < 0.5) daily_chest = true; } //replace component of class container bugfix with reward text let container = document.getElementsByClassName("container bugfix")[0]; let rewardText = document.createElement("h5"); //Add an large emoticon of a reward chest let emoticon = document.createElement("p"); if (daily_chest){ rewardText.innerHTML = "Congratulations! You've earned a daily reward!"; emoticon.innerHTML = "🎁 🎁 🎁"; }else{ rewardText.innerHTML = "Congratulations! You've earned a reward!"; emoticon.innerHTML = "🎁"; } emoticon.style.fontSize = "70px"; rewardText.appendChild(emoticon); rewardText.id = "rewardText"; let prev_element = container.childNodes[0]; container.replaceChild(rewardText,prev_element); //write a text to open the chest let action_text = document.createElement("h5"); action_text.innerHTML = "Click the chest to open it"; container.appendChild(action_text); //get component after container bugfix let element2 = document.getElementsByClassName("with-bottom-padding-1")[0]; element2.remove(); //restore state before reward emoticon.onclick = function(){ //update coins with randon reward let coins = GM_getValue(coins_tot_key); let coins_session = GM_getValue(coins_session_key); let reward = 0 if (daily_chest){ reward = daily_chest_reward(); }else{ reward = random_chest_reward(); } GM_setValue(coins_tot_key,coins+reward); GM_setValue(coins_session_key,coins_session+reward); //if daily chest if (daily_chest){ GM_setValue(last_daily_chest_key,date.toDateString()); } //print how many coins you got let coinsText = document.createElement("h5"); coinsText.innerHTML = "You got "+reward+" coins!"; container.replaceChild(coinsText,rewardText); //add coins emoji let coinsEmoji = document.createElement("p"); coinsEmoji.innerHTML = "💰"; coinsEmoji.style.fontSize = "70px"; coinsText.appendChild(coinsEmoji); action_text.innerHTML = "Click the coins to continue"; coinsEmoji.onclick = function(){ console.log("restoring previous state"); let element = document.getElementsByClassName("container bugfix")[0]; element.replaceChild(prev_element,coinsText); //instert element2 after container bugfix element.parentElement.insertBefore(element2,element.nextSibling); action_text.remove(); GM_setValue(can_get_reward_key,false); updateBar(); }; } } function updateBar(){ let remaining = reward_interval - GM_getValue(done_session_num_key); let percentage = getPercentage(GM_getValue(done_session_num_key),remaining); innerBar.style.width = percentage; innerBar.innerHTML = getDoneAndRemainingString(GM_getValue(done_session_num_key),remaining)+" | "+percentage; } //Reset reviews count function reset(){ //GM_setValue(done_num_key,0); GM_setValue(done_session_num_key,0); GM_setValue(coins_session_key,0); updateBar(); } /** * @param {String} HTML representing a single element * @return {Element} */ function htmlToElement(html) { var template = document.createElement('template'); html = html.trim(); // Never return a text node of whitespace as the result template.innerHTML = html; return template.content.firstChild; } //Increase done review count on button click function increase_done_and_coins() { increase_done(); GM_setValue(coins_tot_key,GM_getValue(coins_tot_key)+review_card_reward); GM_setValue(coins_session_key,GM_getValue(coins_session_key)+review_card_reward); GM_setValue(can_get_reward_key,true); GM_setValue(done_correct_session_num_key,GM_getValue(done_correct_session_num_key)+1); } function increase_done(){ console.log("increasing done"); GM_setValue(done_num_key,GM_getValue(done_num_key)+1); GM_setValue(done_session_num_key,GM_getValue(done_session_num_key)+1); } //Add behaviour to grading buttons function afterShowingGradeButtons(){ GM_setValue(can_get_reward_key,false); outerBar.onclick = null; document.getElementById("grade-1").addEventListener ("click", increase_done, false); document.getElementById("grade-2").addEventListener ("click", increase_done, false); document.getElementById("grade-3").addEventListener ("click", increase_done_and_coins, false); document.getElementById("grade-4").addEventListener ("click", increase_done_and_coins, false); document.getElementById("grade-5").addEventListener ("click", increase_done_and_coins, false); } function addBar(){ let elementAfter = document.getElementsByClassName("main-row")[0]; elementAfter.parentElement.insertBefore(outerBar,elementAfter); } } function reset_session(){ GM_setValue(done_session_num_key,0); GM_setValue(coins_session_key,0); GM_setValue(done_correct_session_num_key,0); GM_setValue(can_get_reward_key,false); } function getAccuracy(){ let done = GM_getValue(done_session_num_key); let correct = GM_getValue(done_correct_session_num_key); if (done == 0){ return 0; } return (correct/done)*100; } function createBuyScreen() { const container = document.createElement('div'); container.classList.add('container', 'bugfix'); const heading = document.createElement('h5'); heading.textContent = "You've finished the reviews card bundle!"; container.appendChild(heading); //Add an large emoticon of a coin box with the number of coins you got let coins_you_got = document.createElement("h5"); let num_coins = Number(GM_getValue(coins_tot_key)); coins_you_got.innerHTML = "You have "+num_coins+" coins!"; container.appendChild(coins_you_got); let coinsEmoji = document.createElement("p"); coinsEmoji.innerHTML = "💰"; coinsEmoji.style.fontSize = "70px"; coins_you_got.appendChild(coinsEmoji); container.appendChild(coins_you_got); let accuracy_el = document.createElement("p"); let accuracy_percentage = getAccuracy(); accuracy_el.innerHTML = "Your accuracy is "+accuracy_percentage+"%, previously was "+GM_getValue(last_session_accuracy_key)+"%"; accuracy_el.style.marginBottom = '0.5rem'; accuracy_el.style.marginLeft = '0.5rem'; if (accuracy_percentage < GM_getValue(last_session_accuracy_key)){ accuracy_el.style.borderLeft = '1rem solid red'; } else { accuracy_el.style.borderLeft = '1rem solid green'; } accuracy_el.style.padding = '0.5rem'; accuracy_el.style.paddingLeft = '0.7rem'; container.appendChild(accuracy_el); const p1 = document.createElement('p'); p1.style.marginBottom = '0.5rem'; p1.style.marginLeft = '0.5rem'; p1.style.borderLeft = '1rem solid green'; p1.style.padding = '0.5rem'; p1.style.paddingLeft = '0.7rem'; p1.innerHTML = `You went through <span class="strong">` + GM_getValue(done_session_num_key) + `</span> reviews cards and earned <span class="strong">` + GM_getValue(coins_session_key) + `</span> coins.`; container.appendChild(p1); const p2 = document.createElement('p'); p2.style.marginBottom = '0.5rem'; p2.style.marginLeft = '0.5rem'; p2.style.borderLeft = '1rem solid gray'; p2.style.padding = '0.5rem'; p2.style.paddingLeft = '0.7rem'; p2.innerHTML = `You still have due items, buy another bundle of <span class="strong">` + bundle_review_cards + `</span> cards for <span class="strong">` + bundle_review_cards*review_card_cost + `</span> coins.`; container.appendChild(p2); const p4 = document.createElement('p'); p4.style.paddingTop = '0.5rem'; p4.textContent = "Do you want to continue?"; container.appendChild(p4); const form1 = document.createElement('form'); form1.method = 'post'; form1.action = '/review'; form1.style.display = 'inline'; const input1 = document.createElement('input'); input1.type = 'hidden'; input1.name = 'continue'; input1.value = '1'; form1.appendChild(input1); const input2 = document.createElement('input'); input2.type input2.type = 'submit'; input2.value = "Buy review bundle and continue"; input2.onclick = function() { GM_setValue(coins_tot_key,Number(GM_getValue(coins_tot_key))-Number(bundle_review_cards*review_card_cost)); //set last session accuracy GM_setValue(last_session_accuracy_key,getAccuracy()); reset_session(); }; input2.classList.add('outline'); input2.style.marginRight = '1rem'; form1.appendChild(input2); container.appendChild(form1); const form2 = document.createElement('form'); form2.method = 'post'; form2.action = '/learn'; form2.style.display = 'inline'; const input3 = document.createElement('input'); input3.type = 'hidden'; input3.name = 'finalize_review_session'; input3.value = '1'; form2.appendChild(input3); const input4 = document.createElement('input'); input4.type = 'submit'; input4.value = "No, I'm done for now."; input4.classList.add('outline', 'v1'); input4.autofocus = true; form2.appendChild(input4); container.appendChild(form2); return container; } //The following function is used by the script. //I Put it here instead of requires due to GreasyFolk rules, the original can be found at https://gist.github.com/BrockA/2625891 /*--- waitForKeyElements(): A utility function, for Greasemonkey scripts, that detects and handles AJAXed content. Usage example: waitForKeyElements ( "div.comments" , commentCallbackFunction ); //--- Page-specific function to do what we want when the node is found. function commentCallbackFunction (jNode) { jNode.text ("This comment changed by waitForKeyElements()."); } IMPORTANT: This function requires your script to have loaded jQuery. */ function waitForKeyElements ( selectorTxt, /* Required: The jQuery selector string that specifies the desired element(s). */ actionFunction, /* Required: The code to run when elements are found. It is passed a jNode to the matched element. */ bWaitOnce, /* Optional: If false, will continue to scan for new elements even after the first match is found. */ iframeSelector /* Optional: If set, identifies the iframe to search. */ ) { var targetNodes, btargetsFound; if (typeof iframeSelector == "undefined") targetNodes = $(selectorTxt); else targetNodes = $(iframeSelector).contents () .find (selectorTxt); if (targetNodes && targetNodes.length > 0) { btargetsFound = true; /*--- Found target node(s). Go through each and act if they are new. */ targetNodes.each ( function () { var jThis = $(this); var alreadyFound = jThis.data ('alreadyFound') || false; if (!alreadyFound) { //--- Call the payload function. var cancelFound = actionFunction (jThis); if (cancelFound) btargetsFound = false; else jThis.data ('alreadyFound', true); } } ); } else { btargetsFound = false; } //--- Get the timer-control variable for this selector. var controlObj = waitForKeyElements.controlObj || {}; var controlKey = selectorTxt.replace (/[^\w]/g, "_"); var timeControl = controlObj [controlKey]; //--- Now set or clear the timer as appropriate. if (btargetsFound && bWaitOnce && timeControl) { //--- The only condition where we need to clear the timer. clearInterval (timeControl); delete controlObj [controlKey] } else { //--- Set a timer, if needed. if ( ! timeControl) { timeControl = setInterval ( function () { waitForKeyElements ( selectorTxt, actionFunction, bWaitOnce, iframeSelector ); }, 300 ); controlObj [controlKey] = timeControl; } } waitForKeyElements.controlObj = controlObj; }