// ==UserScript== // @name 【免费版】广东省教师公需课|广东省教师继续教育信息管理平台|如需自动下一集、自动换课程、秒过、考试答题等高级功能见收费版本:http://doc.zhanyc.cn/pages/gdjsgxk/ // @namespace https://doc.zhanyc.cn/ // @icon https://js.zhanyc.cn/img/js-logo.svg // @version 1.0 // @description 当前是免费版本,只包含了视频页面自动播放、解除播放暂停限制功能。如需自动下一集、自动换课程、秒过、考试答题、全自动无人值守高级功能可升级付费版本|接各类脚本开发、代挂工作,微信:zhanyc_cn 备用微信:zhanfengkuo 个人网站:http://doc.zhanyc.cn // @author zfk // @include *://*.gds.edu.cn/* // @grant GM_getValue // @grant GM_setValue // @grant GM_addStyle // @grant GM_deleteValue // @grant GM_setClipboard // @grant GM_registerMenuCommand // @grant GM_addValueChangeListener // @grant GM_removeValueChangeListener // @grant window.close // @run-at document-end // @require http://libs.baidu.com/jquery/2.0.0/jquery.min.js // ==/UserScript== (function () { let $jq = $; unsafeWindow.$jq = $; let docUrl = "http://doc.zhanyc.cn/pages/gdjsgxk/"; let ignoreAuth = true; let isCheckVersion = false; let isDev = false; let FREEJS = { setting: { version: { version: "1.0-free", body: `
免费版本
`, }, }, pageData: { waitOfIndexArr: [], userNameIndex: null, closeTipsIndex: null, confirmRunIndex: null, qaAnswer: { // 2026 人工智能赋能高质量发展 "c_50f37cb3b5674381928c6f47b868cbdc": ["A", "C", "D", "D", "B", "D", "B", "B", "D", "B", "ABC", "ABC", "AB", "ABC", "ABCD", "ABCD", "ABCD", "BCD", "BCD", "ABC", "A", "B", "A", "B", "B", "B", "A", "B", "B", "A"], // 2026 加快培育发展新质生产力 "c_92845a8663b34348a3583be03075963d": ["A", "D", "B", "B", "A", "D", "B", "B", "D", "B", "ABCD", "ABCDE", "ABC", "ACD", "ABC", "ABCD", "ABCD", "ABD", "ABCD", "ABC", "A", "A", "B", "B", "B", "B", "A", "A", "A", "B"], }, confirmRunZIndex: 19991018, waitTime: 0, video: { index: null, checkIndex: null }, }, // ====== 简易弹窗工具(替代layer.js) ====== _toastTimer: null, toast(msg, time = 3000) { let id = "FREEJS_toast_" + Date.now(); let $toast = $(`
${msg}
`).appendTo("body"); setTimeout(() => { $("#" + id).remove(); }, time); }, _dialogIndex: 0, dialog(options) { let idx = ++FREEJS._dialogIndex; let { title, content, btn, offset, area, time } = options; let w = area && area[0] ? area[0] : "400px"; let topOffset = offset || "100px"; let $overlay = $(`
`).appendTo("body"); let btnHtml = ""; if (btn && btn.length) { btnHtml = `
`; btn.forEach((text, i) => { let cls = i === 0 ? "#1890ff" : "#999"; btnHtml += ``; }); btnHtml += `
`; } let $box = $(`
${title || "脚本提示"} ×
${content || ""}
${btnHtml}
`).appendTo("body"); // 关闭 $overlay.add(`.FREEJS_dialog_close_${idx}`).off("click").on("click", function () { $("#FREEJS_dialog_${idx}").remove(); $box.remove(); }); // 按钮 $(`.FREEJS_dialog_btn_${idx}`).off("click").on("click", function () { let i = parseInt($(this).data("idx")); if (options.yes && i === 0) options.yes(idx); else if (options.btn2 && i === 1) options.btn2(idx); else if (options.btn3 && i === 2) options.btn3(idx); $("#FREEJS_dialog_${idx}").remove(); $box.remove(); }); if (time) { setTimeout(() => { $("#FREEJS_dialog_${idx}").remove(); $box.remove(); }, time); } return idx; }, closeDialog(id) { $("#FREEJS_dialog_" + id).remove(); $(`[id^="FREEJS_dialog_${id}"]`).remove(); }, alertMsg(msg, timeout = 0) { FREEJS.dialog({ title: "脚本提示" + (timeout ? `(${(timeout / 1000).toFixed(2)}秒后自动关闭)` : ""), content: `
${msg}
`, btn: ["关闭"], time: timeout }); }, tipsMsg(msg, timeout = 3000) { FREEJS.toast(msg, timeout); }, tipsMsg2(msg, timeout = 3000) { FREEJS.toast(msg, timeout); }, // ====== 引流悬浮面板 ====== showFreePanel() { if ($("#FREEJS_freePanel").length > 0) return; let $panel = $('
' + '
' + '⚡ 免费版本说明' + '×' + '
' + '
' + '
' + '
当前免费版本功能:
' + '
✅ 考试自动答题
' + '
✅ 视频中途自动答题
' + '
⚠️ 如需看自动看课功能,请升级付费版本
' + '
' + '
' + '
如需使用全功能版本:
' + '
自动下一集、自动换课程考试答题、全自动无人值守等高级功能
' + '
' + '👉 安装付费版本' + '
' + '
' + '
' + '接各类脚本开发、代挂
微信:zhanyc_cn' + '
' + '
' + '
').appendTo("body"); $("#FREEJS_closePanel").on("click", function () { $("#FREEJS_freePanel").remove(); }); }, // ====== 核心功能 ====== async init() { console.log("%c FREEJS init", "background:rgb(0,0,0);color:#fff"); FREEJS.addStyle(); FREEJS.showFreePanel(); // 处理登录跳转页面 if (location.href.indexOf("loginSuccess.html?yh=") != -1) { console.log("%c loginSuccess.html?yh", "background:rgb(255,0,0);color:#fff"); } // 运行页面逻辑 FREEJS.runByUrl(location.href); }, async runByUrl(url) { clearInterval(FREEJS.pageData.video.index); FREEJS.pageData.video.index = null; clearTimeout(FREEJS.pageData.video.checkIndex); FREEJS.pageData.video.checkIndex = null; url = url.toLowerCase(); if (top == window) { FREEJS.page_top(); } if (url.includes("/study/course/progess".toLowerCase())) { await FREEJS.setUserName(); FREEJS.page_videoProgesss(); } else if (url.includes("/study/course/".toLowerCase())) { await FREEJS.setUserName(); FREEJS.page_video(); } else if (url.includes("/course/".toLowerCase())) { FREEJS.page_courseDetail(); } }, page_top() { GM_addValueChangeListener('openLjts', function (name, old_value, new_value, remote) { FREEJS.openLjTips(); }); }, async page_videoProgesss() { console.log("%c page_videoProgesss", "background:rgb(0,0,0);color:#fff"); FREEJS.abortWaitOf(); await FREEJS.waitOf(a => $("#scoreTb tbody tr").length > 0); FREEJS.tipsMsg2("准备采集视频进度"); await FREEJS.waitTimeout(1000); let arr = []; $("#scoreTb tbody tr").each((i, el) => { let title = $(el).find('a:first').text().trim(); let isFinish = !$(el).find('td:last').text().includes('未完成'); arr.push({ title, isFinish }); }); let userData = await FREEJS.getUserData(); userData.video = arr; await FREEJS.setUserData(userData); await FREEJS.confirmRun(); FREEJS.getElByText($(".m-sxtstudy-nav a"), "课程学习")[0].click(); }, async page_video() { console.log("%c page_video", "background:rgb(0,0,0);color:#fff"); let timeout = 2; let lastTime = null; let checkTimeTimesBak = 60; let checkTimeTimes = checkTimeTimesBak; FREEJS.abortWaitOf(); FREEJS.closeWaitConfrimWin(); await FREEJS.waitOf(a => FREEJS.getElByText($("a"), "学习进度") != null); let userData = await FREEJS.getUserData(); if (userData.video.length == 0) { await FREEJS.confirmRun("准备采集视频进度"); FREEJS.getElByText($("a"), "学习进度")[0].click(); return; } if (FREEJS.pageData.video.index != null) { return; } let $el = null; let continueRun = true; $("a.section").each((i, el) => { let titile = $(el).text().trim(); let item = userData.video.find(a => a.title == titile); if (!item) { continueRun = false; return false; } if (!item.isFinish) { $el = $(el); return false; } }); if (!continueRun) { await FREEJS.confirmRun("准备采集视频进度"); FREEJS.getElByText($("a"), "学习进度")[0].click(); return; } if ($el == null) { FREEJS.alertMsg("学习完成"); return; } if (!$el.is(".z-crt")) { await FREEJS.confirmRun("准备切换到第一个未完成视频"); $el[0].click(); return; } FREEJS.pageData.video.index = setInterval(async () => { try { if (FREEJS.pageData.waitTime > 0) { FREEJS.pageData.waitTime -= timeout; return; } if ($(`[data-title="点击播放"]:visible`).length > 0) { FREEJS.pageData.waitTime = 10; let lastPlayTime = Number($("#viewTimeTxt").text()) * 60; lastPlayTime = Math.max(0, lastPlayTime - 30); $(`[data-title="点击播放"]:visible`).click(); FREEJS.tipsMsg2("准备切换到上一次学习的进度", 5000); await FREEJS.waitTimeout(3000); document.querySelector("video").currentTime = lastPlayTime; return; } await FREEJS.checkQA(); await FREEJS.checkExam(); if (!FREEJS.getVideo()) { console.log("%c FREEJS no video", "background:rgb(0,0,0);color:#fff"); return; } let curTime = FREEJS.getCurTime(); if (curTime == lastTime) { lastTime = FREEJS.getCurTime(); checkTimeTimes -= timeout; if (checkTimeTimes <= 0) { checkTimeTimes = checkTimeTimesBak; FREEJS.confirmRun("貌似卡死了,刷新页面重试").then((a) => { location.reload(); }); FREEJS.pageData.waitTime = 10; return; } } else { lastTime = curTime; checkTimeTimes = checkTimeTimesBak; } FREEJS.getVideo().volume = 0; let title = `进度:${FREEJS.getCurTime().toFixed(0)}/${FREEJS.getTotalTime().toFixed(0)}`; $("title").text(title); console.log("%c video run", "background:rgb(255,0,0);color:#fff"); let isFinish = await FREEJS.isPlayFinish(); let isRealFinish = false; if (!isFinish) { isRealFinish = $(".g-study-prompt").text().includes('您已完成观看'); } if (isFinish || isRealFinish) { FREEJS.pageData.waitTime = 15; if (isRealFinish) { FREEJS.pageData.waitTime = 3; } FREEJS.toast("视频即将结束,等待下一步操作", 10000); await FREEJS.setVideoFinish(); FREEJS.nextVideo(); clearInterval(FREEJS.pageData.video.index); FREEJS.pageData.video.index = null; return; } let isPlay = await FREEJS.videoIsPlay(); if (!isPlay) { // 自动播放逻辑,浏览器可能暂停了视频 try { FREEJS.getVideo().play(); } catch (e) { console.log("自动播放被阻止", e); } } } catch (e) { console.error("视频页面定时器出错", e); } }, timeout * 1000); }, nextVideo(waitTime = 10) { FREEJS.toast("视频即将结束,等待下一步操作", waitTime * 1000); setTimeout(() => { $(".btn.next").click(); }, waitTime * 1000); }, // ====== 考试答题 ====== async checkExam() { return new Promise(async (resolve, reject) => { if ($(".m-topic-item").length == 0) { return resolve(); } if ($("video:visible").length > 0) { return resolve(); } if (FREEJS.getElByText($(".u-main-btn"), "交卷") == null) { return resolve(); } FREEJS.pageData.waitTime = 9999; clearInterval(FREEJS.pageData.video.index); FREEJS.pageData.video.index = null; if ($(".m-ck-result").length > 0) { FREEJS.alertMsg("当前考试已经完成"); return; } let examId = location.href.replace(/.*\/study\/course\/([\d\w\_]*).*/, '$1'); if (!FREEJS.pageData.qaAnswer[examId]) { FREEJS.alertMsg("当前考试没有答案,请联系开发者确认(微信:zhanyc_cn)"); return; } let $elArr = []; $(".m-topic-item").each((i, el) => { $elArr.push($(el)); }); for (let i = 0; i < $elArr.length; i++) { const $el = $elArr[i]; let arr = FREEJS.pageData.qaAnswer[examId][i]; for (let j = 0; j < arr.length; j++) { const opt = arr[j]; FREEJS.tipsMsg2("正在答题"); $el.find('label').eq(FREEJS.getOptionIndex(opt)).click(); await FREEJS.waitTimeout(200); } } FREEJS.alertMsg("答题完毕"); return resolve(); }); }, getOptionIndex(option) { let arr = [ ["A", "B", "C", "D", "E", "F", "G", "H"], ["正确", "错误"], ["对", "错"], ]; let opt = option.toUpperCase(); let res = -1; arr.forEach((subArr) => { if (subArr.includes(opt)) { res = subArr.indexOf(opt); return false; } }); return res; }, getOptionLetter(index) { return ["A", "B", "C", "D", "E", "F", "G", "H"][index]; }, async checkQA() { return new Promise(async (resolve, reject) => { if (!$(".m-topic .title:visible").text().trim()) { return resolve(); } if (FREEJS.getElByText($(".u-main-btn"), "交卷") != null) { return resolve(); } FREEJS.pageData.waitTime = 10; function getAnswerList() { let code = unsafeWindow.finishTest.toString().split('var isRigh')[0].replace("function finishTest() {", "").trim(); let answerList = eval(code); return answerList; } let answerList = getAnswerList(); for (let i = 0; i < answerList.length; i++) { const answer = answerList[i]; $(`.m-topic label input[value="${answer}"]`).click(); await FREEJS.waitTimeout(100); $(`.m-topic label input[value="${answer}"]`).click(); await FREEJS.waitTimeout(100); $(`.m-topic label input[value="${answer}"]`).click(); await FREEJS.waitTimeout(100); } $("#questionDiv .u-main-btn").click(); return resolve(); }); }, // ====== 视频工具 ====== play() { FREEJS.getVideo().volume = 0; setTimeout(() => { FREEJS.getVideo().play(); }, 200); }, getVideo() { return $("video")[0]; }, setVideoVolume() { try { if (FREEJS.getVideo().volume != 0) { FREEJS.getVideo().volume = 0; } } catch (e) { console.error(e); } }, setStep(time) { if (FREEJS.getVideo()) { if (time >= 0) { FREEJS.getVideo().currentTime = time; } else { FREEJS.getVideo().currentTime = FREEJS.getVideo().duration + time; } } }, isPlayFinish() { try { return ( FREEJS.getTotalTime() > 0 && FREEJS.getCurTime() + 5 >= FREEJS.getTotalTime() ); } catch (e) { return false; } }, getCurTime() { let res = 0; try { res = FREEJS.getVideo().currentTime; } catch (e) { console.error(e); } return res; }, getTotalTime() { let res = 0; try { res = FREEJS.getVideo().duration; } catch (e) { console.error(e); } return res; }, async videoIsPlay() { return new Promise((resolve) => { try { let curTime = FREEJS.getCurTime(); setTimeout(() => { let time1 = FREEJS.getCurTime(); let res = time1 > curTime; if (res) { setTimeout(() => { let time2 = FREEJS.getCurTime(); let res2 = time2 > time1; resolve(res2); }, 100); } else { return resolve(false); } }, 100); } catch (e) { resolve(false); } }); }, async setVideoFinish() { let title = $("a.z-crt.section").text().trim(); let userData = await FREEJS.getUserData(); let item = userData.video.find(a => a.title == title); if (item) item.isFinish = true; await FREEJS.setUserData(userData); }, // ====== 用户数据 ====== async getUserData() { return new Promise(async (resolve, reject) => { let acc = FREEJS.getUserName(); let defConfig = { acc: acc, name: acc, video: [], finishCourseArr: [], }; let resultList = FREEJS.getGMData("userdata", []); let userData = resultList.find((a) => a.acc == acc); if (!userData) { userData = defConfig; resultList.push(userData); FREEJS.setGMData("userdata", resultList); } else { userData = Object.assign(defConfig, userData); } return resolve(userData); }); }, async setUserData(data) { return new Promise(async (resolve, reject) => { let resultList = FREEJS.getGMData("userdata", []); let acc = FREEJS.getUserName(); let userDataIndex = resultList.findIndex((a) => a.acc == acc); if (userDataIndex == -1) { resultList.push(data); } else { resultList.splice(userDataIndex, 1, data); } FREEJS.setGMData("userdata", resultList); await FREEJS.waitTimeout(200); return resolve(); }); }, getUserName() { return FREEJS.getGMData("username", ""); }, async setUserName() { return new Promise(async (resolve, reject) => { let username = ""; await FREEJS.waitOf((a) => { try { username = $(".m-sxthduser .name").text().trim(); if (username && username != "") { isDev && FREEJS.tipsMsg('username=' + username); FREEJS.setGMData("username", username); return true; } return false; } catch (e) { return false; } }); return resolve(username); }); }, // ====== 弹窗确认 ====== confirmRun(msg = "脚本:3秒后执行下一步操作", time = 3000) { return new Promise((resolve, reject) => { let isRun = true; let confirmRunIndex = FREEJS.dialog({ title: "脚本:是否继续执行?", content: `
${msg}
`, btn: ["取消执行"], yes: function (index) { isRun = false; reject(); FREEJS.closeDialog(index); } }); setTimeout(() => { FREEJS.closeDialog(confirmRunIndex); resolve(true); }, time); }); }, // ====== 工具方法 ====== waitTimeout(timeout) { return new Promise((resolve, reject) => { setTimeout(() => { return resolve(); }, timeout); }); }, waitOf(fun, interval = 1000, timeout = 180) { console.log("%c waitOf", "background:rgb(0,0,0);color:#fff"); return new Promise((resolve, reject) => { let _timeOut = timeout * 1000; if (fun()) { return resolve(); } let index = setInterval(() => { if (timeout != -1) { _timeOut -= interval; if (_timeOut < 0) { clearInterval(index); return reject(); } } if (fun()) { clearInterval(index); return resolve(); } }, interval); FREEJS.pageData.waitOfIndexArr.push(index); }); }, abortWaitOf() { FREEJS.pageData.waitOfIndexArr.forEach(index => { clearInterval(index); }); FREEJS.pageData.waitOfIndexArr = []; }, openDoc() { if (docUrl) { window.open(docUrl); } else { window.open("http://doc.zhanyc.cn/pages/auth/"); } }, openLjTips(tipsAndClose = true, checkUrlBeforeClose = false, timeout = 5000) { let index = FREEJS.dialog({ title: "请确认", offset: "100px", content: `

已经为你打开下一门课程,如果没有打开窗口,请检查浏览器地址栏左右两侧是否有拦截提示,请选择【永久允许】或者在浏览器设置中设置本网站【弹出式窗口和重定向】设置为允许

如下图所示:

`, btn: ["关闭"] }); if (!FREEJS.pageData.ljtsIndexArr) { FREEJS.pageData.ljtsIndexArr = []; } FREEJS.pageData.ljtsIndexArr.push(index); let url = checkUrlBeforeClose ? location.href : ""; if (FREEJS.pageData.closeTipsIndex != null) return; FREEJS.pageData.closeTipsIndex = GM_addValueChangeListener( "closeLJTS", function (name, old_value, new_value, remote) { FREEJS.pageData.ljtsIndexArr.forEach(item => { FREEJS.pageData.closeTipsIndex = null; FREEJS.closeDialog(item); }); FREEJS.pageData.ljtsIndexArr = []; tipsAndClose && FREEJS.tipsAndClose && FREEJS.tipsAndClose(url, timeout); } ); }, closeWaitConfrimWin() { FREEJS.setGMData("closeLJTS", Date.now()); }, tipsAndClose(checkUrl, timeout = 5000) { let mark = Date.now(); FREEJS.pageData.tipsAndCloseMark = mark; FREEJS.confirmRun("准备关闭当前页面,如果不想关闭请点击下面【取消执行】按钮", timeout).then((a) => { if (FREEJS.pageData.tipsAndCloseMark != mark) { isDev && console.log("页面标识变更,取消关闭窗口"); return; } if (!checkUrl || location.href == checkUrl) window.close(); }); }, addStyle() { GM_addStyle(` .zfk-btn{background-color:#0fbcf9;color:#fff;padding:4px 12px;border:none;box-sizing:content-box;font-size:14px;height:20px;border-radius:4px;cursor:pointer;display:inline-block;border:1px solid transparent;white-space:nowrap;user-select:none;text-align:center;vertical-align:middle}.zfk-btn:hover{opacity:.8}.zfk-btn.success{background-color:#38b03f}.zfk-btn.warning{background-color:#f1a325}.zfk-btn.info{background-color:#03b8cf}.zfk-btn.danger{background-color:#ea644a}.zfk-form-tips{font-size:1.2em;color:red}.tips{color:red}.zfk-form{color:#000}.zfk-form input[type=number],.zfk-form input[type=password],.zfk-form input[type=text],.zfk-form textarea{border:1px solid #888;border-radius:4px;padding:5px;box-sizing:border-box}.zfk-form input{display:inline-block;width:auto;outline:auto !important;-webkit-appearance:auto !important;}.zfk-form .zfk-form-item-title{font-weight:700}.zfk-form textarea{width:100%}.zfk-form-item{margin-bottom:10px}.zfk-form-item>label:first-child{width:7em;text-align:right;display:inline-block;padding-right:5px;margin-right:0}.zfk-form-item label{margin:0 4px 0 0!important}.zfk-form-item input{margin:0!important;float:none}.zfk-form-item.block>label:first-child{text-align:left;display:block;width:100%;font-weight:700}.text-l{text-align:left!important}.text-c{text-align:center!important}.text-r{text-align:right!important}.p-0{padding:0!important}.p-5{padding:5px!important}.p-10{padding:10px!important}.p-15{padding:15px!important}.p-20{padding:20px!important}.p-t-0{padding-top:0!important}.p-t-5{padding-top:5px!important}.p-t-10{padding-top:10px!important}.p-t-15{padding-top:15px!important}.p-t-20{padding-top:20px!important}.p-b-0{padding-bottom:0!important}.p-b-5{padding-bottom:5px!important}.p-b-10{padding-bottom:10px!important}.p-b-15{padding-bottom:15px!important}.p-b-20{padding-bottom:20px!important}.p-l-0{padding-left:0!important}.p-l-5{padding-left:5px!important}.p-l-10{padding-left:10px!important}.p-l-15{padding-left:15px!important}[... 1931 chars omitted ...] `); }, getElByText(query, text, mode = "eq", visible = true) { let $el = null; text = text.trim(); $(query).each((i, el) => { if (visible && !$(el).is(":visible")) { return true; } if (mode == "eq" && $(el).text().trim() == text) { $el = $(el); return false; } else if ( mode == "startsWith" && $(el).text().trim().startsWith(text) ) { $el = $(el); return false; } else if (mode == "endsWith" && $(el).text().trim().endsWith(text)) { $el = $(el); return false; } else if (mode == "like" && $(el).text().trim().includes(text)) { $el = $(el); return false; } }); return $el; }, getGMData(item, def) { return GM_getValue(item, def); }, setGMData(item, val) { return GM_setValue(item, val); }, delGMData(item) { return GM_deleteValue(item); }, now() { return new Date().getTime(); }, dateFormat(date = new Date(), fmt = "yyyy-MM-dd HH:mm") { let ret; if (typeof date === "number") date = new Date(date); const opt = { "y+": date.getFullYear().toString(), "M+": (date.getMonth() + 1).toString(), "d+": date.getDate().toString(), "H+": date.getHours().toString(), "m+": date.getMinutes().toString(), "s+": date.getSeconds().toString(), }; for (let k in opt) { ret = new RegExp("(" + k + ")").exec(fmt); if (ret) { fmt = fmt.replace( ret[1], ret[1].length == 1 ? opt[k] : opt[k].padStart(ret[1].length, "0") ); } } return fmt; }, }; // 存储版本号 FREEJS.setGMData("version", FREEJS.setting.version.version); if (!unsafeWindow.FREEJS) unsafeWindow.FREEJS = FREEJS; FREEJS.tipsMsg2("脚本加载总") // 3秒后初始化免费版本,如果检测到zfk对象已挂载(付费版本在运行),则跳过初始化 setTimeout(() => { debugger; if (typeof (zfk) == 'undefined') { FREEJS.init(); } else { console.log('skip init'); } }, 3000); })();