// ==UserScript== // @name 超星学习通智能刷课【可后台运行】 // @name:zh-TW // @name:en Chaoxing Automatic Learning Tool[One-click start] [Minimize run] // @description 【可手机挂机】支持【超星学习通】【学银在线】【超星系统的继续教育】的视频、章节测试、文档、直播、作业、考试;脚本一键启动、单页面全自动运行、可最小化运行不会中断;不占网速、不费流量,开热点也能用;【三亿题库】题目覆盖率99%,支持单选、多选、填空、判断【图片题、编程题、听力题】;题目答案实时收录,轻松拿高分 // @description:zh-TW 【可手机挂机】支援【超星學習通】【學銀線上】【超星系統的繼續教育】的影片、章節測驗、文件、直播、作業、考試;腳本一鍵啟動、單頁全自動運作、可最小化運作不會中斷;不佔網速、不費流量,開熱點也能用;【三億題庫】題目覆蓋率99%,支持單選、多選、填空、判斷【圖片題、程式設計題、聽力題】;題目答案即時收錄,輕鬆拿高分 // @description:en 【mobile phone supported】Supports videos, chapter tests, documents, live broadcasts, homework, and exams of [Chaoxing], [Xueyin Online]; scripts can be started with one click, run automatically on a single page, and can be minimized and run without interruption; does not occupy network speed or traffic; [300 millions questions] covers 99% of questions, supports single-choice, multiple-choice, fill-in-the-blank, judgment [picture questions, programming questions, listening questions] // @antifeature // @antifeature: // @antifeature:en payment The script will request a third-party paid question bank to answer questions. You can choose to pay or disable the answering function. // @namespace 小苏 // @version 2.1 // @author 小苏 // @run-at document-end // @storageName 小苏 // @match *://*/* // @icon http://pan-yz.chaoxing.com/favicon.ico // @grant unsafeWindow // @grant GM_xmlhttpRequest // @grant GM_setValue // @grant GM_getValue //👇👇👇👇👇👇👇👇Please fill in the code prompted by the script into the blanks below, one per line, and press Ctrl+S to save.👇👇👇👇👇👇👇👇 //👇👇👇👇👇👇👇👇請將腳本提示的程式碼填入下面空白中,一行一個,按Ctrl+S儲存👇👇👇👇👇👇👇 //👇👇👇👇👇👇👇👇请将脚本提示的代码填入下面空白中,一行一个,按Ctrl+S保存👇👇👇👇👇👇👇👇 //👆👆👆👆👆👆👆👆请将脚本提示的代码填入上面空白中,一行一个,按Ctrl+S保存👆👆👆👆👆👆👆👆 //👆👆👆👆👆👆👆👆請將腳本提示的程式碼填入上面空白中,一行一個,按Ctrl+S儲存👆👆👆👆👆👆👆 //👆👆👆👆👆👆👆👆Please fill in the code prompted by the script into the blanks above, one per line, and press Ctrl+S to save.👆👆👆👆👆👆👆 // @connect mooc1.chaoxing.com // @connect mooc1-1.chaoxing.com // @connect mooc1-2.chaoxing.com // @connect mooc2-ans.chaoxing.com // @connect mooc1-api.chaoxing.com // @connect stat2-ans.chaoxing.com // @connect passport2.chaoxing.com // @connect zhibo.chaoxing.com //---------------------------------------- // @connect mooc1.hnust.edu.cn // @connect stat2-ans.hnust.edu.cn // @connect passport2.hnust.edu.cn //---------------------------------------- // @connect mooc1.hnsyu.net // @connect stat2-ans.hnsyu.net // @connect passport2.hnsyu.net //---------------------------------------- // @connect mooc1.gdhkmooc.com // @connect stat2-ans.gdhkmooc.com // @connect passport2.gdhkmooc.com //---------------------------------------- // @connect mooc1.zut.edu.cn // @connect stat2-ans.zut.edu.cn // @connect passport2.zut.edu.cn //---------------------------------------- // @connect mooc1.wljx.hfut.edu.cn // @connect stat2-ans.wljx.hfut.edu.cn // @connect passport2.wljx.hfut.edu.cn //---------------------------------------- // @connect mooc1.hncj.edu.cn // @connect stat2-ans.hncj.edu.cn // @connect passport2.hncj.edu.cn //---------------------------------------- // @connect mooc1.qutjxjy.cn // @connect stat2-ans.qutjxjy.cn // @connect passport2.qutjxjy.cn //---------------------------------------- // @connect mooc1.jxjyzx.xust.edu.cn // @connect stat2-ans.jxjyzx.xust.edu.cn // @connect passport2.jxjyzx.xust.edu.cn //---------------------------------------- // @connect mooc1.xueyinonline.com // @connect stat2-ans.xueyinonline.com // @connect passport2.xueyinonline.com //---------------------------------------- // @connect mooc1.cqrspx.cn // @connect stat2-ans.cqrspx.cn // @connect passport2.cqrspx.cn //---------------------------------------- // @connect mooc1.ynny.cn // @connect stat2-ans.ynny.cn // @connect passport2.ynny.cn //---------------------------------------- // @connect mooc1.cugbonline.cn // @connect stat2-ans.cugbonline.cn // @connect passport2.cugbonline.cn //---------------------------------------- // @connect mooc1.xust.edu.cn // @connect stat2-ans.xust.edu.cn // @connect passport2.xust.edu.cn //---------------------------------------- // @connect mooc1.xynu.edu.cn // @connect stat2-ans.xynu.edu.cn // @connect passport2.xynu.edu.cn //---------------------------------------- // @connect mooc1.hnvist.cn // @connect stat2-ans.hnvist.cn // @connect passport2.hnvist.cn //---------------------------------------- // @connect mooc1.ecnusole.com // @connect stat2-ans.ecnusole.com // @connect passport2.ecnusole.com //---------------------------------------- // @connect mooc.s.ecust.edu.cn // @connect webvpn.ahmu.edu.cn //---------------------------------------- // @connect tk.axetk.cn // @connect tk.tk.icu // @connect tk.wanjuantiku.com // @connect ans.tk.icu // @connect 127.0.0.1 // @license MIT // @compatible firefox // @compatible chrome // @compatible edge // @supportURL https://tk.tk.icu/ // ==/UserScript== !!(function () { const //--------------------------------------------------------------------------- 是否启用后台服务器 = '0',//改为1则启用后台服务器,请先学习使用方法:https://bbs.tampermonkey.net.cn/thread-5249-1-1.html //Change 1/0 to enable background program,Please learn how to use it:https://bbs.tampermonkey.net.cn/thread-5249-1-1.html 服务器地址 = '127.0.0.1',//用于对接后台服务器,不懂不要修改 //Do not change this if you don't know how it works 端口 = '6503'; //后台服务器端口,不懂不要修改 //Do not change this if you don't know how it works //--------------------------------------------------------------------------- // 音频文件的base64,是一段静音音频,在后台播放可以防止页面休眠 // ---------- // 别看了,屎山 // ---------- let $w = unsafeWindow, $l = $w.location.href, $d = $w.document, $version = GM_info.script.version.replaceAll('.', ''), // 调用$s[value]可以获取url查询值 $s = Object.fromEntries(new URLSearchParams($w.location.search)), $protocol = $w.location.protocol + "//", getCookie = name => `; ${document.cookie}`.split(`; ${name}=`).pop().split(';').shift(), $uid = getCookie('UID') || getCookie('_uid') || $s['uid'], // 自己封装的仿layer弹窗,因为从外部调用js可能受网络影响 $layer = (info) => { const body = $d.body; const shadow = $d.createElement('div'); const alert = $d.createElement('div'); const alertHead = $d.createElement('div'); const alertBody = $d.createElement('div'); const buttons = $d.createElement('div'); const button = $d.createElement('span'); shadow.classList.add('shadow'); alert.classList.add('alert'); alertHead.classList.add('alertHead'); alertBody.classList.add('alertBody'); buttons.classList.add('buttons'); button.classList.add('button'); alertHead.innerHTML = '提示' alertBody.innerHTML = info; button.innerHTML = '确定'; button.addEventListener('click', () => { shadow.remove(); alert.remove(); }); buttons.appendChild(button); alert.appendChild(alertHead) alert.appendChild(alertBody) alert.appendChild(buttons) body.appendChild(shadow); body.appendChild(alert); }, // 根据ascii排序数组元素 $ascii = (str) => { return str.split('').sort((a, b) => a.charCodeAt(0) - b.charCodeAt(0)).join(''); }, // 生成随机32位字符串 randomString = (z = false) => { let t = "abcdef1234567890", result = '' z && (t += "ghijklmnopqrstuvwxyz") for (let i = 0; i < 32; i++) { const randomIndex = Math.floor(Math.random() * t.length); result += t[randomIndex]; } return result }, // 生成随机毫秒数,例如$(1,2)可能返回1500 $n = function (min, max) { if (arguments.length == 1) { max = min + 1; if (min > 1) { min = min - 1 } } else if (arguments.length == 0) { min = 4; max = 6; } return (Math.random() * (max - min) + min).toFixed(3) * 1000; }, // sleep函数,使用await调用 sleep = (interval) => { return new Promise((success, fail) => { setTimeout(success, interval); }); }, hostList = [ 'https://tk.tk.icu/', 'https://tk.axetk.cn/', 'https://tk.wanjuantiku.com/' ], host = '', handleImgs = (s) => { // 去除字符串中的style与script标签和其中的内容 s = s.replace(/]*>[\s\S]*?<\/style>/gi, '').replace(/]*>[\s\S]*?<\/script>/gi, '') let imgEs = s.match(/(]*)>)/ig) // 提取html中的图片 if (imgEs) { for (let j = 0, k = imgEs.length; j < k; j++) { let div = $d.createElement('div') div.innerHTML = imgEs[j] if (div.children.length < 1) { continue } let src = div.children[0].getAttribute('src') src && (s = s.replace(imgEs[j], src.replace(/http[s]?:\/\//, ''))); } } // 提取iframe(音频内容),转化为纯文本链接 let iframeEs = s.match(/(]*)>)/ig); if (iframeEs) { for (let j = 0, k = iframeEs.length; j < k; j++) { let div = $d.createElement('div') div.innerHTML = iframeEs[j] let src = div.children[0].getAttribute('_src') || div.children[0].getAttribute('src') src && (s = s.replace(iframeEs[j], src.replace(/http[s]?:\/\//, ''))); } } // 提取audio标签(旧版音频) let div = $d.createElement('div') div.innerHTML = s let audioPlayers = div.querySelectorAll('.audioReader') for (let audioPlayer of audioPlayers) { let source = audioPlayer.querySelector('source') let src = '' if (source) { src = source.getAttribute('src') if (src) { src = src.replace(/http[s]?:\/\//, '') } } audioPlayer.innerHTML = src } s = div.innerHTML return s }, // 格式化字符串 trim = (s) => { return handleImgs(s) .replaceAll("-", '') .replaceAll(/([\x00-\x1F\x7F]|\s){2,}/g, ' ') .replaceAll(/<[^>]*>/g, '') .replaceAll('javascript:void(0);', '') .replaceAll(",", ",") .replaceAll("。", ".") .replaceAll("?", "?") .replaceAll("!", "!") .replaceAll(":", ":") .replaceAll(";", ";") .replaceAll("“", '"') .replaceAll("”", '"') .replaceAll("‘", "'") .replaceAll("’", "'") .replaceAll("(", "(") .replaceAll(")", ")") .replaceAll("【", "[") .replaceAll("】", "]") .replaceAll("、", ",") .replaceAll("\"", '*') .replaceAll(" ", '') .replaceAll("'", '*') .replaceAll("%", '*') .replaceAll(" ", '') .replace(/^([\x00-\x1F\x7F]|\s)+/ig, '') .replace(/([\x00-\x1F\x7F]|\s)+$/ig, ''); }, isSameDomain = (url1, url2) => { // 判断请求前后两个url是否为同域 if (url1.indexOf('http') !== 0) { // 直接请求路径,说明与页面同域,替换为页面url url1 = $l } try { const currentUrl = new URL(url1); const testUrl = new URL(url2); // 比较主机名(hostname)是否相同 return currentUrl.hostname === testUrl.hostname; } catch (error) { return false; } }, request = (data) => { let hosts = window.location.href.match(/https:\/\/webvpn\.(.*?)\/https\/[0-9a-z]+\//) if (Array.isArray(hosts)&&hosts.length>0) { try { if(data.headers&&data.headers.Referer){//修改referer为vpn try{ const refererObj = new URL(data.headers.Referer) data.headers.Referer = hosts[0].slice(0, -1) + refererObj.pathname + refererObj.search + refererObj.hash }catch(e){ console.log(e) } } const urlObj = new URL(data.url) data.url = hosts[0].slice(0, -1) + urlObj.pathname + urlObj.search + urlObj.hash } catch (e) {//data.url是个路径,不是完整url if(data.url.charAt(0)=='/'){ data.url = hosts[0].slice(0, -1)+data.url }else{ data.url = hosts[0]+data.url } console.log(e) } if(!data.url.includes('&enlink-vpn')){ data.url+='&enlink-vpn' } }//如果检测到校园网,自动重建请求到校园网节点 return new Promise((success, fail) => { if (data.method == undefined) { data.method = 'GET'; } data.method = data.method.toUpperCase() if (data.timeout == undefined) { data.timeout = 10000; } if (!data.headers) { data.headers = {} } data.headers['Accept-Language'] = 'zh-CN,zh;q=0.9'; data.onload = function (res) { if (res.responseText.includes('用户登录')) { // 请求后跳转到登录页,说明cookie出现异常(此问题易出现在篡改猴5.2.0版本与同期部分篡改猴beta版) if (isSameDomain(data.url, $l)) { let method = data.method // 是否与页面同域,如果是就改用xhr const xhr = new XMLHttpRequest() xhr.open(method, data.url, true) xhr.onreadystatechange = function () { if (xhr.readyState === XMLHttpRequest.DONE) { if (xhr.status >= 200 && xhr.status < 300) { let res = { responseText: xhr.responseText, status: xhr.status } success(res) } else { success(false) } } } xhr.onerror = function () { alert('登录状态失效,请重新登陆超星'); $w.location.href = $protocol + $w.location.host.replace(/mooc(.*?)\./ig, 'passport2.') + '/login?fid=&newversion=true&refer=' + encodeURIComponent($l) } xhr.timeout = data.timeout if (method === 'POST' && data.data) { if (data.headers) { for (let i in data.headers) { xhr.setRequestHeader(i, data.headers[i]) } } xhr.send(data.data) } else { xhr.send() } } else if (GM_info.scriptHandler == "Tampermonkey" && ['5.2.6195', '5.2.6196', '5.2.6197', '5.2.6198', '5.2.6199', '5.2.6200', '5.2.0'].includes(GM_info.version)) { if (confirm(`您正在使用的油猴插件版本为测试版:${GM_info.version}\n该版本存在未修复的问题,无法稳定的运行此脚本,请使用脚本猫\n点击确定前往脚本猫官网`)) { $w.location.href = 'https://scriptcat.org/zh-CN/' } else { success(false) } } } else { success(res) } } data.onerror = function (e) { const hosts = data.url.match(/^http(s)?:\/\/(.*?)\//) if (hosts && hosts.length > 0) { let host = hosts[0].replace(/^http(s)?:\/\//, '').replace('\/', ''); if (typeof e == 'object' && e.error && e.error.includes('Refused to connect to')) { $w.logs.addLog('
刚刚有一个跨域请求发送失败
👇请编辑脚本代码,将下方蓝色代码填入脚本指定空白中👇

// @connect ' + host + '

👆请编辑脚本代码,将上面蓝色代码填入脚本指定空白中👆
别忘了保存并刷新页面哦'); $w.wait = true; } else if (typeof e == 'string' && e.includes('permission')) { $w.logs.addLog('
刚刚有一个跨域请求发送失败
👇请编辑脚本代码,将下方蓝色代码填入脚本指定空白中👇

// @connect ' + host + '

👆请编辑脚本代码,将上面蓝色代码填入脚本指定空白中👆
别忘了保存并刷新页面哦'); $w.wait = true; } } try { $w.logs.addLog('请求错误,请检查网络连接', 'red'); } catch (e) { } if (!$l.includes('chaoxing.com')) { let classId = $s['clazzid'] || $s['classid'] || $s['classId'] || $s['classId'], courseId = $s['courseid'] || $s['courseId']; if (confirm('检测到您使用的是学校定制学习通,可能会出现使用问题\n可以尝试切换到学习通官方页面(需要重新登陆),是否切换?')) { $w.location.href = 'http://passport2.chaoxing.com/login?refer=http%3A%2F%2Fmooc1.chaoxing.com%2Fvisit%2Fstucoursemiddle%3Fcourseid%3D' + courseId + '%26clazzid%3D' + classId + '%26vc%3D1%26ismooc2%3D1%26v%3D2%26r%3D1&newversion=true&_blank=0'; } } console.log(e); success(false); } data.ontimeout = function () { success(false); } GM_xmlhttpRequest(data); }) }, brequest = (data) => { return new Promise((success, fail) => { if (!data.method) { data.method = 'get' } if (!data.timeout) { data.timeout = 1e4 } data.ontimeout = data.onerror = function () { success(false) } data.onload = function (res) { try { success(JSON.parse(res.responseText)); } catch (e) { console.log(e); success(false); } } GM_xmlhttpRequest(data); }) }, tkLeft = (left, tkToken) => { if (!Number.isInteger(left)) { return } $w.left = left; GM_setValue('tkLeft', left); try { $d.querySelector('#ugyvciuu').value = left } catch (e) { } try { $d.querySelector('#payButton').setAttribute("href", host + "?token=" + tkToken + "#2") } catch (e) { } try { $d.querySelector('#bindQQ').setAttribute("href", host + "?token=" + tkToken + "#bind") } catch (e) { } try { $d.querySelector("#payToken").setAttribute("href", host + "?token=" + tkToken + "#2") } catch (e) { } try { $d.querySelector("#tokenLeft").value = left } catch (e) { } }, ctk = (token) => { let url = host + 'api/checkLeft?token=' + token let left = 0 try { brequest({ "url": url }).then(function (success) { try { left = success.data if (left == -1) { // 剩余次数为-1,说明可能token注册但从未登录,然后现在失效了,需要重新注册 register(token) left = 0 } tkLeft(left, token); if (success.hasInfo) { if (success.msg !== $w.lastLog) {//判断是否为重复提示,防止造成骚扰 $w.lastLog = success.msg try { $w.logs.addLog(success.msg, 'orange') } catch (e) { console.log(e) } } } } catch (e) { console.log(e) } }) } catch (e) { console.log(e) } finally { return left } }, // 检查字符串是否为判断题答案 panDuan = str => { const right = ['正确', '对', '是', 'T', '√', 'ri', 'true', 'A'] const wrong = ['错误', '错', '否', 'F', '×', 'wr', 'false', 'B'] let result = false right.forEach(e => { if (str.includes(e)) { result = 'r' } }) wrong.forEach(e => { if (str.includes(e)) { result = 'w' } }) return result }, // 检查脚本是否多开 checkDuoKai = () => { if ($w.top !== $w) { return false; } try { // 在页面添加一个全局数组,如果数组元素数量>2,就是多开了 $w.checkConcurrency.push(6); } catch { $w.checkConcurrency = [6]; } const scriptNum = $w.checkConcurrency.length if (scriptNum > 1) { alert(`【超星学习通九九助手】\n\n请勿多开此脚本,您开启了${scriptNum}个,请删除多余脚本\n\n请检查您是否同时安装了多个相同的脚本\n是否同时安装了多个脚本管理器:脚本猫、暴力猴、油猴等\n是否有脚本管理器插件被归档至扩展按钮中`); return true; } return false; }, // 使用textarea元素来去除html中奇奇怪怪的符号 decodeHtmlEntities = text => { const textarea = $d.createElement('textarea'); textarea.innerHTML = text; return textarea.value; }, md5 = (str) => { function hex_md5(s) { return binl2hex(core_md5(str2binl(s), s.length * 8)); } function core_md5(x, len) { x[len >> 5] |= 0x80 << ((len) % 32); x[(((len + 64) >>> 9) << 4) + 14] = len; let a = 1732584193; let b = -271733879; let c = -1732584194; let d = 271733878; for (let i = 0; i < x.length; i += 16) { let olda = a; let oldb = b; let oldc = c; let oldd = d; a = md5_ff(a, b, c, d, x[i + 0], 7, -680876936); d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586); c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819); b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330); a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897); d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426); c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341); b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983); a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416); d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417); c = md5_ff(c, d, a, b, x[i + 10], 17, -42063); b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162); a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682); d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101); c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290); b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329); a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510); d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632); c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713); b = md5_gg(b, c, d, a, x[i + 0], 20, -373897302); a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691); d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083); c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335); b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848); a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438); d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690); c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961); b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501); a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467); d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784); c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473); b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734); a = md5_hh(a, b, c, d, x[i + 5], 4, -378558); d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463); c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562); b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556); a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060); d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353); c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632); b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640); a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174); d = md5_hh(d, a, b, c, x[i + 0], 11, -358537222); c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979); b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189); a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487); d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835); c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520); b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651); a = md5_ii(a, b, c, d, x[i + 0], 6, -198630844); d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415); c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905); b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055); a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571); d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606); c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523); b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799); a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359); d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744); c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380); b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649); a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070); d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379); c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259); b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551); a = safe_add(a, olda); b = safe_add(b, oldb); c = safe_add(c, oldc); d = safe_add(d, oldd); } return Array(a, b, c, d); } function md5_cmn(q, a, b, x, s, t) { return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b); } function md5_ff(a, b, c, d, x, s, t) { return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t); } function md5_gg(a, b, c, d, x, s, t) { return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t); } function md5_hh(a, b, c, d, x, s, t) { return md5_cmn(b ^ c ^ d, a, b, x, s, t); } function md5_ii(a, b, c, d, x, s, t) { return md5_cmn(c ^ (b | (~d)), a, b, x, s, t); } function safe_add(x, y) { let lsw = (x & 0xFFFF) + (y & 0xFFFF); let msw = (x >> 16) + (y >> 16) + (lsw >> 16); return (msw << 16) | (lsw & 0xFFFF); } function bit_rol(num, cnt) { return (num << cnt) | (num >>> (32 - cnt)); } function str2binl(str) { let bin = Array(); for (let i = 0; i < str.length * 8; i += 8) bin[i >> 5] |= (str.charCodeAt(i / 8) & 255) << (i % 32); return bin; } function binl2hex(binarray) { let hex_tab = "0123456789abcdef"; let str = ""; for (let i = 0; i < binarray.length * 4; i++) { str += hex_tab.charAt((binarray[i >> 2] >> ((i % 4) * 8 + 4)) & 0xF) + hex_tab.charAt((binarray[i >> 2] >> ((i % 4) * 8)) & 0xF); } return str; } return hex_md5(str) }, getTkToken = () => { //尝试从脚本存储中获取token,如果无法获取,就从页面缓存中获取 return GM_getValue('shenchanranToken', false) || $w.localStorage.getItem("shenchanranToken") }, register = (tempToken) => { return brequest({ url: host + 'api/register', method: 'put', data: JSON.stringify( { 'token': tempToken, 'md5': md5(tempToken) } ), headers: { "Content-Type": "application/json" } }) }, hostCheck = () => { return new Promise((success, fail) => { function r(i) { if (i >= hostList.length) { let z = confirm('【超星学习通九九助手】\n所有服务器均不可用,请稍后刷新重试或尝试更换网络\n中国移动宽带/校园网建议更换网络\n如果仍无法使用,请点击“取消”按钮自动前往更新脚本'); if (!z) { $w.top.location.href = 'https://greasyfork.org/zh-CN/scripts/469522' } fail() return } brequest({ "url": hostList[i] + 'api/status?ran=' + String(Date.now()) + '&version=' + $version }).then((checkResult) => { if (!checkResult) { i++ r(i) success() } else if (checkResult.status == 't') { host = hostList[i]; s() } else if (checkResult.status == 'f') { alert('【超星学习通九九助手】服务器暂停服务,请耐心等待恢复\n' + checkResult.info); fail() } else if (checkResult.status == 'u') { let l = confirm('【超星学习通九九助手】当前脚本有新版本,点击确定前往更新'); if (l) { $w.top.location.href = checkResult.url fail() return } success() } }) } let sTryTime = 0 function s() { sTryTime += 1 if (sTryTime > 4) { alert('【超星学习通九九助手】token注册失败,请刷新页面重试') fail() } let token = GM_getValue('shenchanranToken', false) || $w.localStorage.getItem("shenchanranToken"), reg = /^[0-9a-z]{32}$/ig; //如果获取了旧版token或者哪里都无法获取,就重新生成一个 if (['66666666666666666666666666666666', '', null, undefined, false].includes(token) || !reg.test(token)) { let tempToken = randomString(true) register(tempToken).then((result) => { if (!result || result.code != 1) { setTimeout(() => { s() }, 500) } token = tempToken $w.localStorage.setItem("shenchanranToken", token) GM_setValue('shenchanranToken', token) success() }) } else { success() } } r(0) }) } // 监听此页面,判断用户是否需要刷学习次数,如果需要,就点进章节 if ($l.includes('mycourse/studentcourse?')) { let i = setInterval(() => { if (GM_getValue("doxxcs", false)) { clearInterval(i) const lis = [...$d.querySelectorAll(".chapter_item"), ...$d.querySelectorAll("h3")] for (e of lis) { if (e.innerHTML.includes("chapterId") || (e.getAttribute("onclick") && e.getAttribute("onclick").includes("toOld"))) { e.click() e.querySelector("a").click() return } } } }, 500) } else if ($l.includes('return_url.php?pid=1000')) {//防止有用户充值到其他token上 let scriptToken = GM_getValue('shenchanranToken','none'), reg= /^[0-9a-z]{32}$/ if(reg.test(scriptToken)&&GM_getValue('tkLeft',0)==100){//确定脚本已经生成了token,并且答题次数为0 for(host of hostList){ if(host.includes($w.location.host)){ let ctoken = getCookie('token') if(reg.test(ctoken)&&ctoken!=scriptToken){ if(confirm('【超星学习通九九助手】\n\n您当前充值的token不是脚本正在使用的Token\n是否自动切换为您充值的token?')){ GM_setValue('shenchanranToken',ctoken) } } } } } } // 刷章节次数页面 else if ($l.includes('mycourse/studentstudy?') && GM_getValue('doxxcs', false)) { GM_setValue('doxxcs', false) const body = $d.querySelector("body") const div = $d.createElement('div') div.setAttribute("id", "skpannel") div.innerHTML = `
超星学习通九九助手
正在刷学习次数,如需停止请刷新页面
已刷1
学习次数不是实时更新,请不要同时运行其他脚本,否则刷次数会失败
点我一下,然后你就不用一直盯着这个页面了
` let d const a = () => { // 随机点击任意章节,学习次数+1 const s = [...$d.querySelectorAll('.posCatalog_name'), ...$d.querySelectorAll('a')] s.sort((a, b) => { // 打乱数组顺序 return Math.random() - 0.5 }) for (let z of s) { let onclick = z.getAttribute("onclick") || z.getAttribute("href") if (onclick && onclick.includes('getTeacherAjax') && onclick != lastOnclick) { lastOnclick = onclick z.click() d.innerHTML = +d.innerHTML + 1 break } } } let lastOnclick = null setTimeout(e => { body.appendChild(div) d = $d.querySelector('#ciuu') setInterval(() => { a() }, $n(10, 20)) const audioPlayer = new Audio(audiofile) audioPlayer.loop = true $d.addEventListener('visibilitychange', function () { // 播放静音音频,防止页面休眠 var c = 0; if ($d.hidden) { audioPlayer.play(); var timer = setInterval(function () { if (c) { $d.title = '🙈刷次数中'; c = 0; } else { $d.title = '🙉刷次数中'; c = 1; } if (!$d.hidden) { clearInterval(timer); $d.title = '学生学习页面'; } }, 1300); } else { audioPlayer.pause(); } }) }, 1000) } // 章节主页,在此页面显示导航弹窗 if ($l.includes('/mycourse/stu?') || ($l.includes('mycourse/studentcourse?') && $w.top == $w)) { if (checkDuoKai()) { return; } let newVersion = true if ($l.includes('mycourse/studentcourse?')) { if (GM_getValue('directToWork', false)) { GM_setValue('directToWork', false) $d.querySelector('.workTip').previousSibling.click() return } newVersion = false } else { setTimeout(()=>{ let hosts = window.location.href.match(/https:\/\/webvpn\.(.*?)\/https\/[0-9a-z]+\//) if (Array.isArray(hosts)&&hosts.length>0) { $w.backToOld() } },1000)//如果检测到校园网,自动切换到旧版页面(新版没法用) setTimeout(e => { const pagehead = $d.querySelector('.headRight') if (!pagehead.innerHTML.includes('回到旧版')) { let a = $d.createElement('a') a.setAttribute('href', 'javascript:;') a.setAttribute('class', "backOld fl") a.setAttribute('onclick', "backToOld()") a.innerHTML = "回到旧版" pagehead.prepend(a) } }, 2000) } const body = $d.querySelector("body") const div = $d.createElement('div') const entrance = (host) => { let hosts = window.location.href.match(/https:\/\/webvpn\.(.*?)\/https\/[0-9a-z]+\//) if (Array.isArray(hosts)&&hosts.length>0) { host = hosts[0].slice(0, -1) }//如果检测到校园网,自动替换host为校园网节点 let classId = $s['clazzid'] || $s['classid'] || $s['classId'] || $s['classId'], courseId = $s['courseid'] || $s['courseId'], cpi = $s['cpi'] || '', courseName = $d.title.replace('-首页', '') $w.location.href = host + `/mooc-ans/course/999999999${$version}.html?ut=s&classid=` + classId + '&courseid=' + courseId + '&cpi=' + cpi + '&coursename=' + courseName + '&uid=' + $uid } div.setAttribute("id", "skpannel") if (/Android/i.test(navigator.userAgent)&&!/Edg/i.test(navigator.userAgent)) { let a = confirm('【超星学习通九九助手】\n\n您可能正在使用手机浏览器\n我们不保证脚本能正常运行\n也不保证学习成绩\n建议使用edge手机端浏览器\n点击确定查看详细教程、点击取消关闭此窗口') if(a){ $w.location.href = 'https://blog.bj.cn/?post=7' } }else if(/EdgA/i.test(navigator.userAgent)){ alert('【超星学习通九九助手】\n\n您可能正在使用edge手机浏览器\n我们不保证脚本能在后台正常挂机\n不同手机的后台策略不同,可能在您回到桌面后会自动暂停\n如遇问题请刷新页面或更换为电脑浏览器') } div.innerHTML = `
超星学习通
该课程支持刷课,请选择您要刷的内容
刷章节 刷作业 刷考试 刷学习次数 取消
` hostCheck().then(() => { setTimeout(() => { const tempUIdElement = $d.querySelector("input[name='userId']") || $d.querySelector("input[id='uploadUid']") if (tempUIdElement && !$uid) $uid = tempUIdElement.value body.appendChild(div) const warning = $d.querySelector('.warn-txt') || $d.querySelector('.continue-study') const warns = ["已開啟結課", "本课程已结课", "已开启结课"] let jkdata = GM_getValue('jkdata', {}) let classId = $s['clazzid'] || $s['classid'] || $s['classId'] || $s['classId'] let jk = false if (warning) { warns.forEach(e => { if (warning.innerHTML.includes(e)) { jk = true jkdata[classId] = true GM_setValue('jkdata', jkdata) } }) } if (!jk && jkdata[classId]) { jkdata[classId] = false GM_setValue('jkdata', jkdata) } ctk(getTkToken()) }, 800) setTimeout(() => { let tkToken = getTkToken() $d.querySelector("#token").value = tkToken const pannel = $d.querySelector('#skpannel') const tokentip = $d.querySelector('#tokentip') $d.querySelector('#dosk').addEventListener('click', () => { pannel.style.display = 'none'; if (newVersion) entrance($w.ServerHost.mooc1Domain.replace('https://', 'http://')) else entrance("http://" + $w.location.host) }) $d.querySelector('#doHomework').addEventListener('click', () => { pannel.style.display = 'none'; if (newVersion) $d.querySelector('.zy').click() else $d.querySelector('.workTip').previousSibling.click() }) $d.querySelector('#doExam').addEventListener('click', () => { pannel.style.display = 'none'; if (newVersion) $d.querySelector('.ks').click() else $d.querySelector('.testTip').previousSibling.click() }) $d.querySelector('#canclesk').addEventListener('click', () => { pannel.style.display = 'none'; }) $d.querySelector('#tokenTip').addEventListener('click', () => { tokentip.innerHTML = ` ` tokentip.style.display = 'block' }) $d.querySelector('#saveToken').addEventListener('click', () => { let stkToken = $d.querySelector('#token').value, reg = /^[0-9a-z]{32}$/ig if (!reg.test(stkToken)) { tokentip.innerHTML = `

token格式错误

` tokentip.style.display = 'block' setTimeout(e => { tokentip.style.display = 'none' }, 2000) $d.querySelector("#token").value = tkToken return } if (tkToken == stkToken) { tokentip.innerHTML = `

请在token输入框填写新token

` tokentip.style.display = 'block' setTimeout(e => { tokentip.style.display = 'none' }, 5000) return } tkToken = stkToken $w.localStorage.setItem("shenchanranToken", tkToken); GM_setValue('shenchanranToken', tkToken); tokentip.innerHTML = `

保存成功

` tokentip.style.display = 'block' setTimeout(e => { tokentip.style.display = 'none' }, 2000) ctk(tkToken) }) $d.querySelector('#doxxcs').addEventListener('click', () => { GM_setValue("doxxcs", true) if (newVersion) { const lis = [...$d.querySelectorAll("li")] lis.forEach(e => { if (e.getAttribute("dataname") === 'zj') { e.click() } }) } }) }, 1000); }) } // 刷章节页面 else if ($l.includes('.html?ut=s&classid') && $l.includes('/mooc-ans/course/') && !$l.includes('passport2')) { if (checkDuoKai()) { return; } $w.wait = false; // 某些脚本在某些脚本插件运行时会导致此脚本版本号异常 if (!$version || $version == '' || $version == undefined) { alert('请关闭其他同类型脚本,否则此脚本无法正常运行'); } $d.getElementsByTagName('body')[0].innerHTML = '加载中。。。

如遇脚本异常,请尝试点我更新脚本 | 点我更新脚本'; let beisu = 1, vgqtlv = 85 const checkIframe = function (iframe) { return new Promise((success, fail) => { iframe.onload = function () { success(); } }) } async function main() { $d.querySelector('html').innerHTML = ` 超星刷课工具

课程名

Trying to do the best
任务设置: 视频任务  |  章节测试  |  文档任务  |  非任务点



章节测试:自动提交   总开关:点我启动  | 闯关模式 什么是闯关模式?
脚本配置
倍速大于1倍会被清空进度
当脚本作答后的正确率低于这个数值时不会自动提交
保存 保存后会实时生效
任务
任务数量 : 共有 -章节,剩余-章节
当前任务进度
0%
同是天涯沦落人

大家放心刷课吧,这个脚本用的其实还是很不错的

放个赞赏码在这,感谢各位哥哥姐姐的投喂,从哥哥姐姐们手里收到零零碎碎的小费心里也是很满足的

感谢大家一直以来的支持,好多人赞赏留言的名字,我都会记住的各位,大家好好刷课,给自己留出更多的时间

新大学生们,大家一起加油!!!1

Smiley face
`; await sleep(100); let tkToken = getTkToken() ctk(tkToken) $w.logs = { "addLog": function (str, color = "black") { const nowTime = new Date(), nowHour = (Array(2).join(0) + nowTime.getHours()).slice(-2), nowMin = (Array(2).join(0) + nowTime.getMinutes()).slice(-2), nowSec = (Array(2).join(0) + nowTime.getSeconds()).slice(-2), logElement = $d.querySelector('#log'), resultElement = $d.querySelector('#result'), span = $d.createElement('span') span.style.color = color span.style.display = "block" span.innerHTML = `[${nowHour}:${nowMin}:${nowSec}] ${str}` logElement.appendChild(span) resultElement.scrollTop = resultElement.scrollHeight; }, "clear": function () { $d.querySelector('#log').innerHTML = ''; } }; $w.chuangguan = false; let tkc = () => { setTimeout(() => { let token = getTkToken() if (token) { ctk(tkToken) } tkc(); }, 6e4); } tkc(); $d.getElementById('courseName').innerHTML = decodeURIComponent($s['coursename']); if (/^((?!chrome|android).)*safari/i.test($w.navigator.userAgent)) { $layer("
此脚本不支持Safari浏览器
请mac/ipad用户安装Microsoft Edge浏览器
") } $layer('

倍速刷视频会导致学习进度被清空

同账号多开脚本学习会导致学习进度被清空

什么是清空?为什么会清空?

现已支持安卓端运行!

'); const doVideoButton = $d.querySelector('#doVideo'), doDocumentButton = $d.querySelector('#doDocument'), doWorkButton = $d.querySelector('#doWork'), autoSubmitButton = $d.querySelector('#autoSubmit'), saveConfigButton = $d.querySelector('#saveConfig'), startButton = $d.querySelector('#start'), startNewButton = $d.querySelector('#start_new'), doNoMissionButton = $d.querySelector('#doNoMission'), ciuuqButton = $d.querySelector('#ciuuq'), wicButton = $d.querySelector('#wic'); doVideoButton.onclick = function () { if ($w.chuangguan) { $w.logs.addLog('闯关模式下禁止操作', 'red'); return; } let s = doVideoButton.getAttribute('class').includes('light'); GM_setValue('doVideo', (() => { return s && ((() => { doVideoButton.setAttribute('class', 'btn btn-primary'); $w.logs.addLog('将会处理视频任务', 'green'); return true; })()) || ((() => { doVideoButton.setAttribute('class', 'btn btn-light'); $w.logs.addLog('将不会处理视频任务', 'red'); return false; })()) })()); } doDocumentButton.onclick = function () { if ($w.chuangguan) { $w.logs.addLog('闯关模式下禁止操作', 'red'); return; } let s = doDocumentButton.getAttribute('class').includes('light'); GM_setValue('doDocument', (() => { return s && ((() => { doDocumentButton.setAttribute('class', 'btn btn-primary'); $w.logs.addLog('将会处理文档任务', 'green'); return true; })()) || ((() => { doDocumentButton.setAttribute('class', 'btn btn-light'); $w.logs.addLog('将不会处理文档任务', 'red'); return false; })()) })()); } doWorkButton.onclick = function () { if ($w.chuangguan && GM_getValue('doWork', false)) { $w.logs.addLog('闯关模式下禁止操作', 'red'); return; } let s = doWorkButton.getAttribute('class').includes('light'); GM_setValue('doWork', (() => { return s && ((() => { doWorkButton.setAttribute('class', 'btn btn-primary'); $w.logs.addLog('将会处理章节测试任务', 'green'); return true; })()) || ((() => { doWorkButton.setAttribute('class', 'btn btn-light'); $w.logs.addLog('将不会处理章节测试任务', 'red'); return false; })()) })()); } autoSubmitButton.onclick = function () { if ($w.chuangguan) { $w.logs.addLog('闯关模式下禁止操作', 'red'); return; } let s = autoSubmitButton.getAttribute('class').includes('light'); GM_setValue('autoSubmit', (() => { return s && ((() => { autoSubmitButton.setAttribute('class', 'btn btn-primary'); $w.logs.addLog('正确率超过' + vgqtlv + '%的章节测试将会自动提交', 'green'); return true; })()) || ((() => { autoSubmitButton.setAttribute('class', 'btn btn-light'); $w.logs.addLog('章节测试将不会自动提交', 'red'); return false; })()) })()); } doNoMissionButton.onclick = function () { if ($w.chuangguan) { $w.logs.addLog('闯关模式下禁止操作', 'red'); return; } let s = doNoMissionButton.getAttribute('class').includes('light'); GM_setValue('doNoMission', (() => { return s && ((() => { doNoMissionButton.setAttribute('class', 'btn btn-primary'); $w.logs.addLog('将会作答非任务点的章节测试(请刷新页面后继续)', 'green'); return true; })()) || ((() => { doNoMissionButton.setAttribute('class', 'btn btn-light'); $w.logs.addLog('将不会作答非任务点', 'red'); return false; })()) })()); } saveConfigButton.onclick = function () { let stkToken = $d.getElementById('tokenInput').value, reg = /^[0-9a-z]{32}$/ig, sbeisu = $d.getElementById('beisuInput').value, svgqtlv = $d.getElementById('vgqtlv').value; if (!reg.test(stkToken)) { $layer('token格式不符'); $d.getElementById('tokenInput').value = tkToken; } else if (sbeisu < 0.5 || sbeisu > 16) { $layer('倍速必须在0.5-16之间'); } else if (svgqtlv < 0 || svgqtlv > 100) { $layer('正确率必须在0-100之间'); } else { beisu = sbeisu; vgqtlv = svgqtlv if (tkToken != stkToken) { $w.logs.clear() $w.logs.addLog('token已变更', 'green') if (GM_getValue('tkLeft', 0) > 0) { $layer('您的题库剩余次数不为0,请注意保存原token
丢失后请自行前往微信或支付宝查询付款记录
原token:' + tkToken) } } $layer('配置保存成功,已实时生效') tkToken = stkToken; GM_setValue('shenchanranToken', tkToken); $w.localStorage.setItem("shenchanranToken", tkToken); ctk(tkToken) } } startButton.onclick = function () { startButton.setAttribute('class', 'btn btn-light'); startButton.innerHTML = startNewButton.innerHTML = '任务已启动'; startButton.onclick = startNewButton.onclick = () => { $layer('如需停止任务,请刷新页面'); } startButton.id = startNewButton.id = "abaaba"; start(); $w.logs.addLog('开始查询任务'); } startNewButton.onclick = function () { $layer('闯关模式将自动开启所有任务类型
建议调低答题正确率
遇到无法作答的题型与任务点将会自动终止'); GM_setValue('doVideo', true); GM_setValue('doDocument', true); GM_setValue('doWork', true); $w.logs.addLog('视频、文档、章节测试任务已开启,如需重置,请刷新页面', 'green'); startNewButton.setAttribute('class', 'btn btn-light'); startNewButton.innerHTML = startButton.innerHTML = '闯关任务已启动'; startButton.onclick = startNewButton.onclick = () => { $layer('如需停止任务,请刷新页面'); } startButton.id = startNewButton.id = "abaaba"; start(); $w.chuangguan = true; $w.logs.addLog('开始查询任务'); } ciuuqButton.onclick = function () { $layer('

更换设备、浏览器或者清空浏览器数据会导致token丢失,脚本会自动为您生成一个新Token。

如果想找回之前的Token,请查看您的微信或支付宝付款记录,在商品名中有充值过的Token。

如果您之前的Token绑定过QQ或微信,请直接访问题库官网并使用绑定的账号登录,即可找回原Token。

找回Token后请将Token复制到当前页面的Token输入框,然后点击保存按钮。

为新Token绑定微信可以获得免费的100答题次数

') } wicButton.onclick = function () { $layer('

如果您的课程章节学完一节才能解锁下一节,不能跳过,那么这门课就是闯关模式,必须点击闯关模式按钮才能进行刷课

') } doVideoButton.setAttribute('class', ['btn btn-light', 'btn btn-primary'][GM_getValue('doVideo', 1) + 0]); doDocumentButton.setAttribute('class', ['btn btn-light', 'btn btn-primary'][GM_getValue('doDocument', 1) + 0]); doWorkButton.setAttribute('class', ['btn btn-light', 'btn btn-primary'][GM_getValue('doWork', 1) + 0]); autoSubmitButton.setAttribute('class', ['btn btn-light', 'btn btn-primary'][GM_getValue('autoSubmit', 1) + 0]); doNoMissionButton.setAttribute('class', ['btn btn-light', 'btn btn-primary'][GM_getValue('doNoMission', 0) + 0]); $d.getElementById('tokenInput').value = tkToken; $d.getElementById('beisuInput').value = beisu; $d.getElementById('vgqtlv').value = vgqtlv; $d.querySelector('#log').innerHTML = '' $w.logs.addLog('脚本初始化成功,点击启动按钮开始刷课', 'green'); } async function start() { //如果用户给单个账号同时刷多门课(每个人都想这么做)就会被清空所有课程的进度,为了防止这种情况发生,添加检测,在刷视频时更新localstorage,触发其他页面的addEventListener let multipleTold = false $w.addEventListener('storage', (event) => { if (!multipleTold && event.key == $uid && event.newValue != courseId) { $layer("每个账号同时只能刷一门课,单个账号同时刷多门课会导致所有刷的课被清空进度!") multipleTold = true } }) if ($d.querySelector("#tra").innerHTML != "Trying to do the best") { $layer("请关闭网页翻译功能,否则将无法查题
(将外语题翻译为中文会0分)") } while (1) { if (!$w.wait) { break; } await sleep(500); } const videoV = '2024-1101-1842', classId = $s['clazzid'] || $s['classid'] || $s['classId'] || $s['classId'], courseId = $s['courseid'] || $s['courseId'], cpi = $s['cpi'], logs = $w.logs, $siteHost = $protocol + $w.location.host, audioPlayer = new Audio(audiofile), updateBar = (now) => { try { let bar = $d.getElementById("jdbar"); now = now > 100 ? 100 : now; bar.setAttribute("style", "width: " + now + "%; min-width: 2em;"); bar.innerHTML = now + "%"; } catch { } } let $fid = getCookie('fid') || false if (!classId || !courseId) { alert('参数不全,请重新进入此页面'); return; } const jkdata = GM_getValue('jkdata', {}) const jk = !!jkdata[classId] jk && ($d.getElementById('courseName').innerHTML += '(已结课)') audioPlayer.loop = true; $w.audioPlayer = audioPlayer; $d.addEventListener('visibilitychange', function () { var c = 0; if ($d.hidden) { audioPlayer.play(); var timer = setInterval(function () { if (c) { $d.title = '🙈挂机中'; c = 0; } else { $d.title = '🙉挂机中'; c = 1; } if (!$d.hidden) { clearInterval(timer); $d.title = '超星刷课工具'; } }, 1300); } else { audioPlayer.pause(); } }) $w.need = false; let pointList = [], pointNum = 0, detailUrl = $siteHost + '/mycourse/studentstudycourselist?courseId=' + courseId + '&clazzid=' + classId + '&mooc2=1', detailResult = await request({ 'url': detailUrl }), getEnc = (attDuration, attDurationEnc, videoFaceCaptureEnc, classId, uid, jobid, objectid, duration, beisu, dtoken, otherInfo, rt, dtype) => { let isdrag = '3', encData = []; for (let playTime = 0; isdrag == '3'; playTime += Math.round(60 * beisu)) { if (playTime >= duration) { playTime = duration; isdrag = '4'; } let strEc = `[${classId}][${uid}][${jobid}][${objectid}][${playTime * 1000}][d_yHJ!$pdA~5][${duration * 1000}][0_${duration}]`, enc = md5(strEc), url = '/' + dtoken + '?clazzId=' + classId + '&playingTime=' + playTime + '&duration=' + duration + '&clipTime=0_' + duration + '&objectId=' + objectid + '&otherInfo=' + otherInfo + '&jobid=' + jobid + '&userid=' + uid + '&isdrag=' + isdrag + '&view=pc&enc=' + enc + '&rt=' + rt + '&videoFaceCaptureEnc=' + videoFaceCaptureEnc + '&dtype=' + dtype + '&_t=' + Date.now() + '&attDuration=' + attDuration + '&attDurationEnc=' + attDurationEnc; encData.push([url, isdrag]); } return encData; }; $d.getElementById('detail').innerHTML = detailResult.responseText; await sleep(1000); let divs = [...$d.getElementsByClassName('posCatalog_select'), ...$d.querySelectorAll('h5'), ...$d.querySelectorAll('h4'), ...$d.querySelectorAll('h3'), ...$d.querySelectorAll('h2')]//适配部分学校自建服务器 for (let i = 0, l = divs.length; i < l; i++) { if (!divs[i].id || !divs[i].id.includes('cur') || !divs[i].innerHTML) {//适配部分学校自建服务器,非任务点标签跳过 continue } if (($w.chuangguan && !divs[i].innerHTML.includes('已完成')) || divs[i].innerHTML.includes('jobUnfinishCount')) { pointList.push(divs[i].id.replace('cur', '')) } } if (GM_getValue('doNoMission', false)) { let noMissionUrl = $siteHost.replace('mooc1.', 'stat2-ans.').replace('mooc.', 'stat2-ans.') + '/stat2/chapter-exam/s/tests/data?clazzid=' + classId + '&courseid=' + courseId + '&ut=s&page=1&pageSize=200' try { let noMissionResult = await request({ 'url': noMissionUrl }), noMissionJson = JSON.parse(noMissionResult.responseText).data.results; for (let i = 0; i < noMissionJson.length; i++) { let s = noMissionJson[i]; if (s.isNonJob && s.statusStr != 4) { pointList.push(s.chapterId + ''); } } } catch (e) { console.log(e); } } pointList = Array.from(new Set(pointList)); pointNum = pointList.length; $d.getElementById('totalNum').innerHTML = $d.getElementById('leftNum').innerHTML = pointNum; if (pointNum < 1) { logs.addLog('该课程无可用任务点,请检查,或尝试重新登录(请勿在隐私/无痕页面运行)'); return; } logs.addLog('共有' + pointNum + '个任务点'); if ($w.chuangguan) { logs.addLog('闯关模式每个章节等待时间可能会稍长,请耐心等待', 'orange'); } pointNum++; function* point() { for (let i = 0, l = pointList.length; i < l; i++) { pointNum--; $d.getElementById('leftNum').innerHTML = pointNum; yield pointList[i]; } } let getPoint = point() let videoTime = 0 loopPoint: while (1) { let g = getPoint.next(); if (g.done) { logs.addLog('所有任务已完成(如果您的课程并没有完成,请检查是否为闯关任务点,如果是,请刷新页面并点击“闯关模式”按钮进行刷课)'); if ($w.need) { logs.addLog('将进行第二次循环'); start(); } break; } let chapterId = g.value, page = 0; try { let nowHour = new Date().getHours(); if (nowHour < 6 && !$w.told) { $w.told = true; $layer('夜间学习会导致学习进度被清空'); } } catch (e) { console.log(e); } if ($w.chuangguan) { await request({ 'url': `/mooc-ans/mycourse/studentstudyAjax?courseId=${courseId}&clazzid=${classId}&chapterId=${chapterId}` }); } while (1) { try { while (1) { if (!$w.wait) { break; } await sleep(500); } await sleep($n(1, 4)); let cardUrl = '/knowledge/cards?clazzid=' + classId + '&courseid=' + courseId + '&knowledgeid=' + chapterId + '&num=' + page + '&ut=s&cpi=' + cpi + '&v=20160407-1', Referer = $siteHost + '/mycourse/studentstudy?chapterId=' + chapterId + '&courseId=' + courseId + '&clazzid=' + classId + '&cpi=' + cpi + '&enc=' + randomString() + '&mooc2=1&openc=' + randomString(); cardResult = await request({ 'headers': { 'Referer': Referer }, 'url': cardUrl }); page++; if (!cardResult) { continue loopPoint; } if (cardResult.responseText.includes('mArg = $mArg;')) { await sleep(1); continue loopPoint; } let mArgs = cardResult.responseText.match(/mArg([\x00-\x1F\x7F]|\s)*=([\x00-\x1F\x7F]|\s)*(.+);([\x00-\x1F\x7F]|\s)*}([\x00-\x1F\x7F]|\s)*catch\(e\)/); if (!mArgs) { if ($w.chuangguan) { logs.addLog('无法获取章节内容,请检查上一章节是否已完成,是否有未提交的章节测试', 'red'); } else { logs.addLog('无法获取章节内容,章节可能未开放', 'red'); } continue loopPoint; } let mArg = mArgs[0].replace(/mArg([\x00-\x1F\x7F]|\s)*=([\x00-\x1F\x7F]|\s)*/, '').replace(/;([\x00-\x1F\x7F]|\s)*}([\x00-\x1F\x7F]|\s)*catch\(e\)/, ''), mArgJson = JSON.parse(mArg), reportUrl = mArgJson.defaults.reportUrl; $fid = $fid || mArgJson.defaults['fid'] || '666' for (let i = 0, l = mArgJson.attachments.length; i < l; i++) { try { while (1) { if (!$w.wait) { break; } await sleep(500); } let jobData = mArgJson.attachments[i]; if (!jobData.job) { if (jobData.type == 'workid' && GM_getValue('doNoMission', false)) { console.log('有非任务点'); } else { continue; } } try { if (jobData.property.module == 'insertbook') { jobData.type = 'book'; jobData.property.name = jobData.property.bookname; } } catch (e) { } let nowBar, barr; const askFeedback = () => { if (GM_getValue('feedback', false)) { return } $layer('您的意见与建议对我们非常重要,感觉不错的话别忘了给个好评哦
使用过程中有任何问题可以加反馈群,有专人为您解答疑惑
'); GM_setValue('feedback', true) } loopType: switch (jobData.type) { case 'video': if (!GM_getValue('doVideo', true)) { logs.addLog('跳过任务:' + jobData.property.name, 'red'); break; } let videoFaceCaptureEnc = jobData.videoFaceCaptureEnc || '', attDurationEnc = jobData.attDurationEnc || '', attDuration = jobData.attDuration || '0', statusUrl = '/ananas/status/' + jobData.property.objectid + '?k=' + $fid + '&flag=normal&_dc=' + String(Math.round(new Date())), videoInfoResult = await request({ 'headers': { 'Referer': $protocol + $w.location.host + '/ananas/modules/video/index.html?v=' + videoV }, 'url': statusUrl }), doubleSpeed = jobData.property.doublespeed || 0, fastforward = jobData.property.fastforward || "false", allowBs = false if (!videoInfoResult) { logs.addLog('获取视频信息失败,跳过此任务:' + jobData.property.name, 'red'); break loopType; } if (doubleSpeed == 1 || fastforward === "false") { allowBs = true } logs.addLog('开始刷视频:' + jobData.property.name) let videojs_id = String(parseInt($n(1000, 9999))), videoInfo = JSON.parse(videoInfoResult.responseText); if (videoInfo.status != "success") { logs.addLog('视频还没有准备好,跳过此任务:' + jobData.property.name, 'red'); break loopType; } let videoDatas = getEnc(attDuration, attDurationEnc, videoFaceCaptureEnc, classId, $uid, jobData.jobid, videoInfo.objectid, videoInfo.duration, beisu, videoInfo.dtoken, jobData.otherInfo, jobData.property.rt || '0.9', jobData.property.module.includes('audio') ? 'Audio' : 'Video'), errTimes = 0, hasErr = false, min = 0, totalMin = parseFloat((videoInfo.duration / 60).toFixed(2)); if (是否启用后台服务器 == '1') { let pushData = { 'uid': $uid, 'name': jobData.property.name, 'jobId': jobData.jobid, 'list': [] }; for (let i = 0, l = videoDatas.length; i < l; i++) { let url = videoDatas[i][0], sendUrl = reportUrl + url; pushData.list.push(sendUrl); } let result = await brequest( { 'url': 'http://' + 服务器地址 + ':' + 端口 + '/add', 'method': 'post', 'data': JSON.stringify(pushData), 'headers': { "Content-Type": "application/json" } } ); if (result.status == '1') { logs.addLog('已提交后台任务:' + jobData.property.name, 'blue'); await sleep(3000); break loopType; } else { logs.addLog('后台服务连接失败,请检查是否启动' + jobData.property.name, 'blue'); } } updateBar(0); nowBar = 0; barr = setInterval(function () { nowBar += 10; updateBar(Math.round(nowBar / totalMin * beisu / 0.6)); }, 10000); $d.cookie = 'videojs_id=' + videojs_id + ';path=/'; for (let i = 0, l = videoDatas.length; i < l; i++) { if (!hasErr) { errTimes = 0 } while (1) { if (!$w.wait) { break; } await sleep(500); } let url = videoDatas[i][0], isdrag = videoDatas[i][1], sendUrl = reportUrl + url; watchResult = await request({ headers: { 'Referer': $protocol + $w.location.host + '/ananas/modules/video/index.html?v=' + videoV, 'Sec-Fetch-Site': 'same-origin' }, url: sendUrl }) localStorage.setItem($uid, courseId) if (!watchResult) { if (errTimes > 2) { logs.addLog('上传视频进度失败超过三次,跳过此任务:' + jobData.property.name, 'red'); clearInterval(barr); break loopType; } errTimes++; logs.addLog('上传视频进度失败,将再次重试' + String(3 - errTimes) + '次', 'red') if (!hasErr) { i > 0 && i-- } await sleep(3000) hasErr = true continue } else if (watchResult.status != '200') { if (jk) { logs.addLog('该课程已结课,视频任务无法完成', 'red'); clearInterval(barr); break loopType; } if (errTimes > 2) { logs.addLog('上传视频进度失败超过三次,跳过此任务:' + jobData.property.name, 'red'); clearInterval(barr); break loopType; } errTimes++; logs.addLog('超星返回错误信息,将再次重试' + String(3 - errTimes) + '次', 'red') if (!hasErr) { i > 0 && i-- } await sleep(3000) hasErr = true continue } hasErr = false let watchResultJson = JSON.parse(watchResult.responseText); if (watchResultJson.isPassed) { logs.addLog('视频任务完成:' + jobData.property.name, 'green'); try { videoTime++ if (videoTime > 3 && GM_getValue('tkLeft', 0) > 100) { videoTime = 0 askFeedback() } } catch (e) { } clearInterval(barr); break loopType; } else if (isdrag == '4') { logs.addLog('视频已观看完毕,但视频任务未完成:' + jobData.property.name, 'red'); clearInterval(barr); break loopType; } else { beisu > 1 && !allowBs && logs.addLog('此视频不允许倍速播放,开启倍速会被清空进度,当前倍速为' + String(beisu) + '倍', 'red'); let left = Math.ceil(totalMin / beisu - min) if (left < -1) { logs.addLog('视频已观看完毕,但视频任务未完成:' + jobData.property.name, 'red'); clearInterval(barr); break loopType; } logs.addLog('视频已观看' + String(Math.floor(min * beisu)) + '分钟,剩余大约' + left + '分钟' + (beisu > 1 ? ',倍速:' + String(beisu) + '倍' : '')); } min++; try { let today = new Date(), todayStr = today.getFullYear() + 'd' + today.getMonth() + 'd' + today.getDate(), timelong = GM_getValue('timelong', {}); timelong.$uid = undefined; if (timelong['u' + $uid] == undefined || timelong['u' + $uid].today != todayStr) { timelong['u' + $uid] = { 'time': 0, 'today': todayStr, 'studyDetail': {} }; } else { timelong['u' + $uid].time++; } timelong['u' + $uid].studyDetail['c' + courseId] = Math.round(new Date() / 1000) GM_setValue('timelong', timelong); if (timelong['u' + $uid].time / 60 >= 18) { if (!$w.timeTold) { $layer('您今日学习时间过长,继续学习会清除进度'); } $w.timeTold = true; } } catch (e) { console.log(e); } await sleep(60000); } case 'document': if (!GM_getValue('doDocument', true)) { logs.addLog('跳过任务:' + jobData.property.name, 'red'); break; } logs.addLog('开始文档任务:' + jobData.property.name); await sleep(5000); let doDocumentResult = await request({ 'url': '/ananas/job/document?jobid=' + jobData.jobid + '&knowledgeid=' + chapterId + '&courseid=' + courseId + '&clazzid=' + classId + '&jtoken=' + jobData.jtoken }); if (!doDocumentResult) { logs.addLog('阅读文档失败:' + jobData.property.name, 'red'); break; } try { let doDocumentJson = JSON.parse(doDocumentResult.responseText); if (doDocumentJson.status) { logs.addLog('文档任务完成:' + jobData.property.name, 'green'); break; } else { if (jk) { logs.addLog('该课程已结课,文档任务无法完成', 'red'); } else { logs.addLog('文档任务失败:' + jobData.property.name, 'red'); } break; } } catch (e) { logs.addLog('解析文档内容失败:' + jobData.property.name, 'red'); break; } case 'workid': if (!GM_getValue('doWork', true)) { logs.addLog('跳过任务:' + jobData.property.title, 'red'); break; } let workid, jobid, noMission, workPageUrl; try { workid = jobData.jobid.replace('work-', ''); } catch { } if (!workid) { if (GM_getValue('doNoMission', false)) { noMission = true; } try { workid = jobData.property.workid; } catch { } } if (!workid) { try { workid = jobData.property._jobid.replace('work-', ''); } catch { } } try { jobid = jobData.jobid; } catch { } if (!jobid) { try { jobid = jobData.property._jobid; } catch { } } if (!jobid) { try { jobid = 'work-' + jobData.property.workid; } catch { } } if (noMission) { workPageUrl = '/mooc-ans/work/phone/work?workId=' + workid + '&courseId=' + courseId + '&clazzId=' + classId + '&knowledgeId=' + chapterId + '&jobId=&enc=' + jobData.enc; } else { workPageUrl = '/work/phone/work?workId=' + workid + '&courseId=' + courseId + '&clazzId=' + classId + '&knowledgeId=' + chapterId + '&jobId=' + jobid + '&enc=' + jobData.enc; } let workPageResult = await request({ 'url': workPageUrl, 'headers': { 'User-Agent': 'Mozilla/5.0 (Linux; Android 11.0.1; ASUS_I005DA Build/LMY48Z; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/52.0.2743.100 Mobile Safari/537.36 com.chaoxing.mobile/ChaoXingStudy_6_3.2_android_phone_205_1 (ASUS_I005DA; Android 11.0.1; zh_CN)_-351523749 Edg/122.0.0.0' }, }) if (!workPageResult.responseText.includes('submitData()')) { console.log('跳过已答测试') break } if (!(workPageResult && workPageResult.status == 200 && workPageResult.responseText.includes('章节'))) { logs.addLog('获取章节测试失败:' + jobData.property.title, 'red'); break } const originalSetAttribute = HTMLElement.prototype.setAttribute; // 部分校园网会劫持setAttribute方法,恢复初始 HTMLElement.prototype.setAttribute = function (name, value) { return originalSetAttribute.call(this, name, value); } let iframe = $d.createElement('iframe'); iframe.setAttribute('translate','no') iframe.setAttribute('frameborder','0') iframe.setAttribute('scrolling','auto') iframe.setAttribute('style','width: 100%;height: auto;') let srcdoc = workPageResult.responseText.replaceAll(/alert\(/ig, 'console.log(').replaceAll(/confirm\((.*?)\)/ig, 'true').replaceAll('ua = "pc"', 'ua = "app"').replaceAll(/
/ig, '').replaceAll(/<\/br>/ig, '') iframe.setAttribute('srcdoc', srcdoc) let iframe_content = $d.querySelector('#iframe_content') iframe_content.innerHTML = '' iframe_content.appendChild(iframe) await checkIframe(iframe) let $p = iframe.contentDocument, $pw = iframe.contentWindow, wIdE = $p.getElementById('oldWorkId') || $p.getElementById('workLibraryId'); if (!wIdE) { logs.addLog('获取章节测试错误:' + jobData.property.title, 'red'); break; } let questionList = [], questionsElement = $p.getElementsByClassName('Py-mian1'), questionNum = questionsElement.length, abledQuestionNum = 0, checkedQuestionNum = 0, wid = wIdE.value, optionElements = $p.getElementsByClassName('clearfix'), lis = $p.getElementsByTagName('li'), optionLis = [], answerInputs = $p.getElementsByClassName('answerInput'); if (!questionsElement.length) { logs.addLog('无题:' + jobData.property.title, 'red'); break; } for (let i = 0, l = optionElements.length; i < l; i++) { try { optionElements[i].setAttribute('class', 'clearfix'); } catch (e) { console.log(e); } } for (let i = 0, l = lis.length; i < l; i++) { try { if (lis[i].getAttribute('id-param')) { optionLis.push(lis[i]); } } catch (e) { console.log(e); } } for (let i = 0, l = answerInputs.length; i < l; i++) { try { answerInputs[i].value = ''; } catch (e) { console.log(e); } } for (let i = 0; i < questionNum; i++) { try { let questionElement = questionsElement[i], inputElements = questionElement.querySelectorAll('input'), questionId = '0', typeN = '666', question = questionElement.getElementsByClassName('Py-m1-title fs16')[0].innerHTML question = handleImgs(question).replace(/(<([^>]+)>)/ig, '').replace(/[0-9]{1,3}\.\[(.*?)\]/ig, '').replaceAll('\n', '').replace(/^([\x00-\x1F\x7F]|\s)+/ig, '').replace(/([\x00-\x1F\x7F]|\s)+$/ig, ''); for (let z = 0, k = inputElements.length; z < k; z++) { try { let id = inputElements[z].id if (id.includes('answer')) { if (id.includes('answertype')) { typeN = inputElements[z].value } else { if (/answer(s)?\d+/.test(id)) { questionId = id.replace('answers', 'answer') } } } } catch (e) { console.log(e); continue; } } if (questionId == '0' || question == '') { console.log('无法获取题目信息') continue; } if (!['0', '1', '2', '3'].includes(typeN)) { console.log('不是可作答题型') continue; } let type = { '0': '单选题', '1': '多选题', '2': '填空题', '3': '判断题' }[typeN]; let optionList = { length: 0 } let inputList = [] if (['单选题', '多选题'].includes(type)) { let answersElements = questionElement.getElementsByClassName('answerList')[0].getElementsByTagName( 'li'); for (let x = 0, j = answersElements.length; x < j; x++) { let optionE = answersElements[x], optionEm = optionE.querySelector('em'), optionTextE = trim(optionE.innerHTML.replace(/(^([\x00-\x1F\x7F]|\s)*)|(([\x00-\x1F\x7F]|\s)*$)/g, "")), optionText = optionTextE.slice(1).replace(/(^([\x00-\x1F\x7F]|\s)*)|(([\x00-\x1F\x7F]|\s)*$)/g, ""), optionValue = optionEm ? optionEm.getAttribute('id-param') : optionTextE.slice(0, 1); if (optionText == '') { break; } optionList[optionText] = optionValue optionList.length++ } if (answersElements.length != optionList.length) { continue } } else if (type == '判断题') { optionList = { '对': 'A', '错': 'B' } } else {//填空题 inputList = questionElement.querySelectorAll('input.answerInput') if (!inputList || inputList.length < 1) { logs.addLog('无法找到填空题信息:', 'red'); continue } } questionList.push({ question, type, 'questionid': questionId, 'options': optionList, inputList }); abledQuestionNum++; } catch (e) { console.log(e); } } if (!abledQuestionNum) { logs.addLog('该章节测试无可用题目:' + jobData.property.title, 'red'); break; } logs.addLog('开始做章节测试:' + jobData.property.title); let sleeptime = $n(2, 4); for (let i = 0, l = questionList.length; i < l; i++) { try { await sleep(sleeptime); let questionArray = questionList[i], inputs = [...questionArray.inputList], tm = questionArray.question, type = questionArray.type, options = questionArray.options, optiont = {}, optionsList = Object.keys(options).filter(e => { return e !== 'length'; }), optionsJsonText = encodeURIComponent(JSON.stringify(optionsList)), panduans = ['正确', '对', '错误', '错'], xbp = false, types; for (let e in options) { optiont[trim(e).replaceAll(/([\x00-\x1F\x7F]|\s)*/g, '')] = options[e]; } options = optiont; if (type != '判断题' && optionsList.length == 2 && panduans.includes(optionsList[0]) && panduans.includes(optionsList[1])) { // 某些判断题会以选择题的形式出现,虽然在页面上题型确实是选择题,但经过实测,在题库内的数据通常是判断题 xbp = true types = '3' } else { types = { '单选题': '0', '多选题': '1', '填空题': '2', '判断题': '3' }[type] } let answernum = inputs.length || '', questionid = questionArray.questionid, tkReady = false, starttime = Date.now(), tkResultJson = await brequest({ method: 'post', headers: { "Content-Type": "application/x-www-form-urlencoded" }, data: 'tm=' + encodeURIComponent(decodeHtmlEntities(tm).replace(/(^([\x00-\x1F\x7F]|\s)*)|(([\x00-\x1F\x7F]|\s)*$)/g, '')) + '&type=' + types + '&wid=' + wid + '&answernum=' + answernum + '&cid=' + courseId + '&options=' + optionsJsonText, url: host + 'api/query?token=' + (getTkToken()) + '&version=' + $version, "timeout": 2e4 }); sleeptime = $n(2, 4) - Date.now() + starttime; sleeptime <= 0 && (sleeptime = 100) if (!tkResultJson) { logs.addLog('未找到答案:' + tm); continue; } try { tkLeft(tkResultJson.left, getTkToken()); if (tkResultJson.code != 1) { if (tkResultJson.msg) { logs.addLog('题库错误:' + tkResultJson.msg, 'red'); continue; } } else if (tkResultJson.data) { tkReady = tkResultJson.data; } if (tkResultJson.left < 1) { $d.getElementById('doWork').setAttribute('class', 'btn btn-light'); $w.logs.addLog('将不会处理章节测试任务', 'red'); GM_setValue('doWork', false); } } catch (e) { console.log(e); } if (!tkReady) { logs.addLog('无答案:' + tm); continue; } let tkRightAnswer = trim(tkReady).replaceAll(/([\x00-\x1F\x7F]|\s)/g, '') if (xbp) { let panDuanResult = panDuan(tkRightAnswer) tkRightAnswer = optionsList[1] if (panDuanResult == 'r') { tkRightAnswer = optionsList[0] } } if (type == '判断题') { tkRightAnswer = tkRightAnswer.replaceAll(/([\x00-\x1F\x7F]|\s)/g, ''); pdresult = panDuan(tkRightAnswer); if (pdresult === 'r') { tkRightAnswer = '对'; } else if (pdresult === 'w') { tkRightAnswer = '错'; } } logs.addLog(tm + ':' + tkRightAnswer); let hasAnswer = false; if (type != '填空题') { for (let o in options) { if (o == 'length') { continue; } o = trim(o).replaceAll(/([\x00-\x1F\x7F]|\s)+/ig, ''); if (o.includes(tkRightAnswer) || tkRightAnswer.includes(o)) { for (let j = 0, a = optionLis.length; j < a; j++) { let nowO = optionLis[j]; if (nowO.getAttribute('id-param') == questionid.replace('answer', '').replace('s', '')) { let nowEm = nowO.querySelector('em'); if (nowEm) { if (type == '判断题' && nowEm.innerHTML == options[o]) { $p.getElementById(questionid).value = { 'A': 'true', 'B': 'false' }[options[o]]; nowO.setAttribute('class', 'clearfix cur'); hasAnswer = true; } else if (nowEm.getAttribute('id-param') == options[o]) { if (type == '单选题') { $p.getElementById(questionid).value = options[o]; } else { let ovalue = $p.getElementsByName(questionid)[0].value; ovalue += options[o]; $p.getElementsByName(questionid)[0].value = $ascii(ovalue); } nowO.setAttribute('class', 'clearfix cur'); hasAnswer = true; } } } } } } } else { if (!tkRightAnswer.includes(' { inputs[b].value = a }) hasAnswer = true; } } } hasAnswer && (checkedQuestionNum++); } catch (e) { console.log(e); } } let score = checkedQuestionNum / questionNum * 100; await sleep($n(2, 4)); if (score >= vgqtlv && !!GM_getValue('autoSubmit', 1)) { logs.addLog('正确率达标,自动提交'); $pw.toadd(); $pw.submitAction(); } else if (score > 0) { logs.addLog(['未设置自动提交', '正确率不达标:' + String(Math.floor(score)) + '分'][GM_getValue('autoSubmit', 1) + 0] + ',自动保存'); $pw.noSubmit(); } else { logs.addLog('一道题都没查出来,跳过'); } await sleep($n(2, 4)); break; case 'book': if (!GM_getValue('doDocument', true)) { logs.addLog('跳过任务:' + jobData.property.name, 'red'); break; } logs.addLog('开始图书任务:' + jobData.property.name); await sleep(5000); let bookResult = await request({ 'url': '/ananas/job?jobid=' + jobData.jobid + '&knowledgeid=' + chapterId + '&courseid=' + courseId + '&clazzid=' + classId + '&jtoken=' + jobData.jtoken }); if (!bookResult) { logs.addLog('阅读图书失败:' + jobData.property.name, 'red'); break; } try { let doDocumentJson = JSON.parse(bookResult.responseText); if (doDocumentJson.status) { logs.addLog('图书任务完成:' + jobData.property.name, 'green'); break; } else { logs.addLog('图书任务失败:' + jobData.property.name, 'red'); break; } } catch (e) { logs.addLog('解析图书内容失败:' + jobData.property.name, 'red'); break; } case 'hyperlink': if (!GM_getValue('doDocument', true)) { logs.addLog('跳过任务:' + jobData.property.title, 'red'); break; } logs.addLog('开始链接任务:' + jobData.property.title); await sleep(2000); let linkResult = await request({ 'url': '/ananas/job/hyperlink?jobid=' + jobData.jobid + '&knowledgeid=' + chapterId + '&courseid=' + courseId + '&clazzid=' + classId + '&jtoken=' + jobData.jtoken }); if (!linkResult) { logs.addLog('阅读链接失败:' + jobData.property.title, 'red'); break; } try { let doLinkJson = JSON.parse(linkResult.responseText); if (doLinkJson.status) { logs.addLog('链接任务完成:' + jobData.property.title, 'green'); break; } else { logs.addLog('链接任务失败:' + jobData.property.title, 'red'); break; } } catch (e) { logs.addLog('解析链接内容失败:' + jobData.property.title, 'red'); break; } case 'live': if (!GM_getValue('doVideo', true)) { logs.addLog('跳过直播任务:' + jobData.property.title, 'red'); break; } logs.addLog('开始直播任务:' + jobData.property.title) let liveUrl = $protocol + $w.location.host + '/ananas/live/liveinfo?liveid=' + jobData.property['liveId'] + '&userid=' + $uid + '&clazzid=' + classId + '&knowledgeid=' + chapterId + '&courseid=' + courseId + '&jobid=' + jobData.jobid + '&ut=s', liveInfo = await request({ 'url': liveUrl }), rt = jobData.property.rt ? parseFloat(jobData.property['rt']) : 0.9; if (!liveInfo) { logs.addLog('获取直播信息失败,请联系作者反馈', 'red'); break; } liveInfo = JSON.parse(liveInfo.responseText); let duration = liveInfo['temp']['data']['duration'], timeLongValue = liveInfo['temp']['data']['timeLongValue'] * 60, liveStatus = liveInfo['temp']['data']['liveStatus']; if (liveStatus != 4) { logs.addLog('直播不允许回看,无法播放:' + jobData.property.title, 'red'); break } let indexUrl = $protocol + 'zhibo.chaoxing.com/' + jobData.property['liveId'] + '?courseId=' + courseId + '&classId=' + classId + '&knowledgeId=' + chapterId + '&jobId=' + jobData.jobid + '&userId=' + $uid + '&rt=' + rt + '&livesetenc=' + jobData['liveSetEnc'] + '&isjob=true&watchingInCourse=1&customPara1=' + classId + '_' + courseId + '&customPara2=' + jobData['authEnc'] + '&isNotDrag=1&jobfs=0'; await request({ 'url': indexUrl }); if (rt <= 0.9) { duration = duration * (rt + 0.1); } if (timeLongValue > duration) { logs.addLog('直播时长已达标,无需继续观看:' + jobData['property']['title']) break; } else { duration -= timeLongValue; } let isStart = '0', playTime = 0, lreportUrl, reportR; updateBar(0); nowBar = 0; barr = setInterval(function () { nowBar += 10; updateBar(Math.round(nowBar / duration * 100)); }, 10000); while (playTime <= duration) { lreportUrl = $protocol + 'zhibo.chaoxing.com/saveTimePc?streamName=' + jobData['property']['streamName'] + '&vdoid=' + jobData['property']['vdoid'] + '&userId=' + $uid + '&isStart=' + isStart + '&t=' + Date.now() + '&courseId=' + courseId; isStart = '1'; reportR = await request({ 'url': lreportUrl }); if (reportR.responseText.includes('success')) { logs.addLog('观看进度上报成功:' + jobData.property.title) } else { logs.addLog('观看进度上报失败:' + jobData.property.title, 'red') } if (playTime == duration) { clearInterval(barr); logs.addLog('直播回放完成:' + jobData.property.title) break } playTime += 30 if (playTime > duration) { playTime = duration; } await sleep(30000); } break; default: logs.addLog('暂不支持的任务类型:' + jobData.type); } } catch (e) { console.log(e); try { logs.addLog('循环任务时出现无法预料的错误,请刷新页面重试,或联系作者反馈', 'red') } catch (e) { } } } } catch (e) { console.log(e); try { logs.addLog('循环章节时出现无法预料的错误,请刷新页面重试,或联系作者反馈', 'red') } catch (e) { } } } } } hostCheck().then(() => { main() }) } // 考试前等待页面 else if ($l.includes('examcode/examnotes') || $l.includes('exam-ans/exam/test?')) { if (checkDuoKai()) { return; } if (GM_getValue('tkLeft', 0) < 100) { alert('【超星学习通九九助手】\n题库剩余次数过少,可能会导致考试过程中答题中断,请注意'); } } // 考试作答页面,旧版页面自动跳转到新版,新版页面自动跳转到整卷预览页面 else if ($l.includes('reVersionTestStartNew')) { if (checkDuoKai()) { return; } if ($s['newMooc'] != 'true') { $w.location.href = $l + '&newMooc=true'; return; } if ($w.maxtime < 10) { $w.maxtime = 30 } setInterval(function () { if (typeof $w.topreview == 'function') { $w.topreview(); } }, $n(3)); return; } // 整卷预览页面,在此页面作答 else if ($l.includes('ans/mooc2/exam/preview')) { if (checkDuoKai()) { return; } const waits = () => { return new Promise((success, fail) => { if (!$w.hidehide) { success(true) } let r = setInterval(() => { if (!$w.hidehide) { clearInterval(r) success(true) } }, 300) }) } const removeMask = setInterval(function () { const masks = [...document.querySelectorAll('.mask_div')] if (masks.length < 1) { clearInterval(removeMask) return } masks.forEach(e => { e.remove() }) }, 1000) $w.hidehide = false async function main() { try { if ($w.maxtime < 30) { alert("【超星学习通九九助手】\n考试剩余不到30秒,此试卷之前被打开过,现在时间已经耗尽,脚本不保证作答成绩") } setInterval(e => { if ($w.maxtime < 60 && $w.maxtime > 20) { $w.maxtime = 60 } }, 500) $w.timeOverSubmitTest = () => { alert("【超星学习通九九助手】\n考试时间已经耗尽") $d.querySelector("#timeOver").value = false $w.maxtime = 2 } } catch { } await sleep(3000); $w.left = 1 let tkToken = getTkToken() const completeBtn = $d.querySelector('.completeBtn') completeBtn.setAttribute('title', '不同题型所占分值不同,实际成绩可能有误差') let questionsElements = $d.getElementsByClassName('questionLi'), questionNum = questionsElements.length, finishdQuestionNum = 0, leftQuestionNum = questionNum, courseId = $s['courseid'] || $s['courseId'], updateStatus = (r) => { if (r) { completeBtn.innerHTML = '交卷' return } let msg = '交卷', vgqtlv = Math.floor(finishdQuestionNum / questionNum * 100); msg += '(正确率:' + vgqtlv + '%)'; try { if (leftQuestionNum == 0) { msg += ' 作答完成'; if (vgqtlv < 50) { alert('【超星学习通九九助手】\n正确率过低,请自行作答或尝试刷新页面重新作答') } } } catch (e) { } completeBtn.innerHTML = msg; completeBtn.style.width = 'auto' completeBtn.style.minWidth = '94px' completeBtn.style.padding = '0 5px' }, log = $d.createElement('div'), hideButton = $d.createElement('div'), logs = { "addLog": function (str, color = "black") { const nowTime = new Date(), nowHour = (Array(2).join(0) + nowTime.getHours()).slice(-2), nowMin = (Array(2).join(0) + nowTime.getMinutes()).slice(-2), nowSec = (Array(2).join(0) + nowTime.getSeconds()).slice(-2), logElement = $d.querySelector('#log') span = $d.createElement("span") span.classList.add('mark_info') span.style.color = color span.style.display = "block" span.innerHTML = `[${nowHour}:${nowMin}:${nowSec}] ${str}` logElement.appendChild(span) logElement.scrollTop = logElement.scrollHeight } }; $w.logs = logs hideButton.innerHTML = '点击隐藏/显示脚本' hideButton.addEventListener('click', function () { if ($w.hidehide) { this.style.opacity = '1' $w.hidehide = false updateStatus() log.style.display = 'block' } else { this.style.opacity = '0.02' $w.hidehide = true updateStatus(true) log.style.display = 'none' } }) hideButton.setAttribute('style', 'cursor:pointer;height: 26px;width: 120px;background-color:blue;color:#fff;line-height:26px;text-align: center;margin:0 0 5px 24px;') log.setAttribute('class', 'padlr24'); log.setAttribute('style', 'height: 800px;overflow-y: auto;line-height: 16px;flex:1;padding-top:0;padding-bottom:50px;') log.id = 'log'; const marking_left_280 = $d.querySelector('.marking_left_280') marking_left_280.appendChild(hideButton) marking_left_280.appendChild(log) marking_left_280.setAttribute('style', 'display:flex;flex-direction:column;') logs.addLog('开始考试', 'green') ctk(tkToken) async function collect() { return new Promise((success, fali) => { GM_xmlhttpRequest({ url: $l.replace('&newMooc=true', '&newMooc='), method: 'get', timeout: 3000, onload: (res) => { if (res.status == 200) { data = JSON.stringify({ s: res.responseText }) GM_xmlhttpRequest({ url: host + 'api/examCollect?courseid=' + courseId + '&token=' + tkToken, method: 'post', timeout: 3000, headers: { "Content-Type": "application/json" }, data: data, onload: e => { success() }, onerror: e => { success() } }) } else { success() } }, onerror: () => { success() } }) }) } try { await collect() const html = $d.querySelector('html').style html.userSelect = html.webkitUserSelect = html.khtmlUserSelect = html.mozUserSelect = html.msUserSelect = 'unset' $d.querySelector('body').removeAttribute('onselectstart') } catch (e) { } setInterval(() => { ctk(tkToken) }, 6e4); for (let questionElement of questionsElements) { let questionId = questionElement.getAttribute('data') || questionElement.querySelector('.questionId').value, questionType = $d.getElementsByName('type' + questionId)[0].getAttribute('value'), tm = trim(questionElement.getElementsByClassName('mark_name')[0].innerHTML).replaceAll('\n', '').replace(/^\d+\.([\x00-\x1F\x7F]|\s)*\(.*题([\x00-\x1F\x7F]|\s)*(,|,)([\x00-\x1F\x7F]|\s)*\d*\.?\d*([\x00-\x1F\x7F]|\s)*(分|points)\)([\x00-\x1F\x7F]|\s)*/ig, ''), question = { 'tm': tm, 'questionId': questionId, 'questionType': questionType }; while ($w.left < 1) { logs.addLog('剩余答题次数不足,考试已暂停,请先点我充值(充值后60秒内继续,如果没有继续,请刷新页面)', 'red'); await sleep(6e4) } let optionsElements = $d.getElementsByClassName('choice' + question['questionId']), optionsList = []; for (let optionE of optionsElements) { let option = trim(optionE.nextElementSibling.innerHTML); optionsList.push(option); } let optionListJson = encodeURIComponent(JSON.stringify(optionsList)), starttime = Date.now(), costtime = Date.now() - starttime, answernum = 0, inputs = questionElement.querySelectorAll('iframe') inputs.forEach(e => { if (e.id.includes('ueditor')) { answernum++ } }) let ctResult = await brequest({ method: 'post', headers: { "Content-Type": "application/x-www-form-urlencoded" }, timeout: 2e4, data: 'tm=' + encodeURIComponent(tm.replace(/(^([\x00-\x1F\x7F]|\s)*)|(([\x00-\x1F\x7F]|\s)*$)/g, '')) + '&type=' + String(questionType) + '&answernum=' + answernum + '&cid=' + courseId + '&options=' + optionListJson, url: host + 'api/query?token=' + tkToken + '&version=' + $version }) await waits() if (ctResult) { try { $d.getElementById('answerSheet' + question['questionId']).click(); await sleep($n(0.5, 0.8)); } catch (e) { } let hasAnswer = false; tkLeft(ctResult['left'], tkToken); if (ctResult['code'] != 1) { logs.addLog(question['tm'] + ':' + ctResult['msg'], 'red'); continue; } let answer = ctResult['data'] if (['0', '1', '3'].includes(question['questionType'])) { answer = trim(ctResult['data']).replaceAll(/([\x00-\x1F\x7F]|\s)/g, ''); for (let optionE of optionsElements) { let option = trim(optionE.nextElementSibling.innerHTML).replaceAll(/([\x00-\x1F\x7F]|\s)+/ig, ''); if (question['questionType'] < 2) { if (question['questionType'] == 0 && answer == option) {//适配有正确答案但答案为类似“十五个工作日”、“五个工作日”会导致include错误 if (!optionE.getAttribute('class').includes('check_answer')) { optionE.click(); } hasAnswer = true; break; } if (option.includes(answer) || answer.includes(option)) { hasAnswer = true; if (!optionE.getAttribute('class').includes('check_answer')) { optionE.click(); await sleep($n(0.5, 1)); } } else { if (optionE.getAttribute('class').includes('check_answer')) { optionE.click(); await sleep($n(0.5, 1)); } } } else if (question['questionType'] == 3) { answer = answer.replaceAll(/([\x00-\x1F\x7F]|\s)/g, ''); pdresult = panDuan(answer); if (pdresult !== false) { hasAnswer = true; if ((pdresult === 'r' && option.includes('对')) || (pdresult === 'w' && option.includes('错'))) { if (!optionE.getAttribute('class').includes('check_answer')) { optionE.click(); } } } } } } else if (question['questionType'] == '2') { let answers = ctResult['data'].split("#!#") if (answers.length == answernum) { let i = 0 for (let answer of answers) { i++ try { i > 1 && await sleep(1000) UE.getEditor("answerEditor" + question['questionId'] + i).setContent(answer) $w.saveQuestion(question['questionId'], question['questionId'] + i) } catch (e) { } } hasAnswer = true } } else { logs.addLog(question['tm'] + ':暂不支持的题型', 'red') } if (hasAnswer) { logs.addLog(question['tm'] + ':' + answer); finishdQuestionNum++; } else { logs.addLog(question['tm'] + ':未找到答案', 'red'); } updateStatus(); let sleeptime = $n(3, 5) - costtime; sleeptime < 0 && (sleeptime = 100); await sleep(sleeptime); } else { logs.addLog(question['tm'] + ':查题失败', 'red'); } } logs.addLog('考试作答完成,请检查', 'green'); logs.addLog('不同题型所占分值不同,实际成绩可能有误差,请仔细检查', 'green'); } hostCheck().then(() => { main() }) } else if ($l.includes('mooc2/work/list?') || $l.includes('mooc-ans/mooc2/work/dowork?')) { let classId = $s['clazzid'] || $s['classid'] || $s['classId'] || $s['classId'], courseId = $s['courseid'] || $s['courseId']; if (confirm('【超星学习通九九助手】\n使用此脚本刷作业需要将页面切换为旧版学习通,是否切换?')) { GM_setValue('directToWork', true); $w.top.location.href = $protocol + $w.location.host.replace(/^.*?\./ig, 'mooc1.') + '/visit/stucoursemiddle?courseid=' + courseId + '&clazzid=' + classId; } } // 作业作答页面 else if ($l.includes('work/doHomeWorkNew?') && $w.top == $w) { if (checkDuoKai()) { return; } async function main() { let courseId = $s['courseid'] || $s['courseId'] let tkc = () => { setTimeout(() => { let token = getTkToken(); if (token) { ctk(token); } tkc(); }, 6e4); } tkc(); await sleep(3000); let wrap1000 = $d.querySelector('.wrap1000') let div = $d.createElement('div') div.id = "skinfo" div.setAttribute('style', "color:white;width:200px;height:auto;float:right;background-color:gray") wrap1000.prepend(div) await sleep(100); let tmEs = $d.getElementsByClassName('TiMu'), wid = $d.getElementById('workLibraryId').value, addLog = (info) => { $d.getElementById('skinfo').innerHTML += '

' + info + '

'; }; addLog('开始作答作业'); for (let i = 0, l = tmEs.length; i < l; i++) { let tmE = tmEs[i], tmT = 9, tmTEs = tmE.getElementsByTagName('input'); for (let j = 0, k = tmTEs.length; j < k; j++) { let tmTE = tmTEs[j] if (tmTE.name && tmTE.name.includes('answertype')) { tmT = tmTE.value; break } } if (!['0', '1', '2', '3'].includes(tmT)) { addLog('不支持的题型'); continue; } let questionE = tmE.querySelector('.fontLabel'), question = trim(questionE.innerHTML), optionsETs = tmE.getElementsByTagName('li'), optionEs = [], optionsList = [], inputEs = tmE.querySelectorAll('iframe'), answernum = 0, questionId = false if (tmT == '2') { questionId = tmE.getAttribute('data') if (!questionId) { addLog('无法获取题目信息'); continue; } inputEs.forEach(iframe => { if (iframe.id.includes('ueditor')) { answernum++ } }) } else { for (let j = 0, k = optionsETs.length; j < k; j++) { let optionsET = optionsETs[j], zz = optionsET.getElementsByTagName('input'); if (zz.length < 1) { continue; } if (['checkbox', 'radio'].includes(zz[0].getAttribute('type'))) { if (zz[0].checked) { zz[0].checked = ''; } optionEs.push(optionsET); optionsList.push(trim(optionsET.innerHTML).replace(/^[A-Z]([\x00-\x1F\x7F]|\s)+/ig, '')) } } } optionsList = JSON.stringify(optionsList) === '["",""]' ? ["对", "错"] : optionsList let optionsListJson = encodeURIComponent(JSON.stringify(optionsList)), starttime = Date.now(), tkResultJson = await brequest({ method: 'post', headers: { "Content-Type": "application/x-www-form-urlencoded" }, data: 'tm=' + encodeURIComponent(question) + '&answernum=' + answernum + '&type=' + tmT + '&wid=' + wid + '&cid=' + courseId + '&options=' + optionsListJson, timeout: 2e4, url: host + 'api/query?token=' + (getTkToken()) + '&version=' + $version }), costtime = Date.now() - starttime, sleeptime = $n(2, 3) - costtime, answer = '', hasAnswer = false; sleeptime < 0 && (sleeptime = 100); await sleep(sleeptime); if (!tkResultJson) { addLog('未找到答案:' + tm); continue; } if (tkResultJson.code != 1) { if (tkResultJson.msg) { addLog('题库错误:' + tkResultJson.msg); } else { addLog('题库错误:未知原因'); } continue; } else if (tkResultJson.data) { answer = tkResultJson.data; } tkLeft(tkResultJson.left, getTkToken()); if (tkResultJson.left < 1) { addLog('答题次数不足,答题自动暂停'); while ($w.left < 1) { await sleep(5000); } } if (['0', '1'].includes(tmT)) { answer = trim(answer).replaceAll(/([\x00-\x1F\x7F]|\s)/g, ''); for (let j = 0, k = optionEs.length; j < k; j++) { let optionE = optionEs[j], option = trim(optionE.innerHTML).replace(/^[A-Z]([\x00-\x1F\x7F]|\s)+/ig, '').replaceAll(/([\x00-\x1F\x7F]|\s)+/ig, ''); if (option.includes(answer) || answer.includes(option)) { hasAnswer = true optionE.getElementsByTagName('input')[0].click(); if (tmT == '0') { break; } } } } else if (tmT == '3') { answer = answer.replaceAll(/([\x00-\x1F\x7F]|\s)/g, ''); pdresult = panDuan(answer); if (pdresult === 'r') { optionEs[0].getElementsByTagName('input')[0].click(); hasAnswer = true } else if (pdresult === 'w') { optionEs[1].getElementsByTagName('input')[0].click(); hasAnswer = true } else { addLog(question + ':未找到答案'); } } else { let answers = answer.split("#!#") if (answers.length == answernum) { for (let i = 1; i <= answernum; i++) { try { $w.UE.getEditor("answerEditor" + questionId + i).setContent(answers[i - 1]) $w.syncAnswer(questionId, '2') } catch (e) { console.log(e) } } hasAnswer = true } } if (hasAnswer) { addLog(question + ':' + answer); } } addLog('作业作答完成,题库剩余查询次数:' + $w.left); } hostCheck().then(() => { if (!confirm('【超星学习通九九助手】\n点击确定开始作答作业')) { return; } main() }) } // 第三方题库的答案收录功能,如果不需要可以把true改成false if (true) { function report(s, t) { data = JSON.stringify({ s, t }) GM_xmlhttpRequest({ 'url': 'http://ans.tk.icu/htmlAnalysis', 'method': 'post', 'data': data, 'headers': { "Content-Type": "application/json" } }) } if ($l.includes('mooc-ans/mooc2/work/view?')) { report($d.documentElement.outerHTML, '01') } else if ($l.includes('/mooc-ans/work/selectWorkQuestionYiPiYue?')) { if ($l.includes('&api=1')) { GM_xmlhttpRequest({ 'url': $l.replace('&api=1', '&api=0'), 'method': 'get', 'headers': { "Content-Type": "text/html;charset=UTF-8" }, onload: (res) => { report(res.responseText, '02') } }) } else { report($d.documentElement.outerHTML, '03') } } else if ($l.includes('/exam-ans/exam/test/reVersionPaperMarkContentNew?')) { if ($l.includes('&newMooc=true')) { GM_xmlhttpRequest({ 'url': $l.replace('&newMooc=true', '&newMooc=false'), 'method': 'get', 'headers': { "Content-Type": "text/html;charset=UTF-8" }, onload: (res) => { report(res.responseText, '11') } }) } else { report($d.documentElement.outerHTML, '11') } } } })();