Greasy Fork is available in English.

云课堂|职教云|Icve --网课兼考试助手 (绿版v3)

职教云学习效率提升助手小脚本,中文化高度可定制参数,自动课件,课件一目十行,保险模式,解除Ctrl+C限制,下载课件,自动四项评论,课堂智能跟帖讨论,支持自动答题(作业,测验,考试),搜题填题,软件定制

Zainstaluj skrypt?
Skrypt zaproponowany przez autora

Może Ci się również spodobać. 智慧职教MOOC学院 --网课助手 (蓝版)

Zainstaluj skrypt
  1. // ==UserScript==
  2. // @name 云课堂|职教云|Icve --网课兼考试助手 (绿版v3)
  3. // @version 3.6.5
  4. // @description 职教云学习效率提升助手小脚本,中文化高度可定制参数,自动课件,课件一目十行,保险模式,解除Ctrl+C限制,下载课件,自动四项评论,课堂智能跟帖讨论,支持自动答题(作业,测验,考试),搜题填题,软件定制
  5. // @author tuChanged
  6. // @run- document-start
  7. // @grant unsafeWindow
  8. // @grant GM_xmlhttpRequest
  9. // @grant GM_registerMenuCommand
  10. // @match *://*.zjy2.icve.com.cn/*
  11. // @match *://*zjy2.icve.com.cn/*
  12. // @exclude *://*zjy2.icve.com.cn/study/homework/docHomeworkPreview.html*
  13. // @license MIT
  14. // @namespace https://greasyfork.org/users/449085
  15. // @connect 可添加 API 地址
  16. // @supportURL https://github.com/tuchg
  17. // @require https://greasyfork.org/scripts/404781.js
  18. // @contributionURL https://greasyfork.org/users/449085
  19. // ==/UserScript==
  20. /*jshint esversion:6 */
  21. 'use strict'
  22. const setting = {
  23. /**
  24. * 根据下方根据提示修改脚本配置
  25. */
  26. /*
  27. * 📣如果您有软件定制(APP,小程序,管理系统等),毕设困扰,又或者课程设计困扰等欢迎联系,
  28. * 价格从优,源码调试成功,线上稳定运行再付款💰,
  29.    * 支持第三方托管交易
  30. * 实力保证,包远程,包讲解 QQ:2622321887
  31. */
  32.  
  33. // true 为打开,false 为关闭
  34. //正确率不高,题目格式不适配,不推荐打开
  35. 自动答题: false,
  36. //针对某些 nt 老师的点点点,快速完成课堂讨论
  37. 智能跟帖讨论: true,
  38. 激活仅评论: false,//与学神模式冲突,需二选一
  39. // 随机评论,自行扩充格式如 "你好", (英文符号)
  40. 随机评论词库: ["..", "🇨🇳", "💬"],
  41. //开启所有选项卡的评论,最高优先等级,打开该项会覆盖下面的细分设置,
  42. 激活所有选项卡的评论: false,
  43. 激活评论选项卡: false,
  44. 激活问答选项卡: false,
  45. 激活笔记选项卡: false,
  46. 激活报错选项卡: false,
  47. 显示评论数: 1000,
  48. // 刺激!风险未知,暂知时长不良 打开需关闭仅评论
  49. 学神模式: false,
  50. 保险模式: false,//如果课件始终不跳下一个,请勿打开该项
  51. //解除课件下载
  52. 打开课件下载: true,
  53. // 部分课件存在无检测机制问题,会尝试自动关闭保险模式
  54. 自动关闭保险模式: true,
  55. /*影响速度关键选项,延时非最优解,过慢请自行谨慎调整*/
  56. 最高延迟响应时间: 4000,//毫秒
  57. 最低延迟响应时间: 3000,//毫秒
  58. 组件等待时间: 1500,//毫秒 组件包括视频播放器,JQuery,答题等,视网络,设备性能而定,启动失败则调整
  59. 考试填题时间: 30000,//30秒 1 秒=1000 毫秒
  60. //0-高清 1-清晰 2-流畅 3-原画
  61. //感谢tonylu00提供最新实测参数 --0-原画 1-高清 2-清晰 3-流畅
  62. 视频清晰度: 3,
  63. //2倍速,允许开倍速则有效,请放心使用,失败是正常现象
  64. 视频播放倍速: 2,
  65. //是否保持静音
  66. 是否保持静音: true,
  67. //当前版本可不用理会该项, 题库 IP地址 ,可在553行查看对接接口要求
  68. 自定义题库服务器: "🔐"// 协议://IP
  69. }, top = unsafeWindow,
  70. url = location.pathname
  71. //产生区间随机数
  72. const rnd = (min, max) => Math.floor(Math.random() * (max - min + 1) + min);
  73. const classId = getQueryValue("openClassId")
  74. const cellID = getQueryValue("cellId")
  75. const stuId = localStorage.getItem("userId")
  76. // 课件完成相关判定数据
  77. let pageCount, mediaLong, cellType, startTime, lastArchiveCount
  78. //课件是否已完成
  79. let isFinshed = false;
  80. // 评论标志位
  81. const isUnFinishedTabs = [setting.激活所有选项卡的评论 || setting.激活评论选项卡, setting.激活所有选项卡的评论 || setting.激活笔记选项卡, setting.激活所有选项卡的评论 || setting.激活问答选项卡, setting.激活所有选项卡的评论 || setting.激活报错选项卡]
  82.  
  83. //定时任务栈
  84. let taskStack = 0
  85. /**
  86. * 使用异步包装
  87. * 随机延迟执行方法
  88. * @param {需委托执行的函数} func
  89. */
  90. async function delayExec(func, fixedDelay = null) {
  91. // taskStack.push(func)
  92. taskStack++
  93. const newTask = new Promise((resolve, reject) => {
  94. const newTime = rnd(fixedDelay || (setting.最低延迟响应时间) * (taskStack / 3), fixedDelay || (setting.最高延迟响应时间) * (taskStack / 2.5));
  95. setTimeout(() => {
  96. resolve(func())
  97. taskStack--
  98. console.log(`完成延时${newTime}ms的任务,待执行任务总计:${taskStack}`);
  99. }, newTime);
  100. console.log(`新增任务,等待时间${newTime}ms,待执行任务总计:${taskStack}`);
  101. });
  102. return newTask
  103. }
  104. function autoCloseDialog() {
  105. const $dialog = $(".ui-dialog");
  106. //关闭限制弹窗
  107. if ($dialog.length > 0)
  108. $dialog.find("#studyNow").click()
  109.  
  110. if ($(".xcConfirm")) {
  111. $(".xcConfirm").css({ "display": "none" })
  112. }
  113. }
  114.  
  115. GM_registerMenuCommand("🔄重新获取未完成小节", function () {
  116. sessionStorage.clear()
  117. goPage("p")
  118. });
  119.  
  120. GM_registerMenuCommand("问题反馈", function () {
  121. top.open("https://github.com/W-ChihC/SimpleIcveMoocHelper/issues")
  122. });
  123. GM_registerMenuCommand("🌹为脚本维护工作助力", function () {
  124. top.open("https://greasyfork.org/zh-CN/users/449085")
  125. });
  126. GM_registerMenuCommand("📝检查脚本配置", function () {
  127. alert(`
  128. 当前版本:绿版 v3.6.4
  129. 题库:${setting.自定义题库服务器 ? setting.自定义题库服务器 : "❌无"}
  130. 学神模式: ${setting.学神模式 ? "✅打开" : "❌关闭"}
  131. 保险模式: ${setting.保险模式 ? "✅打开" : "❌关闭"}
  132. 仅评论模式: ${setting.激活仅评论 ? "✅打开" : "❌关闭"}
  133. 自动填题:${setting.自动答题 ? "✅打开" : "❌关闭"}
  134. 智能跟帖讨论:${setting.智能跟帖讨论 ? "✅打开" : "❌关闭"}
  135. 当前组件响应时间:${setting.组件等待时间 % (1000 * 60) / 1000}
  136. 考试填题时间:${setting.考试填题时间 % (1000 * 60) / 1000}
  137. 当前评论库: [ ${setting.随机评论词库} ]
  138. 已激活的评论选项卡:${((setting.激活所有选项卡的评论 || setting.激活评论选项卡) ? "评论;" : "") + ((setting.激活所有选项卡的评论 || setting.激活问答选项卡) ? "问答;" : "") + ((setting.激活所有选项卡的评论 || setting.激活笔记选项卡) ? "笔记;" : "") + ((setting.激活所有选项卡的评论 || setting.激活报错选项卡) ? "报错" : "")}\n
  139.  
  140. 📝修改配置请找到油猴插件的管理面板
  141.  
  142. 插件仅供提升学习效率减少,繁杂工作,解放双手之用,未利用任何漏洞达成目的,均为网页自动化测试技术,切勿滥用
  143.  
  144. 脚本完全免费开源,遵循 MIT 协议,严禁倒卖,如果您是购买使用请举报售卖者
  145. `)
  146. });
  147.  
  148.  
  149.  
  150. // 一页页面加载后的工作
  151. delayExec(() => {
  152. autoCloseDialog()
  153. //匹配不需要监听网络的URL
  154. switch (url) {
  155. //作业区
  156. case "/study/homework/preview.html":
  157. case "/study/homework/do.html":
  158. case "/study/faceTeachInfo/testPreview.html":
  159. homeworkHandler()
  160. break;
  161. //考试
  162. case "/study/onlineExam/preview.html":
  163. case "/study/onlineExam/do.html":
  164. alert("请勿过快提交,同时也尽量调整脚本考试填题时间设置\n答题过快会被检测然后翻车哦")
  165. setting.组件等待时间 = setting.考试填题时间
  166. homeworkHandler()
  167. break
  168. //课堂
  169. case "/study/faceTeachInfo/faceTeachActivityListInfo.html":
  170. $(".np-hw-li.progressing .np-hw-score:contains('未参加')").each((i, e) => {
  171. const x = $(e).parent().parent().find(".am-inline-block:not(.zuoda)")
  172. if (x.length > 0) {
  173. x.attr("target", "_blank")
  174. x[0].click()
  175. }
  176. })
  177. break
  178. case "/study/faceTeachInfo/newDiscussStuInfo.html":
  179. if (!setting.智能跟帖讨论 || $(`.commentli[data-stuid='${localStorage.getItem("userId")}']`).length > 0) {
  180. return
  181. }
  182. const t = findOneVaildDiscuss()
  183. if (t) {
  184. delayExec(() => {
  185. $(".faceContent").val(t)
  186. $(".replyOk")[0].click()
  187. })
  188. } else {
  189. alert("暂无人参与")
  190. }
  191. break
  192. }
  193.  
  194. $(document).ajaxSend((e, xhr, options) => {
  195. if (!$.parseParams)
  196. $.extend({
  197. parseParams: function (e) {
  198. for (var o, i = /([^&=]+)=?([^&]*)/g, n = /\+/g, c = function (e) {
  199. return decodeURIComponent(e.replace(n, " "))
  200. }, r = {}; o = i.exec(e);) {
  201. var f = c(o[1])
  202. , a = c(o[2]);
  203. "[]" === f.substring(f.length - 2) ? (f = f.substring(0, f.length - 2),
  204. (r[f] || (r[f] = [])).push(a)) : r[f] = a
  205. }
  206. return r
  207. },
  208. htmlencode: function (o) {
  209. return e("<div />").text(o).html()
  210. },
  211. htmldecode: function (o) {
  212. return e("<div />").html(o).text()
  213. }
  214. })
  215. if (setting.学神模式 && !setting.激活仅评论)
  216. if (options.url.indexOf("stuProcessCellLog") > -1) {
  217.  
  218. const params = $.parseParams && $.parseParams(options.data);
  219. if (params)
  220. options.data = $.param({
  221. ...params,
  222. studyNewlyTime: mediaLong,
  223. picNum: pageCount,
  224. studyNewlyPicNum: pageCount
  225. })
  226. }
  227. // 修改评论页数
  228. if (options.url.indexOf("getCellCommentData") > -1) {
  229. const params = $.parseParams && $.parseParams(options.data);
  230. if (params)
  231. options.data = $.param({
  232. ...params,
  233. pageSize: setting.显示评论数
  234. })
  235. }
  236. });
  237.  
  238. }, setting.组件等待时间);
  239. function findOneVaildDiscuss() {
  240. let str = null
  241. $(".np-question-detail").each((i, e) => {
  242. if (i > 0) {
  243. const text = e.innerText
  244. if (text.trim().length > 10) {
  245. str = text
  246. return false
  247. }
  248. }
  249. })
  250. return str
  251. }
  252.  
  253.  
  254. let lastNum = 10;
  255. let currentCellData = {};
  256. let isPassMonit = false;
  257.  
  258.  
  259.  
  260. // 全局请求拦截器
  261. (function (open, send) {
  262.  
  263. // 拦截发出的请求
  264. XMLHttpRequest.prototype.send = function (data) {
  265.  
  266. // 学生课件状态检查
  267. if (data && data.indexOf("studyNewlyTime") >= 0) {
  268. // 关闭错误弹窗
  269. $(".sgBtn.ok").click();
  270. autoCloseDialog()
  271.  
  272. try {
  273. isPassMonit = true
  274. autoCloseDialog()
  275. if (!setting.激活仅评论) {
  276. let readedNum = parseInt(getQueryValue("studyNewlyPicNum", "?" + data));
  277. // 四舍五入留两位与服务器计时同步
  278. const readedTime = Math.round(parseFloat(getQueryValue("studyNewlyTime", "?" + data)) * 100) / 100;
  279. const picNum = parseInt(getQueryValue("picNum", "?" + data))
  280. // 非媒体课件下启动
  281. if ((!readedTime || setting.学神模式) && !startTime)
  282. startTime = $.now()
  283. // 纠正空课件监控问题
  284. if (pageCount === 1)
  285. readedNum = 1
  286. // 损坏课件的问题
  287. if (picNum === 1 && readedNum === 0)
  288. pageCount = 1
  289. console.log(`文档同步进度:${readedNum}/${pageCount}`, `视频同步进度:${readedTime}/${mediaLong}`);
  290. // 某些课件未被检测
  291. lastNum = readedNum && readedNum
  292. if (lastNum === 0 && setting.保险模式) {
  293. console.log("保险模式启动失败,已尝试关闭");
  294. if (setting.自动关闭保险模式) {
  295. setting.保险模式 = false
  296. requestMatcher("viewDirectory", currentCellData)
  297. }
  298. return
  299. }
  300.  
  301. // 判断当前课件是否已结束
  302. if ((readedNum && pageCount && (readedNum >= pageCount)) || setting.学神模式) {
  303. isFinshed = true
  304. const endTime = $.now()
  305. // 应对检测需停留 10 秒
  306. if (startTime && (endTime - startTime >= 10000)) {
  307. // 评论任务均已完成则跳转
  308. if (isUnFinishedTabs.indexOf(true) === -1) {
  309. nextCell()
  310. return
  311. }
  312. }
  313. console.log(`未满足职教云课件完成检测 10 秒要求,继续等待中,已等待:${endTime - startTime}ms`);
  314. } else {
  315. if (setting.保险模式)
  316. pageCount && console.log(`文档类🔐模式:${readedNum}/${pageCount}`);
  317. nextDOCPPT()
  318. }
  319. } else {
  320. // 评论任务均已完成则跳转
  321. if (isUnFinishedTabs.indexOf(true) === -1) {
  322. nextCell()
  323. return
  324. }
  325. }
  326. } catch (error) {
  327. console.log(error);
  328. }
  329. }
  330. send.apply(this, arguments);
  331. };
  332.  
  333. // 拦截数据响应
  334. XMLHttpRequest.prototype.open = function () {
  335. this.addEventListener("readystatechange", () => {
  336. if (this.readyState >= 4)
  337. requestMatcher(this.responseURL, JSON.parse(this.responseText), this)
  338. }, false);
  339. open.apply(this, arguments);
  340. };
  341. })(XMLHttpRequest.prototype.open, XMLHttpRequest.prototype.send);
  342.  
  343. /**
  344. * 请求匹配器,任务调度中心
  345. */
  346. async function requestMatcher(url, data, that) {
  347. autoCloseDialog()
  348. // debugger
  349. switch (url) {
  350. // 评论
  351. case String(url.match(/.*getCellCommentData$/)):
  352. if (isUnFinishedTabs[0] || isUnFinishedTabs[1] || isUnFinishedTabs[2] || isUnFinishedTabs[3] || setting.激活所有选项卡的评论) {
  353. const userId = localStorage.getItem("userId");
  354. const item = data.list && data.list.find(item => item.userId === userId);
  355. // 评论已完成
  356. console.log("我的评论: ", item);
  357. switch (data.type) {
  358. case 1: {
  359. if (setting.激活评论选项卡 || setting.激活所有选项卡的评论) {
  360. if (!item) {
  361. await submitComment()
  362. console.log("已完成评论提交");
  363. }
  364. isUnFinishedTabs[data.type - 1] = false
  365. }
  366. }
  367. break;
  368. case 2:
  369. {
  370. if (setting.激活笔记选项卡 || setting.激活所有选项卡的评论) {
  371. if (!item) {
  372. await submitNote()
  373. console.log("已完成笔记提交");
  374. }
  375. isUnFinishedTabs[data.type - 1] = false
  376. }
  377.  
  378. }
  379. break;
  380. case 3:
  381. {
  382. if (setting.激活问答选项卡 || setting.激活所有选项卡的评论) {
  383. if (!item) {
  384. await submitQuestion()
  385. console.log("已完成问答提交");
  386. }
  387. isUnFinishedTabs[data.type - 1] = false
  388. }
  389. }
  390. break;
  391. case 4:
  392. {
  393. if (setting.激活报错选项卡 || setting.激活所有选项卡的评论) {
  394. if (!item) {
  395. await submitReport()
  396. console.log("已完成报错提交");
  397. }
  398. isUnFinishedTabs[data.type - 1] = false
  399. }
  400. }
  401. break;
  402. }
  403. if (isUnFinishedTabs.indexOf(true) != -1) {
  404. if (data.type == 4)
  405. data.type = 2
  406. await delayExec(() => {
  407. $($(".am-tabs-nav>li a")[data.type]).click()
  408. })
  409. console.log("完成");
  410. }
  411.  
  412. // let tab = isUnFinishedTabs.indexOf(true);
  413. // if (!setting.激活笔记选项卡 && data.type !== 1)
  414. // tab -= 1
  415. // if (tab > -1 && tab + 2 !== data.type) {
  416. // await delayExec(() => {
  417. // $($(".am-tabs-nav>li a")[tab]).click()
  418. // })
  419. // }
  420.  
  421.  
  422.  
  423. //解决不同机制判断问题
  424. if ((setting.激活仅评论 || isFinshed) && isUnFinishedTabs.indexOf(true) === -1 && taskStack === 0) {
  425. nextCell()
  426. }
  427. }
  428. break;
  429. // 载入课件
  430. case String(url.match(/.*viewDirectory|loadCellResource$/)):
  431. {
  432. if (data.code === -33) {
  433. nextCell()
  434. return
  435. }
  436.  
  437. autoCloseDialog()
  438. if (setting.激活仅评论) {
  439. console.log("仅开启评论已打开");
  440. // commentHandler()
  441. return
  442. }
  443.  
  444. if (currentCellData && setting.打开课件下载) {
  445. // 课件下载 todo
  446. data.isAllowDownLoad = true
  447. data.isDownLoad = true
  448. console.log("当前课件下载地址:", data.downLoadUrl);
  449. // 修改服务器返回数据
  450. if (!that._responseText) {
  451. Object.defineProperty(that, 'responseText', {
  452. get: () => that['_responseText'] === undefined ? that.responseText : that['_responseText'],
  453. set: (val) => {
  454. that['_responseText'] = val
  455. },
  456. enumerable: true
  457. });
  458. }
  459. //修改响应数据
  460. that._responseText = JSON.stringify(data)
  461. }
  462. // 课件页数
  463. pageCount = data.pageCount
  464. // // 课件当前已阅览时间
  465. // readTime = data.stuStudyNewlyTime
  466. // 媒体时间长度
  467. mediaLong = data.audioVideoLong;
  468. // 课件进度
  469. const cellPercent = data.cellPercent
  470. // 课件类型
  471. cellType = data.categoryName
  472. // 如果当前课件为遗漏课件则进入下一个课件
  473. if (cellPercent === 100 && isUnFinishedTabs.indexOf(true) === -1) {
  474. nextCell()
  475. return
  476. }
  477. currentCellData = data
  478. console.log("当前课件: ", data);
  479. cellHandlerMatcher()
  480. }
  481. break;
  482.  
  483. case String(url.match(/.*faceTeachActivityInfo$/)):
  484. {
  485.  
  486.  
  487. }
  488. break
  489. // 课程章节目录
  490. case String(url.match(/.*getProcessList$/)):
  491. {
  492. const localS = sessionStorage.getItem(classId);
  493. //未在本地找到遗留数据则重新获取
  494. if (!localS || localS === "[]" || localS === "null") {
  495.  
  496. if (!confirm("正在获取未完成小节数据,为避免检测,请耐心等待🖥\n✅确定以继续,确认后勿关闭本页\n直到再次弹窗,否则脚本将结束工作\n ‼️插件仅供提升学习效率减少,繁杂工作,解放双手之用,未利用任何漏洞达成目的,均为网页自动化技术,请健康使用勿要滥用\n"))
  497. return
  498. const parentNode = data && data.progress;
  499. //过滤已经学习完的课件
  500. let dirs = parentNode && parentNode.moduleList.filter(item => item.percent !== 100)
  501. if (setting.激活仅评论)
  502. dirs = parentNode.moduleList
  503. //请求课程所有数据
  504. const orginalData = (await sendIcveRequest(urls2.courseView_getCourseDetailList)).courseProcessInfo
  505. //过滤掉已完成的章节
  506. const list = orginalData && orginalData.filter(item => dirs.find(i => i.id === item.id))
  507. const cid = getQueryValue("courseOpenId")
  508. const oid = getQueryValue("openClassId")
  509. //最终处理数据
  510. const finalData = []
  511. //提取未完成的课件
  512. for (const item of list) {
  513. for (const i of item.topics) {
  514. // 最终需要处理的数据
  515. const cellList = (await sendIcveRequest(urls.process_getCellByTopicId, { courseOpenId: cid, openClassId: oid, topicId: i.id })).cellList
  516. cellList && cellList.forEach(item => {
  517. const childList = item.childNodeList;
  518. if (childList && childList.length !== 0) {
  519. const childVaildList = childList.filter(i => {
  520. // if (i.cellType !== 4 && i.fromType !== 4) {
  521. if (i.cellType !== 4) {
  522. if (setting.激活仅评论)
  523. return true
  524. if (i.stuCellFourPercent !== 100)
  525. return true
  526. }
  527. return false
  528. });
  529. console.log(childVaildList);
  530. finalData.push(...childVaildList)
  531. // } else if (item.cellType !== 4 && item.fromType !== 4) {
  532. } else if (item.cellType !== 4) {
  533. if (setting.激活仅评论)
  534. finalData.push(item)
  535. else if (item.stuCellPercent !== 100)
  536. finalData.push(item)
  537. console.log(item);
  538. }
  539. })
  540. }
  541. }
  542. console.log(`已成功缓存${finalData.length}条未完成小节信息`);
  543. sessionStorage.setItem(classId, JSON.stringify(finalData.reverse()))
  544. }
  545. const data_ = JSON.parse(sessionStorage.getItem(classId))
  546. console.log(data_);
  547. if (confirm(`✅已初始化完成,发现${data_.length}个课件未完成,是否立即启动不知疲倦学习🙇🏼‍♂️📚模式`))
  548. goPage(null, data_[data_.length - 1])
  549. }
  550. break;
  551. default:
  552. if (data && data.msg && data.msg.indexOf("操作成功") < 0) {
  553. console.log("无任务可分配", data);
  554. }
  555. break;
  556. }
  557. }
  558. /**
  559. * 查找下一个课件,并在本地缓存更新相应信息
  560. */
  561. function nextCell() {
  562. // debugger
  563. const data = JSON.parse(sessionStorage.getItem(classId));
  564. if (!data) {
  565. if (confirm("🆇未从缓存中检测到课程数据,是否进入正常运行流程\n如果您是购买使用,请举报售卖方,本脚本完全免费开源使用")) {
  566. goPage("p")
  567. return
  568. }
  569. }
  570. const surplusData = data && data.filter(item => item.Id !== cellID);
  571. sessionStorage.setItem(classId, JSON.stringify(surplusData))
  572.  
  573. if (surplusData && surplusData.length === 0) {
  574. alert("课程已完成\n脚本完全免费开源,遵循 MIT 协议,严禁倒卖,如果您是购买使用请举报售卖者")
  575. return
  576. }
  577.  
  578. console.log("当前课件任务已完成----");
  579.  
  580. delayExec(() => {
  581. goPage(null, surplusData.pop())
  582. })
  583. }
  584.  
  585.  
  586. /**
  587. * 跳转到某页面
  588. */
  589. function goPage(url, data = undefined) {
  590. let newPage;
  591. if (!url) {
  592. newPage = `${location.origin}/common/directory/directory.html?courseOpenId=${data.courseOpenId}&openClassId=${classId}&cellId=${data.Id}&flag=${data.flag || "s"}&moduleId=${data.parentId}`;
  593. console.log("下一个课件: ", newPage);
  594. } else {
  595. newPage = `${location.origin}/study/process/process.html?courseOpenId=${getQueryValue("courseOpenId")}&openClassId=${getQueryValue("openClassId")}`
  596. }
  597. top.location.href = newPage
  598. }
  599.  
  600. /**
  601. * 对网站发送请求集中处理,解析结果,处理成功与否逻辑
  602. */
  603. function sendIcveRequest(url, data = {}) {
  604. return new Promise((resolve, reject) => {
  605. delayExec(() => {
  606. _.ajax(url, data, (r) => {
  607. if (r.code == 1) {
  608. resolve(r)
  609. } else {
  610. console.log("请求出问题了🔐", r)
  611. reject(r)
  612. }
  613. })
  614. })
  615. })
  616. }
  617.  
  618.  
  619. /**
  620. * 课件匹配处理调度
  621. */
  622. function cellHandlerMatcher() {
  623.  
  624. if (!setting.激活仅评论)
  625. switch (cellType) {
  626. case "图片":
  627. case "文档":
  628. case "excel文档":
  629. case "office文档":
  630. case "pdf文档":
  631. case "其它":
  632. case "ppt文档":
  633. if (!setting.保险模式)
  634. delayExec(() => {
  635. docHandler()
  636. })
  637. break;
  638. case "ppt":
  639. if (!setting.保险模式)
  640. delayExec(async () => {
  641. await pptHandler()
  642. })
  643. break;
  644. case "swf":
  645. swfHandler()
  646. break;
  647. case "视频":
  648. case "音频":
  649. delayExec(() => {
  650. mediaHandler()
  651. }, setting.组件等待时间)
  652. break;
  653. case "图文":
  654. case "压缩包":
  655. emptyHandler()
  656. break;
  657. default:
  658. console.log(`课件 : ${cellType} 未提供兼容, ${setting.未做兼容课件打开评论 ? '已开启兼容评论,仅运行评论' : '已跳过处理'},请在github issue (https://github.com/W-ChihC/SimpleIcveMoocHelper) 反馈该日志,与作者取得联系`);
  659. break
  660. }
  661. }
  662.  
  663.  
  664.  
  665.  
  666.  
  667. /**
  668. * 获取url查询字段
  669. * @param {查询字段} query
  670. * @param 默认为地址栏
  671. */
  672. function getQueryValue(query, url = window.location.search) {
  673. let theRequest = new Object();
  674. if (url.indexOf("?") != -1) {
  675. let str = url.substr(1);
  676. let strs = str.split("&");
  677. for (let i = 0; i < strs.length; i++)
  678. theRequest[strs[i].split("=")[0]] = unescape(strs[i].split("=")[1]);
  679. }
  680. return theRequest[query];
  681. }
  682.  
  683.  
  684. /**
  685. * 仅仅评论的处理器
  686. */
  687. async function emptyHandler() {
  688. console.log("啥也没干,请联系作者", cellType);
  689. }
  690.  
  691. async function swfHandler() {
  692. //当不支持flash时执行
  693. if ($('.popBox').length !== 0) {
  694. $($('.popBox a')[1]).click()
  695. }
  696. }
  697.  
  698. /**
  699. * 视频/音频类处理
  700. */
  701. function mediaHandler() {
  702. try {
  703. let player = top.jwplayer($(".jwplayer").attr("id"));
  704. let state = null;
  705. state = player.getState();
  706. //视频暂停状态
  707. if (state == "paused" || state === 'idle') {
  708. console.log("媒体已暂停,恢复播放");
  709. player.play()
  710. }
  711. if (player.getDuration() === 0) {
  712. if (state === "buffering") {
  713. const timer = setInterval(() => {
  714. if (player.getPosition() === 0)
  715. delayExec(() => {
  716. if (player.getPosition() === 0)
  717. player.play();
  718. }, setting.组件等待时间);
  719. else
  720. clearInterval(timer)
  721. }, setting.组件等待时间);
  722.  
  723. } else {
  724. if (setting.学神模式 || isUnFinishedTabs.indexOf(true) === -1) {
  725. nextCell()
  726. return
  727. }
  728. isFinshed = true
  729. }
  730. }
  731. //播放原已完成
  732. if (player.getState() == "complete") {
  733. console.log("媒体播放已完成");
  734. // 评论任务均已完成则跳转
  735. if (isUnFinishedTabs.indexOf(true) === -1) {
  736. nextCell()
  737. return
  738. }
  739. isFinshed = true
  740. return
  741. }
  742. //播放回调
  743. player.on("playlistComplete", () => {
  744. console.log("媒体播放完成");
  745. // 评论任务均已完成则跳转
  746. if (isUnFinishedTabs.indexOf(true) === -1) {
  747. nextCell()
  748. return
  749. }
  750. isFinshed = true
  751. })
  752. //配置
  753. player.setMute(setting.是否保持静音)//静音
  754. player.setCurrentQuality(setting.视频清晰度)
  755. try {
  756. player.setPlaybackRate(setting.视频播放倍速)
  757. } catch (error) {
  758. console.log('倍速开启失败...正常现象.');
  759. }
  760. } catch (error) {
  761. console.log("课件为空或无法解析", error);
  762. // 评论任务均已完成则跳转
  763. if (isUnFinishedTabs.indexOf(true) === -1) {
  764. nextCell()
  765. return
  766. }
  767. isFinshed = true
  768. }
  769.  
  770. }
  771. /**
  772. * 文档处理
  773. */
  774. async function docHandler() {
  775.  
  776. if ($(".MPreview-pageNext").length !== 0) {
  777. //根据按钮状态判断是否还有下一页
  778. while ($(".MPreview-pageNext").hasClass('current')) {
  779. console.log(`文档翻页,总页数:${pageCount}`);
  780. //ppt翻页 异步方式
  781. await delayExec(() => {
  782. $(".MPreview-pageNext").click()
  783. })
  784. }
  785. } else {
  786. await pptHandler()
  787. }
  788. }
  789.  
  790.  
  791. /**
  792. * PPT类别处理
  793. */
  794. async function pptHandler() {
  795. // 异步处理
  796. return new Promise(async (resolve, reject) => {
  797. for (let i = 1; i <= pageCount * 2; i++) {
  798. //点击下一页
  799. await delayExec(() => {
  800. nextDOCPPT()
  801. // console.log(`ppt第${i}页,总页数:${pageCount}`);
  802. //达到次数解除阻塞
  803. if (isFinshed || i === pageCount && mediaLong === 0)
  804. resolve()
  805. })
  806. }
  807. // if (pageCount === 1) {
  808. // for (let i = 0; i < 5; i++)
  809. // nextDOCPPT()
  810. // delayExec(() => {
  811. // nextCell()
  812. // resolve()
  813. // }, 15000)
  814. // }
  815. })
  816. }
  817. /**
  818. * 下一页PPT 或文档
  819. */
  820. function nextDOCPPT() {
  821. const pptNext = $(".stage-next"), docNext = $(".MPreview-pageNext"), sNext = $(".stage-next-btn");
  822. pptNext && pptNext.click()
  823. docNext && docNext.click()
  824. sNext && sNext.click()
  825. }
  826. /**
  827. * 对XHR的二次全局封装,方便后期扩展
  828. * @param {*} method
  829. * @param {*} url
  830. * @param {*} headers
  831. * @param {*} data
  832. * @param {*} onSuccess
  833. */
  834. function requestAPI(method, url, { headers = {}, data, onSuccess } = {}) {
  835. return new Promise((resolve, reject) => {
  836. GM_xmlhttpRequest({
  837. method: method,
  838. url: url,
  839. headers: headers,
  840. data: data,
  841. //关闭 cookie
  842. anonymous: true,
  843. timeout: 2000,
  844. onload: function (xhr) {
  845. switch (xhr.status) {
  846. case 200:
  847. case 404:
  848. // let obj = $.parseJSON(xhr.responseText) || {};
  849. if (onSuccess)
  850. onSuccess(xhr)
  851. else
  852. resolve(xhr)
  853. break;
  854. default:
  855. resolve(xhr)
  856. break;
  857. }
  858. },
  859. onabort: function (params) {
  860. reject(params)
  861.  
  862. },
  863. onerror: function (params) {
  864. // debugger
  865. reject(params)
  866. },
  867. ontimeout: function (params) {
  868. reject(params)
  869. }
  870. });
  871. })
  872.  
  873. }
  874.  
  875.  
  876. /**
  877. * 评论
  878. */
  879. async function submitComment() {
  880. // debugger
  881. return new Promise(async (resolve, reject) => {
  882.  
  883. //评5星
  884. $("#star #starImg4").click();
  885. //随机从词库填写评论
  886. $(".commentContent").text(setting.随机评论词库[rnd(0, setting.随机评论词库.length - 1)])
  887. //提交
  888. await delayExec(async () => {
  889. $("#btnComment").click();
  890. resolve()
  891. });
  892. })
  893. }
  894.  
  895. /**
  896. * 问答
  897. */
  898. async function submitQuestion() {
  899. // debugger
  900. return new Promise(async (resolve, reject) => {
  901. //随机从词库填写评论
  902. $(".questionContent").text(setting.随机评论词库[rnd(0, setting.随机评论词库.length - 1)])
  903. //提交
  904. await delayExec(async () => {
  905. $("#btnQuestion").click();
  906. resolve()
  907. }, 60000);
  908. })
  909. }
  910. const list = []
  911. /**
  912. * 笔记
  913. */
  914. async function submitNote() {
  915. // debugger
  916. return new Promise(async (resolve, reject) => {
  917. //随机从词库填写评论
  918. $(".noteContent").text(setting.随机评论词库[rnd(0, setting.随机评论词库.length - 1)])
  919. //提交
  920. await delayExec(async () => {
  921. $("#btnNote").click();
  922. resolve()
  923. }, 60000);
  924. })
  925. }
  926. /**
  927. * 报错
  928. */
  929. async function submitReport() {
  930. return new Promise(async (resolve, reject) => {
  931. //随机从词库填写评论
  932. $(".cellErrorContent").text(setting.随机评论词库[rnd(0, setting.随机评论词库.length - 1)])
  933. //提交
  934. await delayExec(async () => {
  935. $("#btnCellError").click();
  936. resolve()
  937. }, 60000);
  938. })
  939. }
  940.  
  941.  
  942. /*
  943. * 解除文本限制
  944. */
  945. function uncageCopyLimit() {
  946. let arr = ["oncontextmenu", "ondragstart", "onselectstart", "onselect", "oncopy", "onbeforecopy"]
  947. for (let i of arr)
  948. $(".hasNoLeft").attr(i, "return true")
  949. console.log("已成功解除复制限制,📣如果您有软件定制(管理系统,APP,小程序等任何形式私活)等欢迎联系\n价格从优,源码调试成功再付款💰\n实力保证,包远程,包讲解 QQ:2622321887")
  950. }
  951.  
  952.  
  953. /**
  954. * 作业处理
  955. */
  956. async function homeworkHandler() {
  957.  
  958. uncageCopyLimit()
  959. if (!setting.自定义题库服务器) {
  960. alert("未填写题库📝,无法正常使用答题,仅提供解除网站限制")
  961. }
  962. bindBtnToQuestion()
  963. if (setting.自动答题)
  964. autoFill()
  965. }
  966.  
  967. let isAutoFilling = false
  968. /**
  969. * 单选 多选 判断 填空 问答
  970. */
  971. async function autoFill() {
  972. const q = $(".qBtn");
  973. for (let i = 0; i < q.length; i++) {
  974. const e = q[i];
  975. await delayExec(() => {
  976. isAutoFilling = true
  977. e.click()
  978. }, setting.组件等待时间)
  979. }
  980. delayExec(() => {
  981. if (setting.组件等待时间 === setting.考试填题时间) {
  982. alert("如果你不想被老师打零分,就别智障的过快提交")
  983. }
  984. $("#submitHomeWork").click()
  985. isAutoFilling = false
  986. }, setting.组件等待时间)
  987. }
  988.  
  989.  
  990. // 重新渲染答题区的标志位
  991. let reRender = false
  992.  
  993. /**
  994. * 将查询按钮按ID调用插入到题目区未位
  995. */
  996. function bindBtnToQuestion() {
  997. // $(`<button class="qBtn" type="button">🔍</button>`).appendTo(".e-q-quest")
  998. // $($(".e-a-g")[2]).prev(".e-q-q")
  999. $(".e-q-quest").each(async (i, e) => {
  1000. $(`<button class="qBtn" x="${i}" type="button">🔍</button>`).appendTo($(e))
  1001. })
  1002. //去除填空按钮,提高答案匹配
  1003. $('.fillbox').detach()
  1004.  
  1005. //绕过网站全局事件注册
  1006. $(".qBtn").on("click", (event) => {
  1007. reRender = true
  1008. searchAnswer(event.srcElement.attributes["x"].value)
  1009. })
  1010. }
  1011.  
  1012. const server = setting.自定义题库服务器 || "http://127.0.0.1:5000"
  1013.  
  1014. /**
  1015. * //接口对接规范(JSON) 快速通道(/q?q=问题) 更多信息(/q2?q=问题)
  1016. * [
  1017. * {
  1018. * 'question': '问题,可留空',
  1019. * 'answer': '答案', //判断题 1 为正确,其余为错误
  1020. * 'options':'题目选项,可留空',
  1021. * 'msg': '消息,可留空'
  1022. * },{
  1023. *
  1024. * }
  1025. * ]
  1026. *
  1027. */
  1028.  
  1029.  
  1030. /**
  1031. * 填题
  1032. * @param {*} id 答案 ID
  1033. */
  1034. function fillAnswer(aID, qId) {
  1035. // 多选 及自动答题模块
  1036. //todo 后端: 1,2,3
  1037. let answer = $(`#${aID}`).text();
  1038. const qBody = $($(".qBtn")[qId]).parents(".e-q-body");
  1039. const questionType = qBody.data("questiontype");
  1040. let inputBlock;
  1041. switch (questionType) {
  1042. // <!-- 1:单选 2:多选 -->
  1043. case 1:
  1044. case 2:
  1045. answer.split(",").forEach(e => {
  1046. inputBlock = $(qBody.find(`.e-a-g li:contains("${e}")`));
  1047. inputBlock.click()
  1048. inputBlock.focus()
  1049. })
  1050. break;
  1051. // < !--3:判断题-- >
  1052. case 3:
  1053. answer = answer.trim()
  1054. inputBlock = $(qBody.find(".e-a-g li")[(answer == "1" || answer == "正确" || answer == "对" || answer == "√") ? 0 : 1]);
  1055. //默认第一项为正确
  1056. inputBlock.click()
  1057. inputBlock.focus()
  1058. break;
  1059. // <!-- 4:填空题(主观) 5:填空题(客观) 6 问答-->
  1060. case 4:
  1061. case 5:
  1062. answer.split(",").forEach((e, i) => {
  1063. inputBlock = $(qBody.find(".e-a-g input")[i])
  1064. inputBlock.val(e)
  1065. inputBlock.blur()
  1066. })
  1067. break;
  1068. case 6:
  1069. inputBlock = $(qBody.find("textarea")[0])
  1070. inputBlock.val(answer)
  1071. inputBlock.blur()
  1072. break;
  1073. default:
  1074. break;
  1075. }
  1076. }
  1077. // 查看更多答案的锁
  1078. let nextLock = false
  1079. /**
  1080. * 显示搜索框
  1081. * @param {*} params
  1082. */
  1083. async function showAnswerListDiv(questionTitle, data, id) {
  1084. const title = setting.组件等待时间 === setting.考试填题时间 ? `脚本提倡诚信考试,真材实料应考,<b>答案仅供参考</b>,不可全信<br>为保证考试公平,将会在一定范围内返回随机<em>错误答案</em><br>针对考试特殊处理,请耐心等待,出现提示前勿要乱动,否则<em>按舞弊处理</em>其后果自负<br> ${setting.自动答题 ? `下一道题将在<b>${setting.考试填题时间 % (1000 * 60) / 1000}</b>秒后继续` : ""}` : questionTitle.substr(0, 30)
  1085. if ($("#answerBlock").length == 0) {
  1086. const baseDiv = ` <div id="answerBlock" style="background: #cccccc8c;max-width:50%; float: right; margin-right: 230px;overflow:auto; position: fixed; top: 0; right: 0; z-index: 9999;">
  1087. <table border="1" cellspacing="0" align="center" style="font-size: 14px;">
  1088. <caption style="min-width:200px;">${title}</caption>
  1089. <thead>
  1090. <tr>
  1091. <th>题目</th>
  1092. <th>📝</th>
  1093. <th>消息</th>
  1094. </tr>
  1095. <tr>
  1096. <th colspan="2">选项</th>
  1097. </tr>
  1098. <tr>
  1099. <th colspan="2">结果</th>
  1100. </tr>
  1101. </thead>
  1102. <tbody align="left">
  1103. </tbody>
  1104. </table>
  1105. <center><a type="button" id="nextBtn" >查找更多 (慢)</a></center>
  1106.  
  1107. </div>`
  1108. $(baseDiv).appendTo("body")
  1109. // 初次初始化后关闭
  1110. reRender = false
  1111. //允许查看更多
  1112. nextLock = false
  1113. } else {
  1114. if (reRender) {
  1115. //更新对应数据
  1116. $("#answerBlock caption").html(title)
  1117. //删除原有的数据
  1118. $('#answerBlock tbody tr').detach()
  1119. // 换题后立即关闭
  1120. reRender = false
  1121. //允许查看更多
  1122. nextLock = false
  1123. }
  1124. }
  1125. let tbody = "";
  1126. data && data.forEach((item, i) => {
  1127. if (item != null) {
  1128. let { question, answer, options, msg } = item
  1129. const x = rnd(10, 1000000) + i
  1130. tbody += `
  1131. <tr>
  1132. <td>${question || ""}</td>
  1133. <td><a class="aBtn" aId="${x}" qId=${id} type="button" style="margin:2px">填入</a></td>
  1134. <td>
  1135. <p>${msg || ""}</p>
  1136. </td>
  1137. </tr>
  1138. <tr style="height:50px">
  1139. <td colspan="3">${options || ""}</td>
  1140. </tr>
  1141. <tr style="height:50px">
  1142. <td colspan="3"><b id=${x} ><a class="aBtn" aId="${x}" qId=${id}> ${answer || ""}</a></b></td>
  1143. </tr>
  1144. `
  1145. }
  1146. });
  1147. /**
  1148. * 查看更多
  1149. */
  1150. if (!nextLock) {
  1151. $("#nextBtn").off("click")
  1152. $("#nextBtn").on("click", async () => {
  1153. if (!nextLock) {
  1154. /**
  1155. * 慢速接口
  1156. * @param questionTitle 问题
  1157. * @param id 对应 div id (用于定位答题)
  1158. */
  1159. slowSearch(questionTitle, id)
  1160. //不再允许重复访问
  1161. nextLock = true
  1162. }
  1163. })
  1164. }
  1165. /**
  1166. * tbody区
  1167. */
  1168. $(tbody).appendTo("#answerBlock table tbody")
  1169. $('#answerBlock p').css({ margin: '0', wordwrap: 'break-word', maxwidth: '50px' });
  1170. $('#answerBlock em').css({ color: 'red' })
  1171. //绕过网站全局事件注册
  1172. $(".aBtn").on("click", (event) => {
  1173. fillAnswer(event.srcElement.attributes["aId"].value, event.srcElement.attributes["qId"].value)
  1174. })
  1175. // if (setting.自动答题)
  1176. /**填写第一项到答案 */
  1177. try {
  1178.  
  1179. $(".aBtn")[0].click()
  1180.  
  1181. } catch (e) {
  1182.  
  1183. }
  1184. }
  1185. /**
  1186. * 搜索答案
  1187. * @param {*} i
  1188. */
  1189. async function searchAnswer(i) {
  1190. // 往前查找同辈元素
  1191. const question = $($(".qBtn")[i]).prevAll(".e-q-q").text().trim();
  1192.  
  1193. showAnswerListDiv("搜索中...", [], i)
  1194. /**
  1195. * 快速接口
  1196. * @param questionTitle 问题
  1197. * @param id 对应 div id(用于定位答题)
  1198. */
  1199. try {
  1200. await quickSearch(question, i)
  1201. } catch (e) {
  1202. reRender = true
  1203. showAnswerListDiv("搜索失败...", [{ options: "<center><b>作者删库跑路了</b></center>" }], i)
  1204. }
  1205. }