Sorryops

Skip the half of the fun!

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

  1. // ==UserScript==
  2. // @name Sorryops
  3. // @namespace https://git.disroot.org/electromagneticcyclone/sorryops
  4. // @version 20240424.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 all_labels = {
  21. en: {
  22. l: "English",
  23. settings_title: "Settings",
  24. script_language: "Language",
  25. auto_answer: "Auto answer",
  26. display_answer: "Display answer near variant",
  27. register_keyboard_keys: "Register hotkeys",
  28. copy_answers: "Copy results to the clipboard",
  29. append_question_number: "Show question numbers in the final report",
  30. accumulator_enable: "Accumulate test results in one field",
  31. accumulator_prefix: "Accumulated results prefix (test number)",
  32. auto_continue: "Auto continue (DANGEROUS!!! Will be disabled after an hour. Press `d` to disable)",
  33. auto_restart: "Auto restart (DANGEROUS!!! Will be disabled after an hour. Press `d` to disable. Make sure you have infinite attempts)",
  34. },
  35. ru: {
  36. l: "Русский",
  37. settings_title: "Настройки",
  38. script_language: "Язык",
  39. auto_answer: "Автоответчик",
  40. display_answer: "Отображать ответ рядом с вариантом",
  41. register_keyboard_keys: "Горячие клавиши",
  42. copy_answers: "Копировать результаты в буфер обмена",
  43. append_question_number: "Отображать номер вопроса в финальном отчёте",
  44. accumulator_enable: "Собирать отчёты в одно поле",
  45. accumulator_prefix: "Префикс перенесённого отчёта (номер теста)",
  46. auto_continue: "Автопродолжение (ОПАСНО!!! Отключается через час. Нажмите `d`, чтобы остановить)",
  47. auto_restart: "Автоперезапуск (ОПАСНО!!! Отключается через час. Нажмите `d`, чтобы остановить. Убедитесь, что количество попыток неограничено)",
  48. },
  49. };
  50.  
  51. var labels = all_labels[(() => {
  52. var lang = GM_getValue('language', "-");
  53. if (!lang || (lang == "-")) {
  54. lang = navigator.language || navigator.userLanguage;
  55. }
  56. for (var l in all_labels) {
  57. if (lang.includes(l)) {
  58. return l;
  59. }
  60. }
  61. })()];
  62. if (labels == undefined) {
  63. labels = all_labels.ru;
  64. }
  65.  
  66. var config = new GM_config({
  67. id: 'config',
  68. title: labels.settings_title,
  69. fields: {
  70. script_language: {
  71. label: labels.script_language,
  72. type: 'select',
  73. options: [ '-', all_labels.en.l, all_labels.ru.l ],
  74. default: '-',
  75. },
  76. auto_answer: {
  77. label: labels.auto_answer,
  78. type: 'select',
  79. options: [ 'No', 'First', 'Random' ],
  80. default: 'No',
  81. },
  82. display_answer: {
  83. label: labels.display_answer,
  84. type: 'checkbox',
  85. default: true,
  86. },
  87. register_keyboard_keys: {
  88. label: labels.register_keyboard_keys,
  89. type: 'checkbox',
  90. default: true,
  91. },
  92. copy_answers: {
  93. label: labels.copy_answers,
  94. type: 'checkbox',
  95. default: false,
  96. },
  97. append_question_number: {
  98. label: labels.append_question_number,
  99. type: 'checkbox',
  100. default: true,
  101. },
  102. accumulator_enable: {
  103. label: labels.accumulator_enable,
  104. type: 'checkbox',
  105. default: false,
  106. },
  107. accumulator_prefix: {
  108. label: labels.accumulator_prefix,
  109. type: 'text',
  110. default: "",
  111. },
  112. auto_continue: {
  113. label: labels.auto_continue,
  114. type: 'checkbox',
  115. default: false,
  116. },
  117. auto_continue_time: {
  118. type: 'hidden',
  119. default: 0,
  120. },
  121. auto_restart: {
  122. label: labels.auto_restart,
  123. type: 'checkbox',
  124. default: false,
  125. },
  126. auto_restart_time: {
  127. type: 'hidden',
  128. default: 0,
  129. },
  130. },
  131. events: {
  132. init: function() {
  133. if (this.get('auto_continue') && (this.get('auto_answer') == "No")) {
  134. this.set('auto_continue', false);
  135. }
  136. if (this.get('accumulator_enable') == false) {
  137. GM_setValue('accumulated_answers', "");
  138. }
  139. switch (this.get('script_language')) {
  140. case all_labels.en.l:
  141. GM_setValue('language', "en");
  142. break;
  143. case all_labels.ru.l:
  144. GM_setValue('language', "ru");
  145. break;
  146. default:
  147. GM_setValue('language', "-");
  148. break;
  149. }
  150. },
  151. save: function(forgotten) {
  152. this.set('auto_continue_time', Date.now());
  153. this.set('auto_restart_time', Date.now());
  154. if (this.isOpen && this.get('auto_continue') && (this.get('auto_answer') == "No")) {
  155. this.set('auto_continue', false);
  156. alert("Can't automatically continue without answer.");
  157. }
  158. this.init();
  159. },
  160. },
  161. });
  162.  
  163. var answers = [];
  164. var variant, hash;
  165. var testID = (() => {
  166. var url = document.URL;
  167. url = url.slice(url.indexOf("idKM=") + 5);
  168. url = url.slice(0, url.indexOf("&"));
  169. return url;
  170. })();
  171.  
  172. GM_registerMenuCommand('Script Settings', () => {
  173. config.open();
  174. });
  175.  
  176. window.addEventListener('load', actionFunction);
  177. window.onkeydown = (e) => {
  178. if ((e.key == "Enter") && config.get('register_keyboard_keys')) {
  179. press_continue_btn();
  180. }
  181. if (e.key == "d") {
  182. config.set('auto_continue', false);
  183. config.set('auto_restart', false);
  184. config.save();
  185. }
  186. };
  187.  
  188. // https://stackoverflow.com/a/15710692
  189. function hashCode(s) {
  190. return s.split("").reduce(function(a, b) {
  191. a = ((a << 5) - a) + b.charCodeAt(0);
  192. return a & a;
  193. }, 0);
  194. }
  195.  
  196. function update_variant() {
  197. var i, pbox;
  198. var chosen_answer = "";
  199. for (i = 0; i < answers.length; i++) {
  200. chosen_answer += answers[i].checked ? answers[i].value : "";
  201. }
  202. chosen_answer = chosen_answer.split('').sort().join('');
  203. var pboxes = document.getElementsByTagName('p');
  204. const display_answer = config.get('display_answer');
  205. for (i = 0; i < pboxes.length; i++) {
  206. pbox = pboxes[i];
  207. if (pbox.textContent.includes("Вопрос:")) {
  208. pbox.innerHTML = "<i>(Вариант <input onfocus='this.select();' id='variant' value='" + hash + (display_answer == true ? (" " + chosen_answer) : "") + "' readonly>)</i><br>Вопрос:";
  209. break;
  210. }
  211. }
  212. var question_num = undefined;
  213. for (i = 0; i < pboxes.length; i++) {
  214. pbox = pboxes[i];
  215. if (pbox.textContent.includes("Текущий вопрос: ")) {
  216. question_num = pbox.textContent.slice(variant.indexOf("Текущий вопрос: ") + 16);
  217. break;
  218. }
  219. }
  220. var tests = GM_getValue('tests', new Object());
  221. if (tests[testID] === undefined) {
  222. tests[testID] = new Object();
  223. }
  224. tests[testID][hash] = [question_num, chosen_answer];
  225. GM_setValue('tests', tests);
  226. }
  227.  
  228. function test_form_handler() {
  229. var i;
  230. var objects = new Object();
  231. var boxes = document.getElementsByTagName('input');
  232. var form = document.getElementById('testform-answer');
  233. for (i = 0; i < boxes.length; i++) {
  234. if (boxes[i].type === 'checkbox' | boxes[i].type === 'radio') {
  235. var span = document.createElement('span');
  236. span.innerHTML =
  237. boxes[i].type === 'radio' && boxes[i].value == "1"
  238. ? "<b>" + boxes[i].value + ")</b> "
  239. : boxes[i].value + ") ";
  240. boxes[i].parentNode.insertBefore(span, boxes[i]);
  241. objects[boxes[i].value] = boxes[i];
  242. }
  243. }
  244. const sorted_objects = Object.keys(objects).sort().reduce(
  245. (obj, key) => {
  246. obj[key] = objects[key];
  247. return obj;
  248. }, {}
  249. );
  250. for (var key in sorted_objects) {
  251. sorted_objects[key].parentNode.remove();
  252. form.appendChild(sorted_objects[key].parentNode);
  253. answers.push(sorted_objects[key]);
  254. }
  255. const auto_answer = config.get('auto_answer');
  256. var answer;
  257. if (auto_answer == 'Random') {
  258. if (answers[0].type === 'radio') {
  259. var chosen_answer = Math.floor(Math.random() * answers.length);
  260. answers[chosen_answer].click();
  261. } else {
  262. var pick = Math.floor(Math.random() * (Math.pow(2, answers.length) - 1)) + 1;
  263. for (i = 0; i < answers.length; i++) {
  264. if(pick & Math.pow(2, i)) {
  265. answers[i].click();
  266. }
  267. }
  268. }
  269. } else if (auto_answer == 'First') {
  270. answers[0].click();
  271. }
  272. variant = document.getElementById('w0').parentNode.textContent;
  273. variant = variant.slice(variant.indexOf("Вопрос:"));
  274. hash = hashCode(variant);
  275. update_variant();
  276. form.addEventListener('click', update_variant);
  277. }
  278.  
  279. function result_page_handler() {
  280. var i;
  281. var correct = variant.slice(variant.indexOf("Число верных ответов: ") + 22);
  282. correct = correct.slice(0, correct.indexOf("\n"));
  283. var test = GM_getValue('tests', new Object())[testID];
  284. if (test === undefined) {
  285. return;
  286. }
  287. var printer = "";
  288. var sorted_test = [];
  289. for (var hash in test) {
  290. sorted_test.push([hash].concat(test[hash]));
  291. }
  292. sorted_test.sort((a, b) => {return a[1] - b[1]});
  293. console.log(sorted_test);
  294. for (i = 0; i < sorted_test.length; i++) {
  295. printer += (config.get('append_question_number') ? (sorted_test[i][1] + ") ") : "") + sorted_test[i][0] + " " + sorted_test[i][2] + "\n";
  296. }
  297. printer += correct;
  298. if (config.get('copy_answers')) {
  299. GM_setClipboard(printer);
  300. }
  301. if (config.get('accumulator_enable')) {
  302. var acc = GM_getValue('accumulated_answers', "");
  303. if (acc != "") {
  304. acc += "\n\n";
  305. }
  306. var prefix = config.get('accumulator_prefix');
  307. if (prefix != "") {
  308. acc += prefix + "\n";
  309. }
  310. acc += printer;
  311. GM_setValue('accumulated_answers', acc);
  312. printer = acc;
  313. }
  314. 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>";
  315. var pboxes = document.getElementsByTagName('p');
  316. for (i = 0; i < pboxes.length; i++) {
  317. var pbox = pboxes[i];
  318. if (pbox.textContent.includes("Попытка ")) {
  319. pbox.outerHTML += printer;
  320. break;
  321. }
  322. }
  323. var clear = GM_getValue('clear_tests', new Object());
  324. clear[testID] = true;
  325. GM_setValue('clear_tests', clear);
  326. }
  327.  
  328. function DB_cleaner() {
  329. var clear = GM_getValue('clear_tests', new Object());
  330. var tests = GM_getValue('tests', new Object());
  331. for (var test in clear) {
  332. delete tests[test];
  333. }
  334. GM_setValue('tests', tests);
  335. GM_setValue('clear_tests', new Object());
  336. }
  337.  
  338. function press_continue_btn() {
  339. var i;
  340. var buttons = document.getElementsByTagName('button');
  341. var button = undefined;
  342. for (i = 0; i < buttons.length; i++) {
  343. var btn = buttons[i];
  344. if (btn.textContent.includes("Пройти") || btn.textContent.includes("Продолжить")) {
  345. button = btn;
  346. break;
  347. }
  348. }
  349. if (button === undefined) {
  350. return;
  351. }
  352. if (button.textContent.includes("Пройти")) {
  353. window.location.replace(button.parentNode.href);
  354. } else if (button.textContent.includes("Продолжить")) {
  355. button.click();
  356. }
  357. }
  358.  
  359. function actionFunction() {
  360. var old_time, cur_time;
  361. variant = document.getElementById('w0').parentNode.textContent;
  362. if (variant.includes("Вопрос:")) {
  363. DB_cleaner();
  364. test_form_handler();
  365. if (config.get('auto_continue')) {
  366. old_time = config.get('auto_continue_time');
  367. cur_time = Date.now();
  368. if (cur_time - old_time > 60 * 60 * 1000) {
  369. config.set('auto_continue', false);
  370. } else {
  371. press_continue_btn();
  372. }
  373. }
  374. } else if (variant.includes("Результат прохождения теста:")) {
  375. result_page_handler();
  376. if (config.get('auto_restart')) {
  377. old_time = config.get('auto_restart_time');
  378. cur_time = Date.now();
  379. if (cur_time - old_time > 60 * 60 * 1000) {
  380. config.set('auto_restart', false);
  381. } else {
  382. press_continue_btn();
  383. }
  384. }
  385. }
  386. }