您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Collect and reuse ORIOKS test answers
当前为
- // ==UserScript==
- // @name Sorryops
- // @name:ru Сориупс
- // @namespace https://git.disroot.org/electromagneticcyclone/sorryops
- // @version 20240429.1
- // @description Collect and reuse ORIOKS test answers
- // @description:ru Скрипт для сбора и переиспользования ответов на тесты ОРИОКС
- // @icon https://orioks.miet.ru/favicon.ico
- // @author electromagneticcyclone & angelbeautifull
- // @license GPL-3.0-or-later
- // @supportURL https://git.disroot.org/electromagneticcyclone/sorryops
- // @match https://orioks.miet.ru/student/student/test*
- // @grant GM_getValue
- // @grant GM_setValue
- // @grant GM_addStyle
- // @grant GM_registerMenuCommand
- // @grant GM_setClipboard
- // @grant GM_xmlhttpRequest
- // @require https://openuserjs.org/src/libs/sizzle/GM_config.js
- // @connect sorryops.ru
- // @run-at document-start
- // ==/UserScript==
- /* Charset */
- const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
- /* End Charset */
- /* Labels */
- const all_labels = {
- en: {
- l: "English",
- settings_title: "Settings",
- script_language: "Language",
- show_user_id: "Show user ID",
- user_id: "User ID (keep private)",
- server: "Sync answers with server (leave blank to disable)",
- auto_answer: "Auto answer",
- auto_answer_no: "No",
- auto_answer_first: "First",
- auto_answer_random: "Random",
- display_values: "Answers variant",
- display_values_ori: "ORIOKS",
- display_values_sorry: "Sorry",
- display_values_both: "Both",
- display_answer: "Display answer near variant",
- stop_timer: "Freeze and hide timer",
- register_keyboard_keys: "Register hotkeys",
- copy_answers: "Copy results to the clipboard",
- append_question_number: "Show question numbers in the final report",
- accumulator_enable: "Accumulate test results in one field",
- auto_continue: "Auto continue (DANGEROUS!!! Will be disabled after an hour. Press `d` to disable)",
- auto_restart: "Auto restart (DANGEROUS!!! Will be disabled after an hour. Press `d` to disable. Make sure you have infinite attempts)",
- },
- ru: {
- l: "Русский",
- settings_title: "Настройки",
- script_language: "Язык",
- show_user_id: "Показать индетификатор пользователя",
- user_id: "Индетификатор (держать в секрете)",
- server: "Синхронизировать ответы с сервером (оставить пустым для отключения)",
- auto_answer: "Автовыбор ответа",
- auto_answer_no: "Нет",
- auto_answer_first: "Первый",
- auto_answer_random: "Случайный",
- display_values: "Вариант отображения ответов",
- display_values_ori: "ОРИОКС",
- display_values_sorry: "Сори",
- display_values_both: "Оба",
- display_answer: "Отображать ответ рядом с вариантом",
- stop_timer: "Заморозить и скрыть таймер",
- register_keyboard_keys: "Горячие клавиши",
- copy_answers: "Копировать результаты в буфер обмена",
- append_question_number: "Отображать номер вопроса в финальном отчёте",
- accumulator_enable: "Собирать отчёты в одно поле",
- auto_continue: "Автопродолжение (ОПАСНО!!! Отключается через час. Нажмите `d`, чтобы остановить)",
- auto_restart: "Автоперезапуск (ОПАСНО!!! Отключается через час. Нажмите `d`, чтобы остановить. Убедитесь, что количество попыток неограничено)",
- },
- };
- var labels = all_labels[(() => {
- var lang = GM_getValue('language', "-");
- if (!lang || (lang == "-")) {
- lang = navigator.language || navigator.userLanguage;
- }
- for (var l in all_labels) {
- if (lang.includes(l)) {
- return l;
- }
- }
- })()];
- if (labels == undefined) {
- labels = all_labels.ru;
- }
- /* End Labels */
- /* Config */
- var config = new GM_config({
- id: 'config',
- title: labels.settings_title,
- fields: {
- script_language: {
- label: labels.script_language,
- type: 'select',
- options: [
- '-',
- all_labels.en.l,
- all_labels.ru.l,
- ],
- default: '-',
- },
- show_user_id: {
- label: labels.show_user_id,
- type: 'checkbox',
- default: false,
- },
- user_id: {
- label: labels.user_id + (GM_getValue("show_user_id", false) ? "" : "<input readonly value='******'>"),
- type: GM_getValue("show_user_id", false) ? 'text' : 'hidden',
- save: false,
- default: '',
- },
- server: {
- label: labels.server,
- type: 'text',
- default: '',
- },
- valid_user_id: {
- type: 'hidden',
- default: '',
- },
- auto_answer: {
- label: labels.auto_answer,
- type: 'select',
- options: [
- labels.auto_answer_no,
- labels.auto_answer_first,
- labels.auto_answer_random,
- ],
- default: labels.auto_answer_no,
- },
- display_values: {
- label: labels.display_values,
- type: 'select',
- options: [
- labels.display_values_ori,
- labels.display_values_sorry,
- labels.display_values_both,
- ],
- default: labels.display_values_ori,
- },
- display_answer: {
- label: labels.display_answer,
- type: 'checkbox',
- default: true,
- },
- stop_timer: {
- label: labels.stop_timer,
- type: 'checkbox',
- default: false,
- },
- register_keyboard_keys: {
- label: labels.register_keyboard_keys,
- type: 'checkbox',
- default: true,
- },
- copy_answers: {
- label: labels.copy_answers,
- type: 'checkbox',
- default: false,
- },
- append_question_number: {
- label: labels.append_question_number,
- type: 'checkbox',
- default: true,
- },
- accumulator_enable: {
- label: labels.accumulator_enable,
- type: 'checkbox',
- default: false,
- },
- auto_continue: {
- label: labels.auto_continue,
- type: 'checkbox',
- default: false,
- },
- auto_continue_time: {
- type: 'hidden',
- default: 0,
- },
- auto_restart: {
- label: labels.auto_restart,
- type: 'checkbox',
- default: false,
- },
- auto_restart_time: {
- type: 'hidden',
- default: 0,
- },
- },
- events: {
- init: function() {
- var valid_user_id = this.get('valid_user_id');
- if (!validate_user_id(valid_user_id)) {
- valid_user_id = generate_user_id();
- }
- this.set('user_id', valid_user_id);
- this.set('valid_user_id', valid_user_id);
- GM_setValue('show_user_id', this.get('show_user_id'));
- GM_setValue('stop_timer', this.get('stop_timer'));
- if (this.get('auto_continue') && (this.get('auto_answer') == "No")) {
- this.set('auto_continue', false);
- }
- if (this.get('accumulator_enable') == false) {
- GM_setValue('accumulated_answers', "");
- }
- switch (this.get('script_language')) {
- case all_labels.en.l:
- GM_setValue('language', "en");
- break;
- case all_labels.ru.l:
- GM_setValue('language', "ru");
- break;
- default:
- GM_setValue('language', "-");
- break;
- }
- },
- save: function(forgotten) {
- this.set('auto_continue_time', Date.now());
- this.set('auto_restart_time', Date.now());
- if (this.isOpen && this.get('auto_continue') && (this.get('auto_answer') == "No")) {
- this.set('auto_continue', false);
- alert("Can't automatically continue without answer.");
- }
- if (!validate_user_id(forgotten['user_id'])) {
- this.set('user_id', this.get('valid_user_id'))
- alert('User ID is invalid!');
- } else {
- this.set('valid_user_id', forgotten['user_id'])
- }
- this.init();
- },
- },
- });
- GM_registerMenuCommand(labels.settings_title, () => {
- config.open();
- });
- /* End Config */
- /* Server */
- function send_to_server(results) {
- var server = config.get('server');
- if (server != '') {
- GM_xmlhttpRequest({
- method: 'POST',
- url: 'https://' + server,
- headers: {
- 'Content-Type': 'application/json',
- },
- data: JSON.stringify(results),
- });
- }
- }
- function fetch_from_server(path, func) {
- var server = config.get('server');
- if (server != '') {
- GM_xmlhttpRequest({
- method: 'GET',
- url: 'https://' + server + '/' + path,
- onload: function (response) {
- var text = response.responseText;
- if (!text.includes("{")) {
- func({});
- } else {
- func(JSON.parse(text));
- }
- },
- onerror: function (e) {
- func({});
- },
- onabort: function (e) {
- func({});
- },
- ontimeout: function (e) {
- func({});
- }
- });
- } else {
- func({});
- }
- }
- /* End Server */
- /* Stop timer */
- if (GM_getValue('stop_timer', true)) {
- var i, pbox;
- var pboxes = document.getElementsByTagName('p');
- for (i = 0; i < pboxes.length; i++) {
- pbox = pboxes[i];
- if (pbox.textContent.includes("Осталось:")) {
- pbox.parentNode.remove();
- document.getElementsByTagName('hr')[0].remove();
- var injectFakeTimer = function(window) {
- window.setInterval = (f, t) => {
- return window.setInterval(f, 10^999);
- };
- }
- var scriptFakeTimer = document.createElement('script');
- scriptFakeTimer.setAttribute("type", "application/javascript");
- scriptFakeTimer.textContent = '(' + injectFakeTimer + ')(window);';
- document.body.appendChild(scriptFakeTimer);
- break;
- }
- }
- }
- /* End Stop timer */
- /* Events */
- window.addEventListener('load', main);
- window.onkeydown = (e) => {
- if ((e.key == "Enter") && config.get('register_keyboard_keys')) {
- press_continue_btn();
- }
- if (e.key == "d") {
- config.set('auto_continue', false);
- config.set('auto_restart', false);
- config.save();
- }
- };
- /* End Events */
- /* Page properties */
- // const success = -1487162948;
- var answers = [];
- var variant, hash, type;
- var testID = (() => {
- var url = document.URL;
- url = url.slice(url.indexOf("idKM=") + 5);
- url = url.slice(0, url.indexOf("&"));
- return url;
- })();
- /* End properties */
- /* Functions */
- // https://github.com/ajayyy/maze-utils/blob/036086403f675b8fea0e22065f26ba534e351562/src/setup.ts
- function generate_user_id(length = 36) {
- var i;
- var result = "";
- const cryptoFuncs = typeof window === "undefined" ? crypto : window.crypto;
- if (cryptoFuncs && cryptoFuncs.getRandomValues) {
- const values = new Uint32Array(length);
- cryptoFuncs.getRandomValues(values);
- for (i = 0; i < length; i++) {
- result += charset[values[i] % charset.length];
- }
- } else {
- for (i = 0; i < length; i++) {
- result += charset[Math.floor(Math.random() * charset.length)];
- }
- }
- return result;
- }
- function validate_user_id(uid, length = 36) {
- var i;
- if (uid.length != length) {
- return false;
- }
- for (i = 0; i < length; i++) {
- if (!charset.includes(uid[i])) {
- return false;
- }
- }
- return true;
- }
- // https://stackoverflow.com/a/15710692
- function hashCode(s, return_num = false) {
- var result = "";
- var h = s.split("").reduce(function(a, b) {
- a = ((a << 5) - a) + b.charCodeAt(0);
- return a & a;
- }, 0);
- if (return_num) {
- return h;
- }
- while (h != 0) {
- result += charset[((h % charset.length) + charset.length) % charset.length];
- h = Math.floor(Math.abs(h) / charset.length) * (h / Math.abs(h));
- }
- return result;
- }
- function set_to_clear(id, exec_if_not_cleared) {
- var clear = GM_getValue('clear_tests', new Object());
- if (!clear[id]) {
- exec_if_not_cleared();
- }
- clear[id] = true;
- GM_setValue('clear_tests', clear);
- }
- function DB_cleaner() {
- var clear = GM_getValue('clear_tests', new Object());
- var tests = GM_getValue('tests', new Object());
- for (var test in clear) {
- delete tests[test];
- }
- GM_setValue('tests', tests);
- GM_setValue('clear_tests', new Object());
- }
- function press_continue_btn() {
- var i;
- var buttons = document.getElementsByTagName('button');
- var button = undefined;
- for (i = 0; i < buttons.length; i++) {
- var btn = buttons[i];
- if (btn.textContent.includes("Пройти") || btn.textContent.includes("Продолжить")) {
- button = btn;
- break;
- }
- }
- if (button === undefined) {
- return;
- }
- if (button.textContent.includes("Пройти")) {
- window.location.replace(button.parentNode.href);
- } else if (button.textContent.includes("Продолжить")) {
- button.click();
- }
- }
- function calculate_variant_hash() {
- variant = document.getElementById('w0').parentNode.textContent;
- variant = variant.slice(variant.indexOf("Вопрос:"));
- hash = hashCode(variant);
- }
- function update_variant() {
- var i, pbox;
- var chosen_answer = "";
- switch (type) {
- case 'checkbox':
- case 'radio': {
- for (i = 0; i < answers.length; i++) {
- chosen_answer += answers[i].checked ? answers[i].sorry_value : "";
- }
- chosen_answer = chosen_answer.split('').sort().join('');
- if (type == 'checkbox') {
- chosen_answer = "{" + chosen_answer + "}";
- }
- } break;
- case 'text': {
- for (i = 0; i < answers.length; i++) {
- chosen_answer += "[" + answers[i].value + "]";
- }
- }
- }
- var pboxes = document.getElementsByTagName('p');
- const display_answer = config.get('display_answer');
- for (i = 0; i < pboxes.length; i++) {
- pbox = pboxes[i];
- if (pbox.textContent.includes("Вопрос:")) {
- pbox.innerHTML = "<i>(Вариант <input onfocus='this.select();' id='variant' value='" + hash + (display_answer == true ? (" " + chosen_answer) : "") + "' readonly>)</i><br>Вопрос:";
- break;
- }
- }
- var question_num = undefined;
- for (i = 0; i < pboxes.length; i++) {
- pbox = pboxes[i];
- if (pbox.textContent.includes("Текущий вопрос: ")) {
- question_num = pbox.textContent.slice(variant.indexOf("Текущий вопрос: ") + 16).trim();
- break;
- }
- }
- if (hash !== undefined) {
- var tests = GM_getValue('tests', new Object());
- if (tests[testID] === undefined) {
- tests[testID] = new Object();
- }
- tests[testID][hash] = [question_num, chosen_answer, answers.length];
- GM_setValue('tests', tests);
- }
- }
- /* End Functions */
- /* Handlers */
- function test_form_handler(server_data) {
- var i, key, correct, incorrect, answer, sorry_val;
- var boxes = [];
- var sorted_objects;
- var objects_hash = new Object();
- var objects_value = new Object();
- var form = document.getElementById('testform-answer');
- var manual_form = document.getElementById('testform-answer-0');
- if (form != null) {
- boxes = form.getElementsByTagName('input');
- } else if (manual_form != null) {
- i = 1;
- while (manual_form != null) {
- boxes.push(manual_form);
- manual_form = document.getElementById('testform-answer-' + i++);
- }
- }
- type = boxes[0].type;
- switch (type) {
- case 'checkbox':
- case 'radio': {
- for (i = 0; i < boxes.length; i++) {
- boxes[i].hash = hashCode(boxes[i].parentNode.innerText, true);
- objects_hash[boxes[i].hash] = boxes[i];
- objects_value[boxes[i].value] = boxes[i];
- }
- const sorted_objects_hash = Object.keys(objects_hash).sort().reduce(
- (obj, key) => {
- obj[key] = objects_hash[key];
- return obj;
- }, {}
- );
- const sorted_objects_value = Object.keys(objects_value).sort().reduce(
- (obj, key) => {
- obj[key] = objects_value[key];
- return obj;
- }, {}
- );
- i = 0;
- sorted_objects = sorted_objects_hash;
- for (key in sorted_objects) {
- sorted_objects[key].parentNode.remove();
- form.appendChild(sorted_objects[key].parentNode);
- }
- calculate_variant_hash();
- if (server_data.hasOwnProperty('correct')) {
- correct = server_data.correct;
- if (correct === undefined) {
- correct = undefined;
- } else if (correct.hasOwnProperty(hash)) {
- correct = correct[hash];
- } else {
- correct = undefined;
- }
- } else {
- correct = undefined;
- }
- if (server_data.hasOwnProperty('incorrect')) {
- incorrect = server_data.incorrect;
- if (incorrect === undefined) {
- incorrect = [];
- } else if (incorrect.hasOwnProperty(hash)) {
- incorrect = incorrect[hash];
- } else {
- incorrect = [];
- }
- } else {
- incorrect = [];
- }
- for (key in sorted_objects) {
- sorted_objects[key].sorry_value = charset[i++];
- var span = document.createElement('span');
- var disp_val;
- switch (config.get('display_values')) {
- case labels.display_values_ori:
- disp_val = sorted_objects[key].value;
- break;
- case labels.display_values_sorry:
- disp_val = sorted_objects[key].sorry_value;
- break;
- case labels.display_values_both:
- disp_val = sorted_objects[key].value + ":" + sorted_objects[key].sorry_value;
- break;
- }
- span.innerHTML = disp_val + ") ";
- sorted_objects[key].parentNode.insertBefore(span, sorted_objects[key]);
- answers.push(sorted_objects[key]);
- }
- if (config.get('display_values') == labels.display_values_ori) {
- for (key in sorted_objects_value) {
- sorted_objects_value[key].parentNode.remove();
- form.appendChild(sorted_objects_value[key].parentNode);
- }
- }
- const auto_answer = config.get('auto_answer');
- if (correct != undefined) {
- for (answer in answers) {
- if (answers[answer].sorry_value == correct) {
- var correct_element = answers[answer].parentNode;
- sorry_val = answers[answer].sorry_value;
- correct_element.innerHTML = "<div style='color: green;'>" + correct_element.innerHTML + "</div>";
- answers[answer] = correct_element.getElementsByTagName('input')[0];
- answers[answer].sorry_value = sorry_val;
- answers[answer].click();
- break;
- }
- }
- } else if (auto_answer == labels.auto_answer_random) {
- if (answers[0].type === 'radio') {
- var possible_answers = [];
- for (answer in answers) {
- if (incorrect.includes(answers[answer].sorry_value) == false) {
- possible_answers.push(answer);
- } else {
- var incorrect_element = answers[answer].parentNode;
- sorry_val = answers[answer].sorry_value;
- incorrect_element.innerHTML = "<div style='color: red;'>" + incorrect_element.innerHTML + "</div>";
- answers[answer] = incorrect_element.getElementsByTagName('input')[0];
- answers[answer].sorry_value = sorry_val;
- answers[answer] = incorrect_element.getElementsByTagName('input')[0];
- }
- }
- var chosen_answer;
- chosen_answer = Math.floor(Math.random() * possible_answers.length);
- answers[possible_answers[chosen_answer]].click();
- } else {
- var pick = Math.floor(Math.random() * (Math.pow(2, answers.length) - 1)) + 1;
- for (i = 0; i < answers.length; i++) {
- if(pick & Math.pow(2, i)) {
- answers[i].click();
- }
- }
- }
- } else if (auto_answer == labels.auto_answer_first) {
- Object.values(sorted_objects_value)[0].click();
- }
- } break;
- case 'text': {
- answers = boxes;
- calculate_variant_hash();
- if (server_data.hasOwnProperty('correct')) {
- correct = server_data.correct[hash];
- }
- if (server_data.hasOwnProperty('correct')) {
- incorrect = server_data.incorrect[hash];
- }
- } break;
- }
- update_variant();
- for (i = 0; i < answers.length; i++) {
- answers[i].addEventListener('change', update_variant);
- }
- }
- function result_page_handler() {
- var i;
- var correct = variant.slice(variant.indexOf("Число верных ответов: ") + 22);
- correct = correct.slice(0, correct.indexOf("\n")).trim();
- var test = GM_getValue('tests', new Object())[testID];
- if (test === undefined) {
- return;
- }
- var printer = "";
- var sorted_test = [];
- for (var hash in test) {
- sorted_test.push([hash].concat(test[hash]));
- }
- sorted_test.sort((a, b) => {return a[1] - b[1]});
- for (i = 0; i < sorted_test.length; i++) {
- printer += (config.get('append_question_number') ? (sorted_test[i][1] + ") ") : "") + sorted_test[i][0] + " " + sorted_test[i][2] + "\n";
- }
- printer += correct;
- if (config.get('copy_answers')) {
- GM_setClipboard(printer);
- }
- if (config.get('accumulator_enable')) {
- var acc = GM_getValue('accumulated_answers', "");
- if (acc != "") {
- acc += "\n\n";
- }
- var prefix = testID;
- if (prefix != "") {
- acc += prefix + "\n";
- }
- acc += printer;
- GM_setValue('accumulated_answers', acc);
- printer = acc;
- }
- 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>";
- var pboxes = document.getElementsByTagName('p');
- for (i = 0; i < pboxes.length; i++) {
- var pbox = pboxes[i];
- if (pbox.textContent.includes("Попытка ")) {
- pbox.outerHTML += printer;
- break;
- }
- }
- set_to_clear(testID, () => {
- send_to_server({
- type: "test_results",
- uid: config.get('user_id'),
- id: testID,
- answers: sorted_test,
- correct: correct,
- });
- });
- }
- /* End Handlers */
- function main() {
- var old_time, cur_time;
- variant = document.getElementById('w0').parentNode.textContent;
- if (variant.includes("Вопрос:")) {
- fetch_from_server(testID, (server_response) => {
- DB_cleaner();
- test_form_handler(server_response);
- if (config.get('auto_continue')) {
- old_time = config.get('auto_continue_time');
- cur_time = Date.now();
- if (cur_time - old_time > 60 * 60 * 1000) {
- config.set('auto_continue', false);
- } else {
- press_continue_btn();
- }
- }
- });
- } else if (variant.includes("Результат прохождения теста:")) {
- result_page_handler();
- if (config.get('auto_restart')) {
- old_time = config.get('auto_restart_time');
- cur_time = Date.now();
- if (cur_time - old_time > 60 * 60 * 1000) {
- config.set('auto_restart', false);
- } else {
- press_continue_btn();
- }
- }
- }
- }