近日找到了某位大佬所寫的一個超星網課的刷課腳本,比較好用,特來分享一下,此腳本未經作者同意,切勿用作商業用途,否則后果自負。
1 // ==UserScript== 2 // @name 超星網課助手 3 // @version 2.1.0 4 // @description 自動掛機看爾雅MOOC,支持后台、切換窗口不暫停,視頻自動切換,屏蔽視頻內的題目,倍速播放、進度條拖動、快進快退 5 // @author wyn665817 6 // @match *://*.chaoxing.com/* 7 // @require https://greasyfork.org/scripts/18715/code/Hooks.js?version=661566 8 // @connect forestpolice.org 9 // @run-at document-end 10 // @grant unsafeWindow 11 // @grant GM_xmlhttpRequest 12 // @grant GM_setClipboard 13 // @supportURL https://greasyfork.org/zh-CN/scripts/369625/feedback 14 // @license MIT 15 // ==/UserScript== 16 17 // 設置修改后,需要刷新或重新打開網課頁面才會生效 18 var setting = { 19 // 5E3 == 5000,科學記數法,表示毫秒數 20 time: 5E3 // 默認響應速度為5秒,不建議小於3秒 21 ,token: '' // 捐助用戶可以使用上傳選項功能,更精准的匹配答案,此處填寫捐助后獲取的識別碼 22 23 // 1代表開啟,0代表關閉 24 ,video: 1 // 視頻支持后台、切換窗口不暫停,支持多視頻,默認開啟 25 ,work: 1 // 自動答題功能(章節測驗),高准確率,默認開啟 26 ,jump: 1 // 自動切換任務點、章節、課程(需要配置course參數),默認開啟 27 ,face: 0 // 解除面部識別,此功能僅為臨時解除,可能會導致不良記錄(慎用),默認關閉 28 ,login: 0 // 自動登錄,支持監測掉線並自動重連,需要配置詳細參數,默認關閉 29 30 // 僅開啟video時,修改此處才會生效 31 ,line: '公網1' // 視頻播放的默認資源線路,此功能適用於系統默認線路無資源,默認'公網1' 32 ,http: '' // 視頻播放的默認清晰度,可以設置'標清'等,無參數則使用系統默認清晰度,默認'' 33 ,muted: 0 // 視頻靜音播放,此功能在視頻開始播放時調整音量至靜音,默認關閉 34 ,drag: 0 // 倍速播放、進度條拖動、快進快退,使用此功能會出現不良記錄(慎用),默認關閉 35 ,player: '' // 指定播放器的類型,支持'html5'和'flash'兩種參數,其他參數代表系統默認播放器,默認'' 36 37 // 僅開啟work時,修改此處才會生效 38 ,auto: 1 // 答題完成后自動提交,默認開啟 39 ,none: 1 // 無匹配答案時執行默認操作,關閉后若題目無匹配答案則會停止本次自動提交,默認開啟 40 ,wait: 5E3 // 自動提交前的等待時間,用於更改自動答題的提交間隔,默認5秒 41 ,paste: 1 // 文本編輯器允許粘貼,用於解除文本類題目的粘貼限制,默認開啟 42 ,scale: 0 // 富文本編輯器高度自動拉伸,用於文本類題目,答題框根據內容自動調整大小,默認關閉 43 44 // 僅開啟jump時,修改此處才會生效 45 ,check: 1 // 任務點無法自動完成時暫停切換,如果網課已全部解鎖的建議關閉,默認開啟 46 ,course: 0 // 當前課程完成后自動切換課程,僅支持按照根目錄課程順序切換,建議同時配置check參數為0,默認關閉 47 48 // 僅開啟login時,修改此處才會生效,且必須設置以下參數 49 ,school: '' // 學校名稱,要求完整有效可查詢,例如'清華大學',默認'' 50 ,username: '' // 學號/工號/借書證號(郵箱/手機號/賬號),例如'2018010101',默認'' 51 ,password: '' // 密碼,例如'123456',默認'' 52 }, 53 _self = unsafeWindow, 54 top = _self.top, 55 $ = _self.$ || top.$, 56 Hooks = Hooks || window.Hooks, 57 UE = _self.UE, 58 url = location.pathname; 59 60 if (url == '/ananas/modules/video/index.html') { 61 if (setting.video) { 62 jobSort(); 63 checkPlayer(); 64 } else { 65 getIframe(0).remove(); 66 } 67 } else if (url == '/work/doHomeWorkNew') { 68 if (!_self.$) { 69 } else if (setting.work && $('.Btn_blue_1').length) { 70 jobSort(); 71 setTimeout(relieveLimit, setting.time / 2); 72 beforeFind(); 73 } else { 74 getIframe(0).remove(); 75 } 76 } else if (url == '/knowledge/cards') { 77 checkToNext(); 78 } else if (url == '/mycourse/studentcourse') { 79 goCourse(); 80 } else if (url == '/visit/courses') { 81 setting.face && DisplayURL(); 82 } else if (location.host.indexOf('passport2') == 0) { 83 setting.login && getSchoolId(); 84 } 85 86 function getIframe(tip, win, job) { 87 do { 88 win = win ? win.parent : _self; 89 job = $(win.frameElement).prev('.ans-job-icon'); 90 } while (!job.length && win.parent.frameElement); 91 return tip ? win : job; 92 } 93 94 function jobSort() { 95 var win = getIframe(1), 96 $job = $('.ans-job-icon', win.parent.document).next('iframe[src*="/video/index.html"], iframe[src*="/work/index.html"]').not('.ans-job-finished > iframe'); 97 setting.tip = false; 98 if (!$job.length) { 99 } else if ($job[0] == win.frameElement) { 100 setting.tip = true; 101 } else { 102 setInterval(function() { 103 if ($job.filter('.ans-job-icon + iframe').not('.ans-job-finished > iframe')[0] == win.frameElement) { 104 location.reload(); 105 } 106 }, setting.time); 107 } 108 } 109 110 function checkPlayer() { 111 var data = $.parseJSON($(frameElement).attr('data')), 112 danmaku = data && data.danmaku ? data.danmaku : 0; 113 if (setting.player == 'flash') { 114 _self.showHTML5Player = _self.showMoocPlayer; 115 danmaku = 1; 116 } else if (setting.player == 'html5') { 117 _self.showMoocPlayer = _self.showHTML5Player; 118 danmaku = 0; 119 } 120 if (!danmaku && _self.supportH5Video() && !navigator.userAgent.match(/metasr/i)) { 121 hookVideo(_self.videojs, _self.videojs.xhr); 122 } else if (_self.flashChecker().hasFlash) { 123 hookJQuery(); 124 } else { 125 alert("此瀏覽器不支持flash,請修改腳本player參數為'html5',或者更換瀏覽器"); 126 } 127 } 128 129 function hookVideo(Hooks, xhr) { 130 _self.videojs = function () { 131 var config = arguments[1], 132 line = $.grep($.map(config.playlines, function(value, index) { 133 return value.label == setting.line && index; 134 }), function(value) { 135 return $.isNumeric(value); 136 })[0] || 0, 137 http = $.grep(config.sources, function(value) { 138 return value.label == setting.http; 139 })[0]; 140 config.playlines.unshift(config.playlines[line]); 141 config.playlines.splice(line + 1, 1); 142 config.plugins.videoJsResolutionSwitcher.default = http ? http.res : 360; 143 config.plugins.studyControl.enableSwitchWindow = 1; 144 config.plugins.timelineObjects.url = '/richvideo/initdatawithviewer?'; 145 if (setting.drag) { 146 config.plugins.seekBarControl.enableFastForward = 1; 147 config.playbackRates = [1, 1.25, 1.5, 2]; 148 } 149 var player = Hooks.apply(this, arguments); 150 player.children_[0].muted = setting.muted; 151 player.on('loadstart', function() { 152 setting.tip && this.play().catch(function() {}); 153 }); 154 _self.videojs = Hooks; 155 _self.videojs.xhr = setting.login ? function(options, callback) { 156 return xhr.call(this, options, function(error, response) { 157 response.statusCode || top.location.reload(); 158 return callback.apply(this, arguments); 159 }); 160 } : xhr; 161 return player; 162 }; 163 } 164 165 function hookJQuery() { 166 Hooks.set(_self, 'jQuery', function(target, propertyName, ignored, jQuery) { 167 Hooks.method(jQuery.fn, 'cxplayer', function(target, methodName, method, thisArg, args) { 168 var config = args[0]; 169 config.datas.isDefaultPlay = setting.tip; 170 config.enableSwitchWindow = 1; 171 config.datas.currVideoInfo.resourceUrl = '/richvideo/initdatawithviewer?'; 172 config.datas.currVideoInfo.dftLineIndex = $.grep($.map(decodeURIComponent(config.datas.currVideoInfo.getVideoUrl).match(/{.+?}/g) || [], function(value, index) { 173 return value.indexOf(setting.line + setting.http) > -1 && index; 174 }), function(value) { 175 return $.isNumeric(value); 176 })[0] || 0; 177 setting.drag && (config.datas.currVideoInfo.getVideoUrl = config.datas.currVideoInfo.getVideoUrl.replace(/&drag=false&/, '&drag=true&')); 178 var $player = Hooks.Reply.method(arguments); 179 setting.muted && $player.one('onStart', function() { 180 for (var i = 0; i < 16; i++) { 181 $player.addVolNum(false); 182 } 183 }); 184 return $player; 185 }); 186 return Hooks.Reply.set(arguments); 187 }); 188 } 189 190 function relieveLimit() { 191 UE && setting.scale && (_self.UEDITOR_CONFIG.scaleEnabled = false); 192 UE && $('.edui-default + textarea').each(function() { 193 UE.getEditor($(this).attr('name')).ready(function() { 194 this.autoHeightEnabled = true; 195 setting.scale && this.enableAutoHeight(); 196 setting.paste && this.removeListener('beforepaste', _self.myEditor_paste); 197 }); 198 }); 199 if (!setting.paste) return; 200 $('input[onpaste]').removeAttr('onpaste'); 201 _self.myEditor_paste = function() {}; 202 // _self.pasteText = function() {return true}; 203 } 204 205 function beforeFind() { 206 setting.div = $( 207 '<div style="border: 2px dashed rgb(0, 85, 68); width: 330px; position: fixed; top: 0; right: 0; z-index: 99999; background-color: rgba(70, 196, 38, 0.6); overflow-x: auto;">' + 208 '<span style="font-size: medium;"></span>' + 209 '<div style="font-size: medium;">正在搜索答案...</div>' + 210 '<button style="margin-right: 10px;">暫停答題</button>' + 211 '<button style="margin-right: 10px;">' + (setting.auto ? '取消本次自動提交' : '開啟本次自動提交') + '</button>' + 212 '<button style="margin-right: 10px;">重新查詢</button>' + 213 '<button>折疊面板</button>' + 214 '<div style="max-height: 300px; overflow-y: auto;">' + 215 '<table border="1" style="font-size: 12px;">' + 216 '<thead>' + 217 '<tr>' + 218 '<th style="width: 60%; min-width: 130px;">題目</th>' + 219 '<th style="min-width: 130px;">答案</th>' + 220 '</tr>' + 221 '</thead>' + 222 '<tfoot style="display: none;">' + 223 '<tr>' + 224 '<th colspan="2">答案提示框 已折疊</th>' + 225 '</tr>' + 226 '</tfoot>' + 227 '<tbody>' + 228 '<tr>' + 229 '<td colspan="2" style="display: none;"></td>' + 230 '</tr>' + 231 '</tbody>' + 232 '</table>' + 233 '</div>' + 234 '</div>' 235 ).appendTo('body').on('click', 'button, td', function() { 236 var len = $(this).prevAll('button').length; 237 if (this.tagName == 'TD') { 238 GM_setClipboard($(this).text()); 239 } else if (len == 0) { 240 if (setting.loop) { 241 clearInterval(setting.loop); 242 delete setting.loop; 243 setting.div.children('div:eq(0)').text('已暫停搜索'); 244 $(this).text('繼續答題'); 245 } else { 246 setting.loop = setInterval(findAnswer, setting.time); 247 setting.div.children('div:eq(0)').text('正在搜索答案...'); 248 $(this).text('暫停答題'); 249 } 250 } else if (len == 1) { 251 setting.auto = 1 ^ setting.auto; 252 $(this).text(setting.auto ? '取消本次自動提交' : '開啟本次自動提交'); 253 } else if (len == 2) { 254 location.reload(); 255 } else if (len == 3) { 256 setting.div.find('tbody, tfoot').toggle(); 257 } 258 }); 259 setting.lose = setting.num = 0; 260 setting.curs = $('h1').text().trim() || $('script:contains(courseName)', top.document).text().match(/courseName:\'(.+?)\'/)[1]; 261 setting.loop = setInterval(findAnswer, setting.time); 262 setting.tip || setting.div.children('button').eq(0).click(); 263 } 264 265 function findAnswer() { 266 if (setting.num >= $('.TiMu').length) { 267 clearInterval(setting.loop); 268 var text = '答題已完成'; 269 if (setting.lose) { 270 setting.div.children('button').eq(1).hide(); 271 text = '共有 <font color="red">' + setting.lose + '</font> 道題目待完善(已深色標注)'; 272 } else { 273 setTimeout(submitThis, setting.wait); 274 } 275 setting.div.children('button').eq(0).hide(); 276 setting.div.children('div:eq(0)').html(text); 277 return; 278 } 279 var $TiMu = $('.TiMu').eq(setting.num), 280 question = $TiMu.find('.Zy_TItle .clearfix:eq(0)').text().trim(), 281 type = $TiMu.find('input[name^=answertype]:eq(0)').val(), 282 option = setting.token && $TiMu.find('.clearfix ul:eq(0) li .after').map(function() { 283 return $(this).text().trim(); 284 }).filter(function() { 285 return this.length; 286 }).get().join('#'); 287 GM_xmlhttpRequest({ 288 method: 'POST', 289 url: 'http://mooc.forestpolice.org/cx/' + (setting.token || 0) + '/' + encodeURIComponent(question), 290 headers: { 291 'Content-type': 'application/x-www-form-urlencoded' 292 }, 293 data: 'course=' + encodeURIComponent(setting.curs) + '&type=' + type + '&option=' + encodeURIComponent(option), 294 timeout: setting.time, 295 onload: function(xhr) { 296 if (!setting.loop) { 297 } else if (xhr.status == 200) { 298 var obj = $.parseJSON(xhr.responseText); 299 if (obj.code) { 300 setting.div.children('div:eq(0)').text('正在搜索答案...'); 301 $( 302 '<tr>' + 303 '<td>' + question + '</td>' + 304 '<td>' + obj.data + '</td>' + 305 '</tr>' 306 ).appendTo(setting.div.find('tbody')).css('background-color', fillAnswer($TiMu, obj, type) ? '' : 'rgba(0, 150, 136, 0.6)'); 307 setting.num++; 308 } else { 309 setting.div.children('div:eq(0)').text(obj.data || '服務器繁忙,正在重試...'); 310 } 311 setting.div.children('span').html(obj.msg || ''); 312 } else if (xhr.status == 403) { 313 setting.div.children('button').eq(0).click(); 314 setting.div.children('div:eq(0)').text('請求過於頻繁,建議稍后再試'); 315 } else { 316 setting.div.children('div:eq(0)').text('服務器異常,正在重試...'); 317 } 318 }, 319 ontimeout: function() { 320 setting.loop && setting.div.children('div:eq(0)').text('服務器超時,正在重試...'); 321 } 322 }); 323 } 324 325 function fillAnswer($TiMu, obj, type) { 326 var $li = $TiMu.find('ul:eq(0) li'), 327 data = String(obj.data).split('#'), 328 state = setting.lose; 329 // $li.find(':radio:checked').prop('checked', false); 330 obj.code == 1 && $li.each(function() { 331 var $input = $(this).find('input')[0]; 332 if (!$input) { 333 } else if ($input.value == 'true') { 334 ($.inArray('正確', data) + 1 || $.inArray('是', data) + 1) && $input.click(); 335 } else if ($input.value == 'false') { 336 ($.inArray('錯誤', data) + 1 || $.inArray('否', data) + 1) && $input.click(); 337 } else { 338 var tip = $(this).find('.after').text().trim() || new Date(); 339 ($.inArray(tip, data) + 1 || (type == '1' && obj.data.indexOf(tip) + 1)) == $input.checked || $input.click(); 340 } 341 }); 342 if (type.match(/^(0|1|3)$/)) { 343 $li.find('input:checked').length || (setting.none ? $li.find('input')[0].click() : setting.lose++); 344 } else if ($TiMu.find('ul:eq(0) textarea').length) { 345 (obj.code == 1 && data.length == $li.length) || setting.none || setting.lose++; 346 UE && $li.each(function(index, dom) { 347 data[index] = state == setting.lose ? (obj.code == 1 && data[index]) || '不會' : ''; 348 var $input = $(this).find('.inp'); 349 $input.is(':hidden') ? (dom = $(this).next()) : $input.val(data[index]); 350 var $edit = $(dom).find('.edui-default + textarea'); 351 $edit.length && UE.getEditor($edit.attr('name')).setContent(data[index]); 352 }); 353 } else { 354 setting.none || setting.lose++; 355 } 356 return state == setting.lose; 357 } 358 359 function submitThis() { 360 if (setting.auto && $('#validate', top.document).is(':hidden')) { 361 if ($('#confirmSubWin').is(':hidden')) { 362 $('.Btn_blue_1')[0].click(); 363 } else { 364 var $btn = $('#tipContent').next().children(':first'), 365 position = $btn.offset(), 366 mouse = document.createEvent('MouseEvents'); 367 position = [position.left + Math.floor(46 * Math.random() + 1), position.top + Math.floor(26 * Math.random() + 1)]; 368 mouse.initMouseEvent('click', true, true, document.defaultView, 0, 0, 0, position[0], position[1], false, false, false, false, 0, null); 369 $btn[0].dispatchEvent(mouse); 370 } 371 } 372 setTimeout(submitThis, setting.time); 373 } 374 375 function checkToNext() { 376 var $tip = $('.ans-job-icon', document); 377 if (!setting.check) { 378 $tip = $tip.next('iframe[src*="/video/index.html"], iframe[src*="/work/index.html"]').prev(); 379 } 380 setInterval(function() { 381 if (!$tip.parent(':not(.ans-job-finished)').length) { 382 toNext(); 383 } 384 }, setting.time); 385 } 386 387 function toNext() { 388 var $tip = $('span.currents ~ span'); 389 if (!setting.jump) { 390 } else if ($('.lock, .blue', '.currents:header').length || !$tip.length) { 391 $tip = $('.roundpointStudent, .roundpoint').parent(); 392 var index = $tip.index($tip.filter('.currents:header')); 393 $tip.slice(index + 1).not(':has(.lock, .blue)').eq(0).click().length || setting.course && switchCourse(); 394 } else { 395 $tip.eq(0).click(); 396 } 397 } 398 399 function switchCourse() { 400 GM_xmlhttpRequest({ 401 method: 'GET', 402 url: '/visit/courses/study?isAjax=true&fileId=0&debug=', 403 headers: { 404 'Referer': location.origin + '/visit/courses', 405 'X-Requested-With': 'XMLHttpRequest' 406 }, 407 onload: function(xhr) { 408 var list = $(xhr.responseText).find('li[style] a:has(img)').map(function() { 409 return $(this).attr('href'); 410 }), 411 index = list.map(function(index) { 412 return this.indexOf(top.courseId) > -1 && index; 413 }).filter(function() { 414 return $.isNumeric(this); 415 })[0] + 1 || 0; 416 setting.course = list[index] ? $.globalEval('location.replace("' + list[index] + '")') : 0; 417 } 418 }); 419 } 420 421 function goCourse() { 422 var jump = setting.course && document.referrer.match(/\/knowledge\/cards|\/mycourse\/studentstudy/); 423 jump && setTimeout(function() { 424 $('.articlename a[href]:not([class])')[0].click(); 425 }, setting.time); 426 } 427 428 function DisplayURL() { 429 $('.zmodel').on('click', '[onclick^=openFaceTip]', function() { 430 _self.WAY.box.hide(); 431 var $li = $(this).closest('li'); 432 $.get('/visit/goToCourseByFace', { 433 courseId: $li.find('input[name=courseId]').val(), 434 clazzId: $li.find('input[name=classId]').val() 435 }, function(data) { 436 $li.find('[onclick^=openFaceTip]').removeAttr('onclick').attr({ 437 href: $(data).filter('script:last').text().match(/n\("(.+?)"/)[1], 438 target: '_blank' 439 }); 440 alert('本課程已臨時解除面部識別'); 441 }, 'html'); 442 }); 443 } 444 445 function getSchoolId() { 446 $.getJSON('/org/searchUnis?filter=' + encodeURI(setting.school) + '&product=44', function(data) { 447 if (data.result) { 448 var msg = $.grep(data.froms, function(value) { 449 return value.name == setting.school; 450 })[0]; 451 msg ? setTimeout(toLogin, setting.time, msg.schoolid) : alert('學校名稱不完整'); 452 } else { 453 alert('學校查詢錯誤'); 454 } 455 }); 456 } 457 458 function toLogin(fid) { 459 var ref = $('#ref, #refer_0x001').val(); 460 $.post('/login6?refer=' + ref, { 461 fid: fid, 462 uname: setting.username, 463 password: setting.password, 464 logintype: 1 465 }).always(function(data, event) { 466 event == 'success' ? alert($(data).find('#show_error').text()) : location.href = decodeURIComponent(ref); 467 }); 468 } 469 1