Sorryops

Skip the half of the fun!

当前为 2024-04-22 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Sorryops
  3. // @namespace https://git.disroot.org/electromagneticcyclone/sorryops
  4. // @version 20240422.1
  5. // @description Skip the half of the fun!
  6. // @icon https://orioks.miet.ru/favicon.ico
  7. // @author electromagneticcyclone & angelbeautifull
  8. // @license Unlicense
  9. // @supportURL https://git.disroot.org/electromagneticcyclone/sorryops
  10. // @match https://orioks.miet.ru/student/student/test*
  11. // @grant GM_getValue
  12. // @grant GM_setValue
  13. // @grant GM_addStyle
  14. // @grant GM_registerMenuCommand
  15. // @grant GM_setClipboard
  16. // @require https://openuserjs.org/src/libs/sizzle/GM_config.js
  17. // @run-at document-start
  18. // ==/UserScript==
  19.  
  20. var config = new GM_config({
  21. id: 'config',
  22. title: 'Script Settings',
  23. fields: {
  24. auto_answer: {
  25. label: "Auto answer",
  26. type: 'select',
  27. options: [ 'No', 'First', 'Random' ],
  28. default: 'No',
  29. },
  30. display_answer: {
  31. label: "Display answer near variant",
  32. type: 'checkbox',
  33. default: true,
  34. },
  35. register_keyboard_keys: {
  36. label: "Continue by pressing Enter",
  37. type: 'checkbox',
  38. default: true,
  39. },
  40. copy_answers: {
  41. label: "Copy answers to the clipboard",
  42. type: 'checkbox',
  43. default: false,
  44. },
  45. append_question_number: {
  46. label: "Show question numbers in the final report",
  47. type: 'checkbox',
  48. default: true,
  49. },
  50. accumulator_enable: {
  51. label: "Accumulate test results in one field",
  52. type: 'checkbox',
  53. default: false,
  54. },
  55. accumulator_prefix: {
  56. label: "Accumulated results prefix (test number)",
  57. type: 'text',
  58. default: "",
  59. },
  60. auto_continue: {
  61. label: "Auto continue (DANGEROUS!!! Will be disabled after an hour. Press `d` to disable)",
  62. type: 'checkbox',
  63. default: false,
  64. },
  65. auto_continue_time: {
  66. type: 'hidden',
  67. default: 0,
  68. },
  69. auto_restart: {
  70. label: "Auto restart (DANGEROUS!!! Will be disabled after an hour. Press `d` to disable. Make sure you have infinite attempts)",
  71. type: 'checkbox',
  72. default: false,
  73. },
  74. auto_restart_time: {
  75. type: 'hidden',
  76. default: 0,
  77. },
  78. },
  79. events: {
  80. init: function() {
  81. if (this.get('auto_continue') && (this.get('auto_answer') == "No")) {
  82. this.set('auto_continue', false);
  83. }
  84. if (this.get('accumulator_enable') == false) {
  85. GM_setValue('accumulated_answers', "");
  86. }
  87. },
  88. save: function(forgotten) {
  89. this.set('auto_continue_time', Date.now());
  90. this.set('auto_restart_time', Date.now());
  91. if (this.isOpen && this.get('auto_continue') && (this.get('auto_answer') == "No")) {
  92. this.set('auto_continue', false);
  93. alert("Can't automatically continue without answer.");
  94. }
  95. },
  96. },
  97. });
  98.  
  99. var answers = [];
  100. var variant, hash;
  101. var testID = (() => {
  102. var url = document.URL;
  103. url = url.slice(url.indexOf("idKM=") + 5);
  104. url = url.slice(0, url.indexOf("&"));
  105. return url;
  106. })();
  107.  
  108. GM_registerMenuCommand('Script Settings', () => {
  109. config.open();
  110. });
  111.  
  112. window.addEventListener('load', actionFunction);
  113. window.onkeydown = (e) => {
  114. if ((e.key == "Enter") && config.get('register_keyboard_keys')) {
  115. press_continue_btn();
  116. }
  117. if (e.key == "d") {
  118. config.set('auto_continue', false);
  119. config.set('auto_restart', false);
  120. config.save();
  121. }
  122. };
  123.  
  124. // https://stackoverflow.com/a/15710692
  125. function hashCode(s) {
  126. return s.split("").reduce(function(a, b) {
  127. a = ((a << 5) - a) + b.charCodeAt(0);
  128. return a & a;
  129. }, 0);
  130. }
  131.  
  132. function update_variant() {
  133. var i, pbox;
  134. var chosen_answer = "";
  135. for (i = 0; i < answers.length; i++) {
  136. chosen_answer += answers[i].checked ? answers[i].value : "";
  137. }
  138. chosen_answer = chosen_answer.split('').sort().join('');
  139. var pboxes = document.getElementsByTagName('p');
  140. const display_answer = config.get('display_answer');
  141. for (i = 0; i < pboxes.length; i++) {
  142. pbox = pboxes[i];
  143. if (pbox.textContent.includes("Вопрос:")) {
  144. pbox.innerHTML = "<i>(Вариант <input onfocus='this.select();' id='variant' value='" + hash + (display_answer == true ? (" " + chosen_answer) : "") + "' readonly>)</i><br>Вопрос:";
  145. break;
  146. }
  147. }
  148. var question_num = undefined;
  149. for (i = 0; i < pboxes.length; i++) {
  150. pbox = pboxes[i];
  151. if (pbox.textContent.includes("Текущий вопрос: ")) {
  152. question_num = pbox.textContent.slice(variant.indexOf("Текущий вопрос: ") + 16);
  153. break;
  154. }
  155. }
  156. var tests = GM_getValue('tests', new Object());
  157. if (tests[testID] === undefined) {
  158. tests[testID] = new Object();
  159. }
  160. tests[testID][hash] = [question_num, chosen_answer];
  161. GM_setValue('tests', tests);
  162. }
  163.  
  164. function test_form_handler() {
  165. var i;
  166. var objects = new Object();
  167. var boxes = document.getElementsByTagName('input');
  168. var form = document.getElementById('testform-answer');
  169. for (i = 0; i < boxes.length; i++) {
  170. if (boxes[i].type === 'checkbox' | boxes[i].type === 'radio') {
  171. var span = document.createElement('span');
  172. span.innerHTML =
  173. boxes[i].type === 'radio' && boxes[i].value == "1"
  174. ? "<b>" + boxes[i].value + ")</b> "
  175. : boxes[i].value + ") ";
  176. boxes[i].parentNode.insertBefore(span, boxes[i]);
  177. objects[boxes[i].value] = boxes[i];
  178. }
  179. }
  180. const sorted_objects = Object.keys(objects).sort().reduce(
  181. (obj, key) => {
  182. obj[key] = objects[key];
  183. return obj;
  184. }, {}
  185. );
  186. for (var key in sorted_objects) {
  187. sorted_objects[key].parentNode.remove();
  188. form.appendChild(sorted_objects[key].parentNode);
  189. answers.push(sorted_objects[key]);
  190. }
  191. const auto_answer = config.get('auto_answer');
  192. var answer;
  193. if (auto_answer == 'Random') {
  194. if (answers[0].type === 'radio') {
  195. var chosen_answer = Math.floor(Math.random() * answers.length);
  196. answers[chosen_answer].click();
  197. } else {
  198. var pick = Math.floor(Math.random() * (Math.pow(2, answers.length) - 1)) + 1;
  199. for (i = 0; i < answers.length; i++) {
  200. if(pick & Math.pow(2, i)) {
  201. answers[i].click();
  202. }
  203. }
  204. }
  205. } else if (auto_answer == 'First') {
  206. answers[0].click();
  207. }
  208. variant = document.getElementById('w0').parentNode.textContent;
  209. variant = variant.slice(variant.indexOf("Вопрос:"));
  210. hash = hashCode(variant);
  211. update_variant();
  212. form.addEventListener('click', update_variant);
  213. }
  214.  
  215. function result_page_handler() {
  216. var i;
  217. var correct = variant.slice(variant.indexOf("Число верных ответов: ") + 22);
  218. correct = correct.slice(0, correct.indexOf("\n"));
  219. var test = GM_getValue('tests', new Object())[testID];
  220. if (test === undefined) {
  221. return;
  222. }
  223. var printer = "";
  224. var sorted_test = [];
  225. for (var hash in test) {
  226. sorted_test.push([hash].concat(test[hash]));
  227. }
  228. sorted_test.sort((a, b) => {return a[1] - b[1]});
  229. console.log(sorted_test);
  230. for (i = 0; i < sorted_test.length; i++) {
  231. printer += (config.get('append_question_number') ? (sorted_test[i][1] + ") ") : "") + sorted_test[i][0] + " " + sorted_test[i][2] + "\n";
  232. }
  233. printer += correct;
  234. if (config.get('copy_answers')) {
  235. GM_setClipboard(printer);
  236. }
  237. if (config.get('accumulator_enable')) {
  238. var acc = GM_getValue('accumulated_answers', "");
  239. if (acc != "") {
  240. acc += "\n\n";
  241. }
  242. var prefix = config.get('accumulator_prefix');
  243. if (prefix != "") {
  244. acc += prefix + "\n";
  245. }
  246. acc += printer;
  247. GM_setValue('accumulated_answers', acc);
  248. printer = acc;
  249. }
  250. printer = "<textarea readonly style='resize:none; width:fit-content; height:fit-content' rows='" + String(Object.keys(test).length + 1) + "' cols='50' onfocus='this.select();' id='answers'>" + printer + "</textarea>";
  251. var pboxes = document.getElementsByTagName('p');
  252. for (i = 0; i < pboxes.length; i++) {
  253. var pbox = pboxes[i];
  254. if (pbox.textContent.includes("Попытка ")) {
  255. pbox.outerHTML += printer;
  256. break;
  257. }
  258. }
  259. var clear = GM_getValue('clear_tests', new Object());
  260. clear[testID] = true;
  261. GM_setValue('clear_tests', clear);
  262. }
  263.  
  264. function DB_cleaner() {
  265. var clear = GM_getValue('clear_tests', new Object());
  266. var tests = GM_getValue('tests', new Object());
  267. for (var test in clear) {
  268. delete tests[test];
  269. }
  270. GM_setValue('tests', tests);
  271. GM_setValue('clear_tests', new Object());
  272. }
  273.  
  274. function press_continue_btn() {
  275. var i;
  276. var buttons = document.getElementsByTagName('button');
  277. var button = undefined;
  278. for (i = 0; i < buttons.length; i++) {
  279. var btn = buttons[i];
  280. if (btn.textContent.includes("Пройти") || btn.textContent.includes("Продолжить")) {
  281. button = btn;
  282. break;
  283. }
  284. }
  285. if (button === undefined) {
  286. return;
  287. }
  288. if (button.textContent.includes("Пройти")) {
  289. window.location.replace(button.parentNode.href);
  290. } else if (button.textContent.includes("Продолжить")) {
  291. button.click();
  292. }
  293. }
  294.  
  295. function actionFunction() {
  296. var old_time, cur_time;
  297. variant = document.getElementById('w0').parentNode.textContent;
  298. if (variant.includes("Вопрос:")) {
  299. DB_cleaner();
  300. test_form_handler();
  301. if (config.get('auto_continue')) {
  302. old_time = config.get('auto_continue_time');
  303. cur_time = Date.now();
  304. if (cur_time - old_time > 60 * 60 * 1000) {
  305. config.set('auto_continue', false);
  306. } else {
  307. press_continue_btn();
  308. }
  309. }
  310. } else if (variant.includes("Результат прохождения теста:")) {
  311. result_page_handler();
  312. if (config.get('auto_restart')) {
  313. old_time = config.get('auto_restart_time');
  314. cur_time = Date.now();
  315. if (cur_time - old_time > 60 * 60 * 1000) {
  316. config.set('auto_restart', false);
  317. } else {
  318. press_continue_btn();
  319. }
  320. }
  321. }
  322. }