近日找到了某位大佬所写的一个超星网课的刷课脚本,比较好用,特来分享一下,此脚本未经作者同意,切勿用作商业用途,否则后果自负。
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 onl oad: 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 onl oad: 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