Sorryops

Collect and reuse ORIOKS test answers

当前为 2024-05-02 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Sorryops
  3. // @name:ru Сориупс
  4. // @namespace https://git.disroot.org/electromagneticcyclone/sorryops
  5. // @version 20240502.4
  6. // @description Collect and reuse ORIOKS test answers
  7. // @description:ru Скрипт для сбора и переиспользования ответов на тесты ОРИОКС
  8. // @icon https://orioks.miet.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 = "20240502.4";
  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) {
  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'),
  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 (!text.includes("{")) {
  315. func({});
  316. } else {
  317. fetched_data = JSON.parse(text);
  318. GM_setValue('fetched_data', fetched_data);
  319. func(fetched_data);
  320. }
  321. },
  322. onerror: function (e) {
  323. func({});
  324. },
  325. onabort: function (e) {
  326. func({});
  327. },
  328. ontimeout: function (e) {
  329. func({});
  330. }
  331. });
  332. } else {
  333. func(fetched_data);
  334. }
  335. }
  336.  
  337. /* End Server */
  338.  
  339. /* Stop timer */
  340.  
  341. if (GM_getValue('stop_timer', true)) {
  342. var i, pbox;
  343. var pboxes = document.getElementsByTagName('p');
  344. for (i = 0; i < pboxes.length; i++) {
  345. pbox = pboxes[i];
  346. if (pbox.textContent.includes("Осталось:")) {
  347. pbox.parentNode.remove();
  348. document.getElementsByTagName('hr')[0].remove();
  349. var injectFakeTimer = function(window) {
  350. window.setInterval = (f, t, its_me = false) => {
  351. return window.setInterval(f, its_me ? t : 10^999);
  352. };
  353. }
  354. var scriptFakeTimer = document.createElement('script');
  355. scriptFakeTimer.setAttribute("type", "application/javascript");
  356. scriptFakeTimer.textContent = '(' + injectFakeTimer + ')(window);';
  357. document.body.appendChild(scriptFakeTimer);
  358. break;
  359. }
  360. }
  361. }
  362.  
  363. /* End Stop timer */
  364.  
  365. /* Events */
  366.  
  367. window.addEventListener('load', main);
  368. window.onkeydown = (e) => {
  369. if ((e.key == "Enter") && config.get('register_keyboard_keys')) {
  370. press_continue_btn();
  371. }
  372. if (e.key == "d") {
  373. config.set('auto_continue', false);
  374. config.set('auto_restart', false);
  375. config.save();
  376. }
  377. };
  378.  
  379. /* End Events */
  380.  
  381. /* Page properties */
  382.  
  383. // const success = -1487162948;
  384. var answers = [];
  385. var sorted_objects_value = [];
  386. var variant, hash, type, correct, incorrect, version;
  387. var prev_new_answer_f = false;
  388. var new_answer_f = false;
  389. var testID = (() => {
  390. var url = document.URL;
  391. url = url.slice(url.indexOf("idKM=") + 5);
  392. url = url.slice(0, url.indexOf("&"));
  393. return url;
  394. })();
  395.  
  396. /* End properties */
  397.  
  398. /* Functions */
  399.  
  400. function comb(s, blacklist = []) {
  401. var result = [];
  402. for (var i = 1; i < (1 << s.length); i++) {
  403. var temp = '';
  404. for (var j = 0; j < s.length; j++) {
  405. if (i & Math.pow(2, j)) {
  406. temp += s[j];
  407. }
  408. }
  409. temp = "{" + temp + "}";
  410. if (!blacklist.includes(temp)) {
  411. result.push(temp);
  412. }
  413. }
  414. return result;
  415. }
  416.  
  417. // https://github.com/ajayyy/maze-utils/blob/036086403f675b8fea0e22065f26ba534e351562/src/setup.ts
  418. function generate_user_id(length = 36) {
  419. var i;
  420. var result = "";
  421. const cryptoFuncs = typeof window === "undefined" ? crypto : window.crypto;
  422. if (cryptoFuncs && cryptoFuncs.getRandomValues) {
  423. const values = new Uint32Array(length);
  424. cryptoFuncs.getRandomValues(values);
  425. for (i = 0; i < length; i++) {
  426. result += charset[values[i] % charset.length];
  427. }
  428. } else {
  429. for (i = 0; i < length; i++) {
  430. result += charset[Math.floor(Math.random() * charset.length)];
  431. }
  432. }
  433. return result;
  434. }
  435.  
  436. function validate_user_id(uid, length = 36) {
  437. var i;
  438. if (uid.length != length) {
  439. return false;
  440. }
  441. for (i = 0; i < length; i++) {
  442. if (!charset.includes(uid[i])) {
  443. return false;
  444. }
  445. }
  446. return true;
  447. }
  448.  
  449. // https://stackoverflow.com/a/15710692
  450. function hashCode(s, return_num = false) {
  451. var result = "";
  452. var h = s.split("").reduce(function(a, b) {
  453. a = ((a << 5) - a) + b.charCodeAt(0);
  454. return a & a;
  455. }, 0);
  456. if (return_num) {
  457. return h;
  458. }
  459. while (h != 0) {
  460. result += charset[((h % charset.length) + charset.length) % charset.length];
  461. h = Math.floor(Math.abs(h) / charset.length) * (h / Math.abs(h));
  462. }
  463. return result;
  464. }
  465.  
  466. function set_to_clear(id, exec_if_not_cleared) {
  467. var clear = GM_getValue('clear_tests', new Object());
  468. if (!clear[id]) {
  469. exec_if_not_cleared();
  470. }
  471. clear[id] = true;
  472. GM_setValue('clear_tests', clear);
  473. }
  474.  
  475. function DB_cleaner() {
  476. var clear = GM_getValue('clear_tests', new Object());
  477. var tests = GM_getValue('tests', new Object());
  478. for (var test in clear) {
  479. delete tests[test];
  480. }
  481. GM_setValue('tests', tests);
  482. GM_setValue('clear_tests', new Object());
  483. }
  484.  
  485. function press_continue_btn() {
  486. var i;
  487. var buttons = document.getElementsByTagName('button');
  488. var button = undefined;
  489. for (i = 0; i < buttons.length; i++) {
  490. var btn = buttons[i];
  491. if (btn.textContent.includes("Пройти") || btn.textContent.includes("Продолжить")) {
  492. button = btn;
  493. break;
  494. }
  495. }
  496. if (button === undefined) {
  497. return;
  498. }
  499. if (button.textContent.includes("Пройти")) {
  500. window.location.replace(button.parentNode.href);
  501. } else if (button.textContent.includes("Продолжить")) {
  502. button.click();
  503. }
  504. }
  505.  
  506. function calculate_variant_hash() {
  507. variant = document.getElementById('w0').parentNode.textContent;
  508. variant = variant.slice(variant.indexOf("Вопрос:"));
  509. hash = hashCode(variant);
  510. }
  511.  
  512. function update_variant() {
  513. var i, pbox;
  514. var status = "Неизвестен";
  515. var chosen_answer = "";
  516. switch (type) {
  517. case 'checkbox':
  518. case 'radio': {
  519. for (i = 0; i < answers.length; i++) {
  520. chosen_answer += answers[i].checked ? answers[i].sorry_value : "";
  521. }
  522. chosen_answer = chosen_answer.split('').sort().join('');
  523. if (type == 'checkbox') {
  524. chosen_answer = "{" + chosen_answer + "}";
  525. }
  526. } break;
  527. case 'text': {
  528. for (i = 0; i < answers.length; i++) {
  529. chosen_answer += "[" + answers[i].value + "]";
  530. }
  531. }
  532. }
  533. new_answer_f = true;
  534. if ((version != VERSION) && (version !== undefined)) {
  535. status = "<span style='color: red;'>Скрипт устарел</span>";
  536. GM_setValue('fetched_data', {});
  537. } else if (version === undefined) {
  538. if (config.get('wait_server_response')) {
  539. status = "<span style='color: blue;'>Ожидание ответа</span>";
  540. } else {
  541. status = "<span style='color: red;'>Нет соединения</span>";
  542. }
  543. } else if (chosen_answer == correct) {
  544. status = "<span style='color: green;'>Верно</span>";
  545. new_answer_f = false;
  546. } else if (incorrect.includes(chosen_answer)) {
  547. status = "<span style='color: red;'>Неверно</span>";
  548. new_answer_f = false;
  549. }
  550. GM_setValue('new_answer_f', prev_new_answer_f || new_answer_f);
  551. var pboxes = document.getElementsByTagName('p');
  552. const display_answer = config.get('display_answer');
  553. for (i = 0; i < pboxes.length; i++) {
  554. pbox = pboxes[i];
  555. if (pbox.textContent.includes("Вопрос:")) {
  556. pbox.innerHTML = "<i>(Вариант <input onfocus='this.select();' id='variant' value='" + hash + (display_answer == true ? (" " + chosen_answer) : "") + "' readonly>)</i>";
  557. if (config.get('server') != '') {
  558. pbox.innerHTML += "<br>Статус: " + status;
  559. }
  560. pbox.innerHTML += "<br>Вопрос:";
  561. break;
  562. }
  563. }
  564. var question_num = undefined;
  565. for (i = 0; i < pboxes.length; i++) {
  566. pbox = pboxes[i];
  567. if (pbox.textContent.includes("Текущий вопрос: ")) {
  568. question_num = pbox.textContent.slice(variant.indexOf("Текущий вопрос: ") + 16).trim();
  569. break;
  570. }
  571. }
  572. if (hash !== undefined) {
  573. var tests = GM_getValue('tests', new Object());
  574. if (tests[testID] === undefined) {
  575. tests[testID] = new Object();
  576. }
  577. tests[testID][hash] = [question_num, chosen_answer, answers.length];
  578. GM_setValue('tests', tests);
  579. }
  580. }
  581.  
  582. function color_answers() {
  583. var answer, correct_element, incorrect_element, sorry_val;
  584. switch (type) {
  585. case 'radio': {
  586. for (answer in answers) {
  587. if (answers[answer].sorry_value == correct) {
  588. if (!answers[answer].sorry_colored && (version !== undefined)) {
  589. correct_element = answers[answer].parentNode;
  590. sorry_val = answers[answer].sorry_value;
  591. correct_element.innerHTML = "<span style='color: green;'>" + correct_element.innerHTML + "</span>";
  592. answers[answer] = correct_element.getElementsByTagName('input')[0];
  593. answers[answer].sorry_value = sorry_val;
  594. answers[answer].sorry_colored = true;
  595. }
  596. }
  597. if (incorrect.includes(answers[answer].sorry_value)) {
  598. if (!answers[answer].sorry_colored && (version !== undefined)) {
  599. incorrect_element = answers[answer].parentNode;
  600. sorry_val = answers[answer].sorry_value;
  601. incorrect_element.innerHTML = "<span style='color: red;'>" + incorrect_element.innerHTML + "</span>";
  602. answers[answer] = incorrect_element.getElementsByTagName('input')[0];
  603. answers[answer].sorry_value = sorry_val;
  604. answers[answer].sorry_colored = true;
  605. }
  606. }
  607. }
  608. } break;
  609. case 'checkbox': {
  610. for (answer in answers) {
  611. if (correct != undefined) {
  612. if (correct.includes(answers[answer].sorry_value)) {
  613. if (!answers[answer].sorry_colored && (version !== undefined)) {
  614. correct_element = answers[answer].parentNode;
  615. sorry_val = answers[answer].sorry_value;
  616. correct_element.innerHTML = "<span style='color: green;'>" + correct_element.innerHTML + "</span>";
  617. answers[answer] = correct_element.getElementsByTagName('input')[0];
  618. answers[answer].sorry_value = sorry_val;
  619. answers[answer].sorry_colored = true;
  620. }
  621. }
  622. }
  623. }
  624. } break;
  625. }
  626. }
  627.  
  628. function auto_answer() {
  629. var answer, chosen_answer, sorry_val, correct_element, incorrect_element;
  630. const auto_answer = config.get('auto_answer');
  631. function pick_answer(picked) {
  632. switch (type) {
  633. case 'radio': {
  634. for (answer in answers) {
  635. if (answers[answer].sorry_value == picked) {
  636. answers[answer].click();
  637. }
  638. }
  639. } break;
  640. case 'checkbox': {
  641. for (answer in answers) {
  642. if (picked.includes(answers[answer].sorry_value)) {
  643. answers[answer].click();
  644. }
  645. }
  646. } break;
  647. case 'text': {
  648. var corr = picked.slice(1, picked.length - 1).split('][');
  649. for (i = 0; (i < answers.leght) && (i < corr.length); i++) {
  650. answers[i].placeholder = corr[i];
  651. }
  652. } break;
  653. }
  654. }
  655. var greed = GM_getValue('greed');
  656. if (greed === undefined) {
  657. greed = 99999;
  658. }
  659. if (correct != undefined) {
  660. pick_answer(correct);
  661. } else if (auto_answer == labels.auto_answer_random) {
  662. if ((incorrect.length >= Math.max(config.get('auto_answer_not_greedy'), 1)) &&
  663. ((config.get('auto_answer_not_greedy') > 0) || (greed <= 0))) {
  664. chosen_answer = Math.floor(Math.random() * incorrect.length);
  665. pick_answer(incorrect[chosen_answer]);
  666. } else {
  667. GM_setValue('greed', greed - 1);
  668. switch (type) {
  669. case 'radio': {
  670. var possible_answers = [];
  671. for (answer in answers) {
  672. if (incorrect.includes(answers[answer].sorry_value) == false) {
  673. possible_answers.push(answer);
  674. }
  675. }
  676. chosen_answer = Math.floor(Math.random() * possible_answers.length);
  677. answers[possible_answers[chosen_answer]].click();
  678. } break;
  679. case 'checkbox': {
  680. var combs = comb(charset.slice(0, answers.length), incorrect);
  681. var pick = combs[Math.floor(Math.random() * combs.length)];
  682. for (i = 0; i < answers.length; i++) {
  683. if(pick.includes(answers[i].sorry_value)) {
  684. answers[i].click();
  685. }
  686. }
  687. } break;
  688. }
  689. }
  690. } else if (auto_answer == labels.auto_answer_first) {
  691. Object.values(sorted_objects_value)[0].click();
  692. }
  693. }
  694.  
  695. function parse_server_data(server_data) {
  696. version = server_data.version;
  697. if ((version !== VERSION) && (version !== undefined)) {
  698. console.warn("Sorryops is outdated");
  699. server_data = {};
  700. GM_openInTab("https://greasyfork.org/en/scripts/481036-sorryops");
  701. config.set('auto_continue', false);
  702. }
  703. if (server_data.hasOwnProperty('correct')) {
  704. correct = server_data.correct;
  705. if (correct === undefined) {
  706. correct = undefined;
  707. } else if (correct.hasOwnProperty(hash)) {
  708. correct = correct[hash];
  709. } else {
  710. correct = undefined;
  711. }
  712. } else {
  713. correct = undefined;
  714. }
  715. if (server_data.hasOwnProperty('incorrect')) {
  716. incorrect = server_data.incorrect;
  717. if (incorrect === undefined) {
  718. incorrect = [];
  719. } else if (incorrect.hasOwnProperty(hash)) {
  720. incorrect = incorrect[hash];
  721. } else {
  722. incorrect = [];
  723. }
  724. } else {
  725. incorrect = [];
  726. }
  727. }
  728.  
  729. function auto_continue() {
  730. var old_time, cur_time;
  731. if (config.get('auto_continue')) {
  732. old_time = config.get('auto_continue_time');
  733. cur_time = Date.now();
  734. if (cur_time - old_time > 60 * 60 * 1000) {
  735. config.set('auto_continue', false);
  736. } else {
  737. press_continue_btn();
  738. }
  739. }
  740. }
  741.  
  742. function auto_restart() {
  743. var old_time, cur_time;
  744. if (config.get('auto_restart')) {
  745. old_time = config.get('auto_restart_time');
  746. cur_time = Date.now();
  747. if (cur_time - old_time > 60 * 60 * 1000) {
  748. config.set('auto_restart', false);
  749. } else {
  750. press_continue_btn();
  751. }
  752. }
  753. }
  754.  
  755. /* End Functions */
  756.  
  757. /* Handlers */
  758.  
  759. function test_form_handler(server_response) {
  760. var i, key, answer, sorry_val;
  761. var complicated_hash_f = false;
  762. var boxes = [];
  763. var sorted_objects;
  764. var objects_hash = new Object();
  765. var objects_value = new Object();
  766. var form = document.getElementById('testform-answer');
  767. var manual_form = document.getElementById('testform-answer-0');
  768. if (form != null) {
  769. boxes = form.getElementsByTagName('input');
  770. } else if (manual_form != null) {
  771. i = 1;
  772. while (manual_form != null) {
  773. boxes.push(manual_form);
  774. manual_form = document.getElementById('testform-answer-' + i++);
  775. }
  776. }
  777. type = boxes[0].type;
  778. for (i = 0; i < boxes.length; i++) {
  779. if (boxes[i].parentNode.innerHTML.includes("<img")) {
  780. complicated_hash_f = true;
  781. break;
  782. }
  783. }
  784. switch (type) {
  785. case 'checkbox':
  786. case 'radio': {
  787. for (i = 0; i < boxes.length; i++) {
  788. boxes[i].hash = hashCode(complicated_hash_f ? boxes[i].parentNode.innerHTML : boxes[i].parentNode.innerText, true);
  789. objects_hash[boxes[i].hash] = boxes[i];
  790. objects_value[boxes[i].value] = boxes[i];
  791. }
  792. const sorted_objects_hash = Object.keys(objects_hash).sort().reduce(
  793. (obj, key) => {
  794. obj[key] = objects_hash[key];
  795. return obj;
  796. }, {}
  797. );
  798. sorted_objects_value = Object.keys(objects_value).sort().reduce(
  799. (obj, key) => {
  800. obj[key] = objects_value[key];
  801. return obj;
  802. }, {}
  803. );
  804. i = 0;
  805. sorted_objects = sorted_objects_hash;
  806. for (key in sorted_objects) {
  807. sorted_objects[key].parentNode.remove();
  808. form.appendChild(sorted_objects[key].parentNode);
  809. }
  810. calculate_variant_hash();
  811. parse_server_data(server_response);
  812. for (key in sorted_objects) {
  813. sorted_objects[key].sorry_value = charset[i++];
  814. var span = document.createElement('span');
  815. var disp_val;
  816. switch (config.get('display_values')) {
  817. case labels.display_values_ori:
  818. disp_val = sorted_objects[key].value;
  819. break;
  820. case labels.display_values_sorry:
  821. disp_val = sorted_objects[key].sorry_value;
  822. break;
  823. case labels.display_values_both:
  824. disp_val = sorted_objects[key].value + ":" + sorted_objects[key].sorry_value;
  825. break;
  826. }
  827. span.innerHTML = disp_val + ") ";
  828. sorted_objects[key].parentNode.insertBefore(span, sorted_objects[key]);
  829. answers.push(sorted_objects[key]);
  830. }
  831. if (config.get('display_values') == labels.display_values_ori) {
  832. for (key in sorted_objects_value) {
  833. sorted_objects_value[key].parentNode.remove();
  834. form.appendChild(sorted_objects_value[key].parentNode);
  835. }
  836. }
  837. } break;
  838. case 'text': {
  839. answers = boxes;
  840. calculate_variant_hash();
  841. } break;
  842. }
  843. color_answers();
  844. auto_answer();
  845. update_variant();
  846. for (i = 0; i < answers.length; i++) {
  847. answers[i].addEventListener('change', update_variant);
  848. }
  849. }
  850.  
  851. function result_page_handler() {
  852. var i;
  853. var correct_num = variant.slice(variant.indexOf("Число верных ответов: ") + 22);
  854. var all_num = variant.slice(variant.indexOf("Число неверных ответов: ") + 24);
  855. correct_num = correct_num.slice(0, correct_num.indexOf("\n")).trim();
  856. all_num = all_num.slice(0, all_num.indexOf("\n")).trim();
  857. all_num = (parseInt(all_num) + parseInt(correct_num)).toString();
  858. var test = GM_getValue('tests', new Object())[testID];
  859. if (test === undefined) {
  860. return;
  861. }
  862. var printer = "";
  863. var sorted_test = [];
  864. for (var hash in test) {
  865. sorted_test.push([hash].concat(test[hash]));
  866. }
  867. sorted_test.sort((a, b) => {return a[1] - b[1]});
  868. for (i = 0; i < sorted_test.length; i++) {
  869. printer += (config.get('append_question_number') ? (sorted_test[i][1] + ") ") : "") + sorted_test[i][0] + " " + sorted_test[i][2] + "\n";
  870. }
  871. printer += correct_num;
  872. if (config.get('copy_answers')) {
  873. GM_setClipboard(printer);
  874. }
  875. if (config.get('accumulator_enable')) {
  876. var acc = GM_getValue('accumulated_answers', "");
  877. if (acc != "") {
  878. acc += "\n\n";
  879. }
  880. var prefix = testID;
  881. if (prefix != "") {
  882. acc += prefix + "\n";
  883. }
  884. acc += printer;
  885. GM_setValue('accumulated_answers', acc);
  886. printer = acc;
  887. }
  888. 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>";
  889. var pboxes = document.getElementsByTagName('p');
  890. for (i = 0; i < pboxes.length; i++) {
  891. var pbox = pboxes[i];
  892. if (pbox.textContent.includes("Попытка ")) {
  893. pbox.outerHTML += printer;
  894. break;
  895. }
  896. }
  897. set_to_clear(testID, () => {
  898. if (GM_getValue('new_answer_f')) {
  899. send_to_server({
  900. type: "test_results",
  901. uid: config.get('user_id'),
  902. id: testID,
  903. answers: sorted_test,
  904. correct: correct_num,
  905. all: all_num,
  906. });
  907. }
  908. GM_setValue('fetched_data', {});
  909. GM_setValue('new_answer_f', false);
  910. var greed = config.get('auto_answer_greed_level');
  911. if (greed < 0) {
  912. greed = 99999;
  913. }
  914. GM_setValue('greed', greed);
  915. });
  916. }
  917.  
  918. /* End Handlers */
  919.  
  920. function main() {
  921. variant = document.getElementById('w0').parentNode.textContent;
  922. prev_new_answer_f = !!GM_getValue('new_answer_f');
  923. if (variant.includes("Вопрос:")) {
  924. fetch_from_server(testID, (server_response) => {
  925. DB_cleaner();
  926. test_form_handler(server_response);
  927. if (config.get('wait_server_response') && (version === undefined)) {
  928. window.setInterval(() => {fetch_from_server(testID, (server_response) => {
  929. if (version === undefined) {
  930. parse_server_data(server_response);
  931. color_answers();
  932. if (config.get('auto_continue')) {
  933. auto_answer();
  934. }
  935. update_variant();
  936. if (version === VERSION) {
  937. auto_continue();
  938. }
  939. }
  940. })}, 1000, true);
  941. } else {
  942. auto_continue();
  943. }
  944. });
  945. } else if (variant.includes("Результат прохождения теста:")) {
  946. result_page_handler();
  947. auto_restart();
  948. }
  949. }