// ==UserScript== // @name 甘肃专业技术教育学习助手 // @namespace https://gsadjy.sccchina.net/ // @version 1.0 // @description 甘肃专业技术教育(安德职业培训):勾选课程自助学习;免费体验 1 门课,专业版激活后 30 天有效,免费考试,1:1常速模式和效率模式自由选择。 // @author 柠檬真酸 // @match https://gsadjy.sccchina.net/* // @match https://*.sccchina.net/* // @match https://*.sccchina.net/mizar/* // @grant GM_xmlhttpRequest // @grant GM_info // @grant GM_openInTab // @connect gsadjy.sccchina.net // @connect *.sccchina.net // @connect oa40.ahzsksw.cn // @connect *.ahzsksw.cn // @connect mizarfile.chinaedu.net // @connect huaweicloudobs.ahjxjy.cn // @run-at document-idle // ==/UserScript== (function () { "use strict"; if (!/\/mizar\//i.test(location.pathname) && !/gsadjy\.sccchina\.net$/i.test(location.host)) { return; } const SCRIPT_VERSION = (() => { try { if (typeof GM_info !== "undefined" && GM_info?.script?.version) { return String(GM_info.script.version); } } catch (_) {} return "1.0"; })(); const SCRIPT_NAME = "\u7518\u8083\u4e13\u4e1a\u6280\u672f\u6559\u80b2\u5b66\u4e60\u52a9\u624b"; function secToMin(sec) { return Math.max(0, Math.ceil((Number(sec) || 0) / 60)); } function progressMinText(pos, total) { return `${secToMin(pos)}/${secToMin(total)} \u5206\u949f`; } const ORIGIN = location.origin; const BASE = "/mizar"; const SIGN_SALT = "52c203760cf28798a44f6ac4"; const PANEL_LOGO_URL = "https://huaweicloudobs.ahjxjy.cn/895789f9086469785b846d30c0ed95f9.png"; const DEFAULT_CLOUD_API_BASE = "https://oa40.ahzsksw.cn"; const CLOUD_TOKEN_KEY = "gsadjy_cloud_token_v1"; const CLOUD_LEASE_CACHE_KEY = "gsadjy_cloud_lease_cache_v1"; const CLOUD_CLIENT_CFG_KEY = "gsadjy_client_cfg_v1"; const LEARNING_UID_KEY = "gsadjy_learning_uid_v1"; const PRO_BUY_URL = "https://card.wlxy.live/details/7621DC2D"; const KEY_QUEUE = "gsadjy_queue_v1"; const KEY_DONE = "gsadjy_done_v1"; const KEY_MODE = "gsadjy_mode_v1"; const KEY_POS = "gsadjy_panel_pos_v1"; const KEY_COLLAPSED = "gsadjy_panel_collapsed_v1"; const MODE_EFF = "efficiency"; const MODE_RT = "realtime"; let EFF_PARTS = 3; let EFF_WAIT_MS = 1500; let RT_STEP = 60; let RT_WAIT_MS = 60000; let LOCK_EVERY_MS = 20000; let FREE_COURSE_LIMIT_DEFAULT = 1; const EXAM_ANSWER_BY_QID = { "d64bee48-59ab-4fbb-8010-8b87168886d6": "1", "c568dfd3-1c5c-40ef-a6be-c4e5a03392c1": "4", "2a7547e3-e478-4c7e-bfb7-6b601994be99": "3", "6dcd4a78-d149-4a36-8433-fcc21e74ba4f": "2", "cf46fc99-a1a3-48ba-9ed0-05ce48e50a26": "4", }; const EXAM_ANSWER_BANK = [ { match: "\u4e2d\u56fd\u5171\u4ea7\u515a\u6700\u5927\u7684\u4f18\u52bf", answerText: "\u5bc6\u5207\u8054\u7cfb\u7fa4\u4f17", fallbackValue: "1" }, { match: "\u4f5c\u98ce\u5efa\u8bbe\u7684\u5386\u7a0b\u5206\u4e3a", answerText: "\u4e09", fallbackValue: "4" }, { match: "\u5341\u9879\u89c4\u5b9a", answerText: "\u56db\u5ddd\u7701\u59d4\u7701\u653f\u5e9c", fallbackValue: "3" }, { match: "\u516b\u9879\u89c4\u5b9a", answerText: "2012", fallbackValue: "2" }, { match: "\u515a\u7684\u5341\u4e5d\u5927\u548c\u4e8c\u5341\u5927\u540e", answerText: "\u4e24\u6b21", fallbackValue: "4" }, ]; const state = { busy: false, stopFlag: false, trains: [], courses: [], queue: loadJson(KEY_QUEUE, []), mode: localStorage.getItem(KEY_MODE) || MODE_EFF, logLines: [], currentCourse: "", currentTask: "\u70b9\u5f00\u59cb\u540e\u81ea\u52a8\u5b66\u4e60", userName: "", learningUserId: localStorage.getItem(LEARNING_UID_KEY) || "", examBusy: false, examActivities: [], cloudToken: localStorage.getItem(CLOUD_TOKEN_KEY) || "", cloudLease: "", cloudTier: "free", cloudProExpireAt: 0, freeCourseLimit: FREE_COURSE_LIMIT_DEFAULT, freeUsedCourses: 0, canExam: false, activationTtlDays: 30, proBuyUrl: PRO_BUY_URL, doneIds: new Set(loadJson("gsadjy_done_v1", [])), panelNotice: "", chapterCache: Object.create(null), }; function loadJson(k, def) { try { const v = JSON.parse(localStorage.getItem(k) || "null"); return v == null ? def : v; } catch (_) { return def; } } function saveJson(k, v) { localStorage.setItem(k, JSON.stringify(v)); } function sleep(ms) { return new Promise((r) => setTimeout(r, ms)); } function escapeHtml(s) { return String(s || "") .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """); } function rememberDone(id) { if (!id) return; state.doneIds.add(id); saveJson(KEY_DONE, [...state.doneIds]); } function log(msg, level) { const line = { text: `[${new Date().toLocaleTimeString()}] ${String(msg || "").trim()}`, level: level || "", }; state.logLines.unshift(line); if (state.logLines.length > 200) state.logLines.length = 200; const box = document.getElementById("gs-run-log"); if (box) { box.innerHTML = state.logLines .slice(0, 80) .map((t) => { const row = typeof t === "string" ? { text: t, level: "" } : t; const cls = row.level === "warn" ? " gs-log-warn" : ""; return `