Sorryops

Collect and reuse ORIOKS test answers

  1. // ==UserScript==
  2. // @name Sorryops
  3. // @name:ru Сориупс
  4. // @namespace https://git.disroot.org/electromagneticcyclone/sorryops
  5. // @version 20240503.2
  6. // @description Collect and reuse ORIOKS test answers
  7. // @description:ru Скрипт для сбора и переиспользования ответов на тесты ОРИОКС
  8. // @icon https://sorryops.ru/favicon.ico
  9. // @author electromagneticcyclone & angelbeautifull
  10. // @license GPL-3.0-or-later
  11. // @supportURL https://git.disroot.org/electromagneticcyclone/sorryops
  12. // @match https://orioks.miet.ru/student/student/test*
  13. // @grant GM_getValue
  14. // @grant GM_setValue
  15. // @grant GM_addStyle
  16. // @grant GM_registerMenuCommand
  17. // @grant GM_setClipboard
  18. // @grant GM_xmlhttpRequest
  19. // @grant GM_openInTab
  20. // @require https://openuserjs.org/src/libs/sizzle/GM_config.js
  21. // @connect sorryops.ru
  22. // @run-at document-start
  23. // ==/UserScript==
  24.  
  25. /* Version */
  26. const VERSION = "20240503.2";
  27. /* End Version */
  28.  
  29. /* Charset */
  30. const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
  31. /* End Charset */
  32.  
  33. /* Labels */
  34.  
  35. const all_labels = {
  36. en: {
  37. l: "English",
  38. settings_title: "Settings",
  39. script_language: "Language",
  40. show_user_id: "Show user ID",
  41. user_id: "User ID (keep private)",
  42. server: "Sync answers with server (leave blank to disable)",
  43. wait_server_response: "Wait server response",
  44. auto_answer: "Auto answer",
  45. auto_answer_no: "No",
  46. auto_answer_first: "First",
  47. auto_answer_random: "Random",
  48. auto_answer_not_greedy: "Choose a known answer if there are more wrong answers than that number",
  49. auto_answer_greed_level: "Number of randomly answered questions",
  50. display_values: "Answers variant",
  51. display_values_ori: "ORIOKS",
  52. display_values_sorry: "Sorry",
  53. display_values_both: "Both",
  54. display_answer: "Display answer near variant",
  55. stop_timer: "Freeze and hide timer",
  56. register_keyboard_keys: "Register hotkeys",
  57. copy_answers: "Copy results to the clipboard",
  58. append_question_number: "Show question numbers in the final report",
  59. accumulator_enable: "Accumulate test results in one field",
  60. auto_continue: "Auto continue (DANGEROUS!!! Will be disabled after an hour. Press `d` to disable)",
  61. auto_restart: "Auto restart (DANGEROUS!!! Will be disabled after an hour. Press `d` to disable. Make sure you have infinite attempts)",
  62. },
  63. ru: {
  64. l: "Русский",
  65. settings_title: "Настройки",
  66. script_language: "Язык",
  67. show_user_id: "Показать индетификатор пользователя",
  68. user_id: "Индетификатор (держать в секрете)",
  69. server: "Синхронизировать ответы с сервером (оставить пустым для отключения)",
  70. wait_server_response: "Ждать ответа сервера",
  71. auto_answer: "Автовыбор ответа",
  72. auto_answer_no: "Нет",
  73. auto_answer_first: "Первый",
  74. auto_answer_random: "Случайный",
  75. auto_answer_not_greedy: "Выбирать известный ответ, если неправильных больше этого числа",
  76. auto_answer_greed_level: "Количество вопросов, на которые будет случайный ответ",
  77. display_values: "Вариант отображения ответов",
  78. display_values_ori: "ОРИОКС",
  79. display_values_sorry: "Сори",
  80. display_values_both: "Оба",
  81. display_answer: "Отображать ответ рядом с вариантом",
  82. stop_timer: "Заморозить и скрыть таймер",
  83. register_keyboard_keys: "Горячие клавиши",
  84. copy_answers: "Копировать результаты в буфер обмена",
  85. append_question_number: "Отображать номер вопроса в финальном отчёте",
  86. accumulator_enable: "Собирать отчёты в одно поле",
  87. auto_continue: "Автопродолжение (ОПАСНО!!! Отключается через час. Нажмите `d`, чтобы остановить)",
  88. auto_restart: "Автоперезапуск (ОПАСНО!!! Отключается через час. Нажмите `d`, чтобы остановить. Убедитесь, что количество попыток неограничено)",
  89. },
  90. };
  91.  
  92. var labels = all_labels[(() => {
  93. var lang = GM_getValue('language', "-");
  94. if (!lang || (lang == "-")) {
  95. lang = navigator.language || navigator.userLanguage;
  96. }
  97. for (var l in all_labels) {
  98. if (lang.includes(l)) {
  99. return l;
  100. }
  101. }
  102. })()];
  103. if (labels == undefined) {
  104. labels = all_labels.ru;
  105. }
  106.  
  107. /* End Labels */
  108.  
  109. /* Config */
  110.  
  111. var config = new GM_config({
  112. id: 'config',
  113. title: labels.settings_title,
  114. fields: {
  115. script_language: {
  116. label: labels.script_language,
  117. type: 'select',
  118. options: [
  119. '-',
  120. all_labels.en.l,
  121. all_labels.ru.l,
  122. ],
  123. default: '-',
  124. },
  125. show_user_id: {
  126. label: labels.show_user_id,
  127. type: 'checkbox',
  128. default: false,
  129. },
  130. user_id: {
  131. label: labels.user_id + (GM_getValue("show_user_id", false) ? "" : "<input readonly value='******'>"),
  132. type: GM_getValue("show_user_id", false) ? 'text' : 'hidden',
  133. save: false,
  134. default: '',
  135. },
  136. valid_user_id: {
  137. type: 'hidden',
  138. default: '',
  139. },
  140. server: {
  141. label: labels.server,
  142. type: 'text',
  143. default: '',
  144. },
  145. wait_server_response: {
  146. label: labels.wait_server_response,
  147. type: 'checkbox',
  148. default: true,
  149. },
  150. auto_answer: {
  151. label: labels.auto_answer,
  152. type: 'select',
  153. options: [
  154. labels.auto_answer_no,
  155. labels.auto_answer_first,
  156. labels.auto_answer_random,
  157. ],
  158. default: labels.auto_answer_no,
  159. },
  160. auto_answer_not_greedy: {
  161. label: labels.auto_answer_not_greedy,
  162. type: 'number',
  163. default: -1,
  164. },
  165. auto_answer_greed_level: {
  166. label: labels.auto_answer_greed_level,
  167. type: 'number',
  168. default: -1,
  169. },
  170. display_values: {
  171. label: labels.display_values,
  172. type: 'select',
  173. options: [
  174. labels.display_values_ori,
  175. labels.display_values_sorry,
  176. labels.display_values_both,
  177. ],
  178. default: labels.display_values_ori,
  179. },
  180. display_answer: {
  181. label: labels.display_answer,
  182. type: 'checkbox',
  183. default: true,
  184. },
  185. stop_timer: {
  186. label: labels.stop_timer,
  187. type: 'checkbox',
  188. default: false,
  189. },
  190. register_keyboard_keys: {
  191. label: labels.register_keyboard_keys,
  192. type: 'checkbox',
  193. default: true,
  194. },
  195. copy_answers: {
  196. label: labels.copy_answers,
  197. type: 'checkbox',
  198. default: false,
  199. },
  200. append_question_number: {
  201. label: labels.append_question_number,
  202. type: 'checkbox',
  203. default: true,
  204. },
  205. accumulator_enable: {
  206. label: labels.accumulator_enable,
  207. type: 'checkbox',
  208. default: false,
  209. },
  210. auto_continue: {
  211. label: labels.auto_continue,
  212. type: 'checkbox',
  213. default: false,
  214. },
  215. auto_continue_time: {
  216. type: 'hidden',
  217. default: 0,
  218. },
  219. auto_restart: {
  220. label: labels.auto_restart,
  221. type: 'checkbox',
  222. default: false,
  223. },
  224. auto_restart_time: {
  225. type: 'hidden',
  226. default: 0,
  227. },
  228. },
  229. events: {
  230. init: function() {
  231. var valid_user_id = this.get('valid_user_id');
  232. if (!validate_user_id(valid_user_id)) {
  233. valid_user_id = generate_user_id();
  234. }
  235. this.set('user_id', valid_user_id);
  236. this.set('valid_user_id', valid_user_id);
  237. GM_setValue('show_user_id', this.get('show_user_id'));
  238. GM_setValue('stop_timer', this.get('stop_timer'));
  239. if (this.get('auto_continue') && (this.get('auto_answer') == "No")) {
  240. this.set('auto_continue', false);
  241. }
  242. if (this.get('accumulator_enable') == false) {
  243. GM_setValue('accumulated_answers', "");
  244. }
  245. switch (this.get('script_language')) {
  246. case all_labels.en.l:
  247. GM_setValue('language', "en");
  248. break;
  249. case all_labels.ru.l:
  250. GM_setValue('language', "ru");
  251. break;
  252. default:
  253. GM_setValue('language', "-");
  254. break;
  255. }
  256. },
  257. save: function(forgotten) {
  258. this.set('auto_continue_time', Date.now());
  259. this.set('auto_restart_time', Date.now());
  260. if (this.isOpen && this.get('auto_continue') && (this.get('auto_answer') == "No")) {
  261. this.set('auto_continue', false);
  262. alert("Can't automatically continue without answer.");
  263. }
  264. if (!validate_user_id(forgotten['user_id'])) {
  265. this.set('user_id', this.get('valid_user_id'))
  266. alert('User ID is invalid!');
  267. } else {
  268. this.set('valid_user_id', forgotten['user_id'])
  269. }
  270. this.init();
  271. },
  272. },
  273. });
  274.  
  275. GM_registerMenuCommand(labels.settings_title, () => {
  276. config.open();
  277. });
  278.  
  279. /* End Config */
  280.  
  281. /* Server */
  282.  
  283. function send_to_server(results) {
  284. var server = config.get('server');
  285. if (server != '') {
  286. GM_xmlhttpRequest({
  287. method: 'POST',
  288. url: 'https://' + server,
  289. headers: {
  290. 'Content-Type': 'application/json',
  291. },
  292. data: JSON.stringify(results),
  293. });
  294. }
  295. }
  296.  
  297. function fetch_from_server(path, func) {
  298. var server = config.get('server');
  299. var fetched_data = GM_getValue('fetched_data');
  300. if ((fetched_data == undefined) || (fetched_data.forbidden == true)) {
  301. fetched_data = {};
  302. }
  303. if ((server != '') && (Object.keys(fetched_data).length == 0)) {
  304. GM_xmlhttpRequest({
  305. method: 'GET',
  306. url: 'https://' + server + '/' + path + '?uid=' + config.get('user_id') + "&sid=" + student_name,
  307. timeout: 1000,
  308. onload: function (response) {
  309. var text = response.responseText;
  310. if (response.status == 404) {
  311. fetched_data = {version: VERSION};
  312. GM_setValue('fetched_data', fetched_data);
  313. func(fetched_data);
  314. } else if (response.status == 403) {
  315. fetched_data = {version: VERSION, forbidden: true};
  316. forbidden = true;
  317. GM_setValue('fetched_data', fetched_data);
  318. func(fetched_data);
  319. } else if (!text.includes("{")) {
  320. func({});
  321. } else {
  322. fetched_data = JSON.parse(text);
  323. GM_setValue('fetched_data', fetched_data);
  324. func(fetched_data);
  325. }
  326. },
  327. onerror: function (e) {
  328. func({});
  329. },
  330. onabort: function (e) {
  331. func({});
  332. },
  333. ontimeout: function (e) {
  334. func({});
  335. }
  336. });
  337. } else {
  338. func(fetched_data);
  339. }
  340. }
  341.  
  342. /* End Server */
  343.  
  344. /* Stop timer */
  345.  
  346. if (GM_getValue('stop_timer', true)) {
  347. var i, pbox;
  348. var pboxes = document.getElementsByTagName('p');
  349. for (i = 0; i < pboxes.length; i++) {
  350. pbox = pboxes[i];
  351. if (pbox.textContent.includes("Осталось:")) {
  352. pbox.parentNode.remove();
  353. document.getElementsByTagName('hr')[0].remove();
  354. var injectFakeTimer = function(window) {
  355. window.setInterval = (f, t, its_me = false) => {
  356. return window.setInterval(f, its_me ? t : 10^999);
  357. };
  358. }
  359. var scriptFakeTimer = document.createElement('script');
  360. scriptFakeTimer.setAttribute("type", "application/javascript");
  361. scriptFakeTimer.textContent = '(' + injectFakeTimer + ')(window);';
  362. document.body.appendChild(scriptFakeTimer);
  363. break;
  364. }
  365. }
  366. }
  367.  
  368. /* End Stop timer */
  369.  
  370. /* Events */
  371.  
  372. window.addEventListener('load', main);
  373. window.onkeydown = (e) => {
  374. if ((e.key == "Enter") && config.get('register_keyboard_keys')) {
  375. press_continue_btn();
  376. }
  377. if (e.key == "d") {
  378. config.set('auto_continue', false);
  379. config.set('auto_restart', false);
  380. config.save();
  381. }
  382. };
  383.  
  384. /* End Events */
  385.  
  386. /* Page properties */
  387.  
  388. // const success = -1487162948;
  389. var answers = [];
  390. var sorted_objects_value = [];
  391. var variant, hash, type, correct, incorrect, version;
  392. var student_name = "";
  393. var forbidden = false;
  394. var prev_new_answer_f = false;
  395. var new_answer_f = false;
  396. var testID = (() => {
  397. var url = document.URL;
  398. url = url.slice(url.indexOf("idKM=") + 5);
  399. url = url.slice(0, url.indexOf("&"));
  400. return url;
  401. })();
  402.  
  403. /* End properties */
  404.  
  405. /* Functions */
  406.  
  407. function comb(s, blacklist = []) {
  408. var result = [];
  409. for (var i = 1; i < (1 << s.length); i++) {
  410. var temp = '';
  411. for (var j = 0; j < s.length; j++) {
  412. if (i & Math.pow(2, j)) {
  413. temp += s[j];
  414. }
  415. }
  416. temp = "{" + temp + "}";
  417. if (!blacklist.includes(temp)) {
  418. result.push(temp);
  419. }
  420. }
  421. return result;
  422. }
  423.  
  424. // https://github.com/ajayyy/maze-utils/blob/036086403f675b8fea0e22065f26ba534e351562/src/setup.ts
  425. function generate_user_id(length = 36) {
  426. var i;
  427. var result = "";
  428. const cryptoFuncs = typeof window === "undefined" ? crypto : window.crypto;
  429. if (cryptoFuncs && cryptoFuncs.getRandomValues) {
  430. const values = new Uint32Array(length);
  431. cryptoFuncs.getRandomValues(values);
  432. for (i = 0; i < length; i++) {
  433. result += charset[values[i] % charset.length];
  434. }
  435. } else {
  436. for (i = 0; i < length; i++) {
  437. result += charset[Math.floor(Math.random() * charset.length)];
  438. }
  439. }
  440. return result;
  441. }
  442.  
  443. function validate_user_id(uid, length = 36) {
  444. var i;
  445. if (uid.length != length) {
  446. return false;
  447. }
  448. for (i = 0; i < length; i++) {
  449. if (!charset.includes(uid[i])) {
  450. return false;
  451. }
  452. }
  453. return true;
  454. }
  455.  
  456. // https://stackoverflow.com/a/15710692
  457. function hashCode(s, return_num = false) {
  458. var result = "";
  459. var h = s.split("").reduce(function(a, b) {
  460. a = ((a << 5) - a) + b.charCodeAt(0);
  461. return a & a;
  462. }, 0);
  463. if (return_num) {
  464. return h;
  465. }
  466. while (h != 0) {
  467. result += charset[((h % charset.length) + charset.length) % charset.length];
  468. h = Math.floor(Math.abs(h) / charset.length) * (h / Math.abs(h));
  469. }
  470. return result;
  471. }
  472.  
  473. function set_to_clear(id, exec_if_not_cleared) {
  474. var clear = GM_getValue('clear_tests', new Object());
  475. if (!clear[id]) {
  476. exec_if_not_cleared();
  477. }
  478. clear[id] = true;
  479. GM_setValue('clear_tests', clear);
  480. }
  481.  
  482. function DB_cleaner() {
  483. var clear = GM_getValue('clear_tests', new Object());
  484. var tests = GM_getValue('tests', new Object());
  485. for (var test in clear) {
  486. delete tests[test];
  487. }
  488. GM_setValue('tests', tests);
  489. GM_setValue('clear_tests', new Object());
  490. }
  491.  
  492. function press_continue_btn() {
  493. var i;
  494. var buttons = document.getElementsByTagName('button');
  495. var button = undefined;
  496. for (i = 0; i < buttons.length; i++) {
  497. var btn = buttons[i];
  498. if (btn.textContent.includes("Пройти") || btn.textContent.includes("Продолжить")) {
  499. button = btn;
  500. break;
  501. }
  502. }
  503. if (button === undefined) {
  504. return;
  505. }
  506. if (button.textContent.includes("Пройти")) {
  507. window.location.replace(button.parentNode.href);
  508. } else if (button.textContent.includes("Продолжить")) {
  509. button.click();
  510. }
  511. }
  512.  
  513. function calculate_variant_hash() {
  514. variant = document.getElementById('w0').parentNode.textContent;
  515. variant = variant.slice(variant.indexOf("Вопрос:"));
  516. hash = hashCode(variant);
  517. }
  518.  
  519. function update_variant() {
  520. var i, pbox;
  521. var status = "Неизвестен";
  522. var chosen_answer = "";
  523. switch (type) {
  524. case 'checkbox':
  525. case 'radio': {
  526. for (i = 0; i < answers.length; i++) {
  527. chosen_answer += answers[i].checked ? answers[i].sorry_value : "";
  528. }
  529. chosen_answer = chosen_answer.split('').sort().join('');
  530. if (type == 'checkbox') {
  531. chosen_answer = "{" + chosen_answer + "}";
  532. }
  533. } break;
  534. case 'text': {
  535. for (i = 0; i < answers.length; i++) {
  536. chosen_answer += "[" + answers[i].value + "]";
  537. }
  538. }
  539. }
  540. new_answer_f = true;
  541. if ((version != VERSION) && (version !== undefined)) {
  542. status = "<span style='color: red;'>Скрипт устарел</span>";
  543. GM_setValue('fetched_data', {});
  544. } else if (version === undefined) {
  545. if (config.get('wait_server_response')) {
  546. status = "<span style='color: blue;'>Ожидание ответа</span>";
  547. } else {
  548. status = "<span style='color: red;'>Нет соединения</span>";
  549. }
  550. } else if (forbidden) {
  551. status = "<span style='color: red;'>Доступ запрещён</span>";
  552. } else if (chosen_answer == correct) {
  553. status = "<span style='color: green;'>Верно</span>";
  554. new_answer_f = false;
  555. } else if (incorrect.includes(chosen_answer)) {
  556. status = "<span style='color: red;'>Неверно</span>";
  557. new_answer_f = false;
  558. }
  559. GM_setValue('new_answer_f', prev_new_answer_f || new_answer_f);
  560. var pboxes = document.getElementsByTagName('p');
  561. const display_answer = config.get('display_answer');
  562. for (i = 0; i < pboxes.length; i++) {
  563. pbox = pboxes[i];
  564. if (pbox.textContent.includes("Вопрос:")) {
  565. pbox.innerHTML = "<i>(Вариант <input onfocus='this.select();' id='variant' value='" + hash + (display_answer == true ? (" " + chosen_answer) : "") + "' readonly>)</i>";
  566. if (config.get('server') != '') {
  567. pbox.innerHTML += "<br>Статус: " + status;
  568. }
  569. pbox.innerHTML += "<br>Вопрос:";
  570. break;
  571. }
  572. }
  573. var question_num = undefined;
  574. for (i = 0; i < pboxes.length; i++) {
  575. pbox = pboxes[i];
  576. if (pbox.textContent.includes("Текущий вопрос: ")) {
  577. question_num = pbox.textContent.slice(variant.indexOf("Текущий вопрос: ") + 16).trim();
  578. break;
  579. }
  580. }
  581. if (hash !== undefined) {
  582. var tests = GM_getValue('tests', new Object());
  583. if (tests[testID] === undefined) {
  584. tests[testID] = new Object();
  585. }
  586. tests[testID][hash] = [question_num, chosen_answer, answers.length];
  587. GM_setValue('tests', tests);
  588. }
  589. }
  590.  
  591. function color_answers() {
  592. var answer, correct_element, incorrect_element, sorry_val;
  593. switch (type) {
  594. case 'radio': {
  595. for (answer in answers) {
  596. if (answers[answer].sorry_value == correct) {
  597. if (!answers[answer].sorry_colored && (version !== undefined)) {
  598. correct_element = answers[answer].parentNode;
  599. sorry_val = answers[answer].sorry_value;
  600. correct_element.innerHTML = "<span style='color: green;'>" + correct_element.innerHTML + "</span>";
  601. answers[answer] = correct_element.getElementsByTagName('input')[0];
  602. answers[answer].sorry_value = sorry_val;
  603. answers[answer].sorry_colored = true;
  604. }
  605. }
  606. if (incorrect.includes(answers[answer].sorry_value)) {
  607. if (!answers[answer].sorry_colored && (version !== undefined)) {
  608. incorrect_element = answers[answer].parentNode;
  609. sorry_val = answers[answer].sorry_value;
  610. incorrect_element.innerHTML = "<span style='color: red;'>" + incorrect_element.innerHTML + "</span>";
  611. answers[answer] = incorrect_element.getElementsByTagName('input')[0];
  612. answers[answer].sorry_value = sorry_val;
  613. answers[answer].sorry_colored = true;
  614. }
  615. }
  616. }
  617. } break;
  618. case 'checkbox': {
  619. for (answer in answers) {
  620. if (correct != undefined) {
  621. if (correct.includes(answers[answer].sorry_value)) {
  622. if (!answers[answer].sorry_colored && (version !== undefined)) {
  623. correct_element = answers[answer].parentNode;
  624. sorry_val = answers[answer].sorry_value;
  625. correct_element.innerHTML = "<span style='color: green;'>" + correct_element.innerHTML + "</span>";
  626. answers[answer] = correct_element.getElementsByTagName('input')[0];
  627. answers[answer].sorry_value = sorry_val;
  628. answers[answer].sorry_colored = true;
  629. }
  630. }
  631. }
  632. }
  633. } break;
  634. }
  635. }
  636.  
  637. function auto_answer() {
  638. var answer, chosen_answer, sorry_val, correct_element, incorrect_element;
  639. const auto_answer = config.get('auto_answer');
  640. function pick_answer(picked) {
  641. switch (type) {
  642. case 'radio': {
  643. for (answer in answers) {
  644. if (answers[answer].sorry_value == picked) {
  645. answers[answer].click();
  646. }
  647. }
  648. } break;
  649. case 'checkbox': {
  650. for (answer in answers) {
  651. if (picked.includes(answers[answer].sorry_value)) {
  652. answers[answer].click();
  653. }
  654. }
  655. } break;
  656. case 'text': {
  657. var corr = picked.slice(1, picked.length - 1).split('][');
  658. for (i = 0; (i < answers.leght) && (i < corr.length); i++) {
  659. answers[i].placeholder = corr[i];
  660. }
  661. } break;
  662. }
  663. }
  664. var greed = GM_getValue('greed');
  665. if (greed === undefined) {
  666. greed = 99999;
  667. }
  668. if (correct != undefined) {
  669. pick_answer(correct);
  670. } else if (auto_answer == labels.auto_answer_random) {
  671. if ((incorrect.length >= Math.max(config.get('auto_answer_not_greedy'), 1)) &&
  672. ((config.get('auto_answer_not_greedy') > 0) || (greed <= 0))) {
  673. chosen_answer = Math.floor(Math.random() * incorrect.length);
  674. pick_answer(incorrect[chosen_answer]);
  675. } else {
  676. GM_setValue('greed', greed - 1);
  677. switch (type) {
  678. case 'radio': {
  679. var possible_answers = [];
  680. for (answer in answers) {
  681. if (incorrect.includes(answers[answer].sorry_value) == false) {
  682. possible_answers.push(answer);
  683. }
  684. }
  685. chosen_answer = Math.floor(Math.random() * possible_answers.length);
  686. answers[possible_answers[chosen_answer]].click();
  687. } break;
  688. case 'checkbox': {
  689. var combs = comb(charset.slice(0, answers.length), incorrect);
  690. var pick = combs[Math.floor(Math.random() * combs.length)];
  691. for (i = 0; i < answers.length; i++) {
  692. if(pick.includes(answers[i].sorry_value)) {
  693. answers[i].click();
  694. }
  695. }
  696. } break;
  697. }
  698. }
  699. } else if (auto_answer == labels.auto_answer_first) {
  700. Object.values(sorted_objects_value)[0].click();
  701. }
  702. }
  703.  
  704. function parse_server_data(server_data) {
  705. version = server_data.version;
  706. if ((version !== VERSION) && (version !== undefined)) {
  707. console.warn("Sorryops is outdated");
  708. server_data = {};
  709. GM_openInTab("https://greasyfork.org/en/scripts/481036-sorryops");
  710. config.set('auto_continue', false);
  711. }
  712. if (server_data.hasOwnProperty('correct')) {
  713. correct = server_data.correct;
  714. if (correct === undefined) {
  715. correct = undefined;
  716. } else if (correct.hasOwnProperty(hash)) {
  717. correct = correct[hash];
  718. } else {
  719. correct = undefined;
  720. }
  721. } else {
  722. correct = undefined;
  723. }
  724. if (server_data.hasOwnProperty('incorrect')) {
  725. incorrect = server_data.incorrect;
  726. if (incorrect === undefined) {
  727. incorrect = [];
  728. } else if (incorrect.hasOwnProperty(hash)) {
  729. incorrect = incorrect[hash];
  730. } else {
  731. incorrect = [];
  732. }
  733. } else {
  734. incorrect = [];
  735. }
  736. }
  737.  
  738. function auto_continue() {
  739. var old_time, cur_time;
  740. if (config.get('auto_continue')) {
  741. old_time = config.get('auto_continue_time');
  742. cur_time = Date.now();
  743. if (cur_time - old_time > 60 * 60 * 1000) {
  744. config.set('auto_continue', false);
  745. } else {
  746. press_continue_btn();
  747. }
  748. }
  749. }
  750.  
  751. function auto_restart() {
  752. var old_time, cur_time;
  753. if (config.get('auto_restart')) {
  754. old_time = config.get('auto_restart_time');
  755. cur_time = Date.now();
  756. if (cur_time - old_time > 60 * 60 * 1000) {
  757. config.set('auto_restart', false);
  758. } else {
  759. press_continue_btn();
  760. }
  761. }
  762. }
  763.  
  764. /* End Functions */
  765.  
  766. /* Handlers */
  767.  
  768. function test_form_handler(server_response) {
  769. var i, key, answer, sorry_val;
  770. var complicated_hash_f = false;
  771. var boxes = [];
  772. var sorted_objects;
  773. var objects_hash = new Object();
  774. var objects_value = new Object();
  775. var form = document.getElementById('testform-answer');
  776. var manual_form = document.getElementById('testform-answer-0');
  777. if (form != null) {
  778. boxes = form.getElementsByTagName('input');
  779. } else if (manual_form != null) {
  780. i = 1;
  781. while (manual_form != null) {
  782. boxes.push(manual_form);
  783. manual_form = document.getElementById('testform-answer-' + i++);
  784. }
  785. }
  786. type = boxes[0].type;
  787. for (i = 0; i < boxes.length; i++) {
  788. if (boxes[i].parentNode.innerHTML.includes("<img")) {
  789. complicated_hash_f = true;
  790. break;
  791. }
  792. }
  793. switch (type) {
  794. case 'checkbox':
  795. case 'radio': {
  796. for (i = 0; i < boxes.length; i++) {
  797. boxes[i].hash = hashCode(complicated_hash_f ? boxes[i].parentNode.innerHTML : boxes[i].parentNode.innerText, true);
  798. objects_hash[boxes[i].hash] = boxes[i];
  799. objects_value[boxes[i].value] = boxes[i];
  800. }
  801. const sorted_objects_hash = Object.keys(objects_hash).sort().reduce(
  802. (obj, key) => {
  803. obj[key] = objects_hash[key];
  804. return obj;
  805. }, {}
  806. );
  807. sorted_objects_value = Object.keys(objects_value).sort().reduce(
  808. (obj, key) => {
  809. obj[key] = objects_value[key];
  810. return obj;
  811. }, {}
  812. );
  813. i = 0;
  814. sorted_objects = sorted_objects_hash;
  815. for (key in sorted_objects) {
  816. sorted_objects[key].parentNode.remove();
  817. form.appendChild(sorted_objects[key].parentNode);
  818. }
  819. calculate_variant_hash();
  820. parse_server_data(server_response);
  821. for (key in sorted_objects) {
  822. sorted_objects[key].sorry_value = charset[i++];
  823. var span = document.createElement('span');
  824. var disp_val;
  825. switch (config.get('display_values')) {
  826. case labels.display_values_ori:
  827. disp_val = sorted_objects[key].value;
  828. break;
  829. case labels.display_values_sorry:
  830. disp_val = sorted_objects[key].sorry_value;
  831. break;
  832. case labels.display_values_both:
  833. disp_val = sorted_objects[key].value + ":" + sorted_objects[key].sorry_value;
  834. break;
  835. }
  836. span.innerHTML = disp_val + ") ";
  837. sorted_objects[key].parentNode.insertBefore(span, sorted_objects[key]);
  838. answers.push(sorted_objects[key]);
  839. }
  840. if (config.get('display_values') == labels.display_values_ori) {
  841. for (key in sorted_objects_value) {
  842. sorted_objects_value[key].parentNode.remove();
  843. form.appendChild(sorted_objects_value[key].parentNode);
  844. }
  845. }
  846. } break;
  847. case 'text': {
  848. answers = boxes;
  849. calculate_variant_hash();
  850. } break;
  851. }
  852. color_answers();
  853. auto_answer();
  854. update_variant();
  855. for (i = 0; i < answers.length; i++) {
  856. answers[i].addEventListener('change', update_variant);
  857. }
  858. }
  859.  
  860. function result_page_handler() {
  861. var i;
  862. var correct_num = variant.slice(variant.indexOf("Число верных ответов: ") + 22);
  863. var all_num = variant.slice(variant.indexOf("Число неверных ответов: ") + 24);
  864. correct_num = correct_num.slice(0, correct_num.indexOf("\n")).trim();
  865. all_num = all_num.slice(0, all_num.indexOf("\n")).trim();
  866. all_num = (parseInt(all_num) + parseInt(correct_num)).toString();
  867. var test = GM_getValue('tests', new Object())[testID];
  868. if (test === undefined) {
  869. return;
  870. }
  871. var printer = "";
  872. var sorted_test = [];
  873. for (var hash in test) {
  874. sorted_test.push([hash].concat(test[hash]));
  875. }
  876. sorted_test.sort((a, b) => {return a[1] - b[1]});
  877. for (i = 0; i < sorted_test.length; i++) {
  878. printer += (config.get('append_question_number') ? (sorted_test[i][1] + ") ") : "") + sorted_test[i][0] + " " + sorted_test[i][2] + "\n";
  879. }
  880. printer += correct_num;
  881. if (config.get('copy_answers')) {
  882. GM_setClipboard(printer);
  883. }
  884. if (config.get('accumulator_enable')) {
  885. var acc = GM_getValue('accumulated_answers', "");
  886. if (acc != "") {
  887. acc += "\n\n";
  888. }
  889. var prefix = testID;
  890. if (prefix != "") {
  891. acc += prefix + "\n";
  892. }
  893. acc += printer;
  894. GM_setValue('accumulated_answers', acc);
  895. printer = acc;
  896. }
  897. 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>";
  898. var pboxes = document.getElementsByTagName('p');
  899. for (i = 0; i < pboxes.length; i++) {
  900. var pbox = pboxes[i];
  901. if (pbox.textContent.includes("Попытка ")) {
  902. pbox.outerHTML += printer;
  903. break;
  904. }
  905. }
  906. set_to_clear(testID, () => {
  907. if (GM_getValue('new_answer_f')) {
  908. send_to_server({
  909. type: "test_results",
  910. uid: config.get('user_id'),
  911. sid: student_name,
  912. id: testID,
  913. answers: sorted_test,
  914. correct: correct_num,
  915. all: all_num,
  916. });
  917. }
  918. GM_setValue('fetched_data', {});
  919. GM_setValue('new_answer_f', false);
  920. var greed = config.get('auto_answer_greed_level');
  921. if (greed < 0) {
  922. greed = 99999;
  923. }
  924. GM_setValue('greed', greed);
  925. });
  926. }
  927.  
  928. /* End Handlers */
  929.  
  930. function main() {
  931. var abox;
  932. var aboxes = document.getElementsByTagName('a');
  933. for (abox in aboxes) {
  934. if ((aboxes[abox].className == 'dropdown-toggle') && aboxes[abox].href.endsWith('#') && !(aboxes[abox].title == 'Уведомления и объявления')) {
  935. student_name = hashCode(aboxes[abox].innerText);
  936. break;
  937. }
  938. }
  939. variant = document.getElementById('w0').parentNode.textContent;
  940. prev_new_answer_f = !!GM_getValue('new_answer_f');
  941. if (variant.includes("Вопрос:")) {
  942. fetch_from_server(testID, (server_response) => {
  943. DB_cleaner();
  944. test_form_handler(server_response);
  945. if (config.get('wait_server_response') && (version === undefined)) {
  946. window.setInterval(() => {fetch_from_server(testID, (server_response) => {
  947. if (version === undefined) {
  948. parse_server_data(server_response);
  949. color_answers();
  950. if (config.get('auto_continue')) {
  951. auto_answer();
  952. }
  953. update_variant();
  954. if (version === VERSION) {
  955. auto_continue();
  956. }
  957. }
  958. })}, 1000, true);
  959. } else {
  960. auto_continue();
  961. }
  962. });
  963. } else if (variant.includes("Результат прохождения теста:")) {
  964. result_page_handler();
  965. auto_restart();
  966. }
  967. }