Bunpro: Auto Commit 2.0

Automatically submits your answer once it's correct.

  1. // ==UserScript==
  2. // @name Bunpro: Auto Commit 2.0
  3. // @namespace http://tampermonkey.net/
  4. // @version 2.1.3
  5. // @description Automatically submits your answer once it's correct.
  6. // @author Kumirei
  7. // @include *bunpro.jp/*
  8. // @exclude *community.bunpro.jp*
  9. // @require https://greasyfork.org/scripts/370623-bunpro-helpful-events/code/Bunpro:%20Helpful%20Events.js?version=974369
  10. // @require https://greasyfork.org/scripts/370219-bunpro-buttons-bar/code/Bunpro:%20Buttons%20Bar.js?version=768992
  11. // @grant none
  12. // ==/UserScript==
  13.  
  14. (function() {
  15. var $ = window.$;
  16.  
  17. // ---------------------------------------------------------------SETTINGS--------------------------------------------------------------- //
  18.  
  19. var settings = {
  20. bugReporting: false, // Get alerts when the script can't find the answer
  21. indicator: true, // Add an indicator for whether an answer was found to the stats bar
  22. prints: false // Print helpful debug information in the console
  23. };
  24.  
  25. // -----------------------------------------------------------------MAIN----------------------------------------------------------------- //
  26.  
  27. // Global variable
  28. var autocommit = {
  29. answer: "",
  30. on: true,
  31. id: 0,
  32. sentence: ""
  33. };
  34.  
  35. // Update when a new item is loaded
  36. $('html')[0].addEventListener('new-review-item', function() {
  37. // Initialize if first item
  38. if (!$('#AutoCommit').length) initialise();
  39.  
  40. // Reset
  41. autocommit.answer = "";
  42. updateIndicator();
  43. autocommit.id = findID();
  44.  
  45. // Find the answer by matching the question to the example sentences
  46. matchExamples(parse);
  47. if (autocommit.answer == "") matchExamples((e)=>{return parse(e).replace(/です/g, "");}); // Try removing formalities if no answer was found
  48. if (autocommit.answer == "" && settings.bugReporting) alertNoAnswer();
  49. else updateIndicator();
  50. if (settings.prints) console.log(autocommit.id, 'Answer:', autocommit.answer);
  51. });
  52.  
  53. // Set up the page for continued running
  54. function initialise() {
  55. addButton();
  56. addEventlistener();
  57. if (settings.indicator) addIndicator();
  58. }
  59.  
  60. // Adds the on/off button to the buttons bar
  61. function addButton() {
  62. autocommit.on = (localStorage.getItem('BPautoCommit') == "false" ? false : true);
  63. buttonsBar.addButton('AutoCommit', 'Auto Commit ' + (autocommit.on == true ? "ON" : "OFF"), toggleAutoCommit);
  64. }
  65.  
  66. // Add event listener that matches the answer to the input when the input changes
  67. function addEventlistener() {
  68. $('#study-answer-input').on('keyup', function(e) {
  69. if (autocommit.on) {
  70. var currentInput = $('#study-answer-input')[0].value;
  71. if (currentInput == autocommit.answer) {
  72. $('#submit-study-answer').click();
  73. }
  74. }
  75. });
  76. }
  77.  
  78. // Adds a red/green cirle which indicates whether the answer has been found
  79. function addIndicator() {
  80. $('.in-review-stats').append('<div id="autocommit-indicator" class="review__stats" title="Indicator for whether Autocommit has found an answer for this question"></div>');
  81. $('head').append('<style id="AutocommitCSS">'+
  82. ' :root {--autocommit-indicator: red;}'+
  83. ' #autocommit-indicator {'+
  84. ' height: 15px;'+
  85. ' width: 15px;'+
  86. ' background-color: var(--autocommit-indicator);'+
  87. ' border-radius: 50%;'+
  88. ' margin: 13px 5px;'+
  89. ' }'+
  90. '</style>');
  91. }
  92.  
  93. // Updates the indicator color depending on whether there is an answer
  94. function updateIndicator() {
  95. if (settings.indicator) {
  96. var newColor = (autocommit.answer == "" ? "red" : "green");
  97. if (!autocommit.on) newColor = "red";
  98. $('html')[0].style.setProperty('--autocommit-indicator', newColor);
  99. }
  100. }
  101.  
  102. // Finds and returns the ID for the grammar point
  103. function findID() {
  104. return $('.level_lesson_info > a')[0].href.split("grammar_points/")[1];
  105. }
  106.  
  107. // Matches the study question with the example sentences to find the answer
  108. function matchExamples(parser) {
  109. var questionElem = $('.study-question-japanese > div')[0];
  110. var sentence = parser(questionElem); // Study question with .* where the input should be
  111. autocommit.sentence = sentence;
  112. var examples = $('.examples .japanese-example-sentence');
  113. autocommit.answer = "";
  114. examples.each(function(i, e) {
  115. var example = parser(e);
  116. if (example.match(sentence) != null) {
  117. var startIndex = sentence.match(/\./).index;
  118. var sentenceEnd = sentence.slice(startIndex+2)+'$';
  119. var endIndex = example.match(sentenceEnd).index;
  120. autocommit.answer = example.slice(startIndex, endIndex); // Crop out the answer
  121. if (settings.prints) console.log(i, example);
  122. }
  123. });
  124. }
  125.  
  126. // Parses the sentence then cleans it by removing punctuation and stuff. Add .* for study question input
  127. function parse(elem) {
  128. var text = parseSentence(elem);
  129. text = text.replace(/___/g, '.*').replace(/\[.*\]$/g, '').replace(/[、。\(\)\s\[\]]/g, '');
  130. return text;
  131. }
  132.  
  133. // Extracts the sentence in kana from the sentence elements
  134. function parseSentence(sentenceElem) {
  135. var text = "";
  136. sentenceElem.childNodes.forEach((elem)=>{
  137. //console.log(elem.className==""?undefined:elem.className, elem.nodeName, elem.textContent);
  138. if (elem.className == "study-area-input") text += "___"; // Underscores to indicate input
  139. else if (elem.nodeName == "STRONG" || elem.nodeName == "SPAN"|| elem.nodeName == "chui") text += parseSentence(elem); // Need to go deeper for these
  140. else if (!elem.textContent.includes('(')) text += elem.textContent; // If no furigana is included, just grab the text
  141. else text += elem.textContent.split('(')[1].replace(')', ''); // Else grab the furigana?
  142. });
  143. return text;
  144. }
  145.  
  146. // Alerts the user with info regarding why no answer was found
  147. function alertNoAnswer(id) {
  148. var examples = '';
  149. $('.examples .japanese-example-sentence').each(function(i, e) {examples += 'Example: ' + parse(e) +'\n';});
  150. var text = 'Bunpro: Autocommit could not find the answer to this question.'+
  151. '\nCopy the following and sent it to Kumi on the Bunpro forums.'+
  152. '\n\nIf you don\'t want to receive these bug alerts anymore, disable them by setting bugReporting = false at the top of the script.\n'+
  153. '\nGrammar ID: ' + autocommit.id +
  154. '\nSentence: ' + autocommit.sentence +
  155. '\n' + examples;
  156. alert(text);
  157. if (settings.prints) console.log(text);
  158. }
  159.  
  160. // Toggles the running state of the script
  161. var toggleAutoCommit = function() {
  162. autocommit.on = !autocommit.on;
  163. var name = "Auto Commit " + (autocommit.on == true ? "ON" : "OFF");
  164. $('#AutoCommit')[0].value = name;
  165. updateIndicator();
  166. };
  167. })();