// ==UserScript== // @name 国资e学刷课程,可秒刷 // @namespace https://greasyfork.org/zh-CN/scripts/493533/feedback // @version 2.0 // @description 2.0版本更新!现已全面支持最新版国资e学平台,点击“即刻开刷”按钮,即可自动完成播放页面内所有课程。UI交互优化升级,体验更加丰富流畅!本脚完全免费,提醒大家谨防二次售卖,确保使用安全无忧。 // @author ZouYS // @match https://elearning.tcsasac.com/* // @icon https://p3-sign.douyinpic.com/obj/douyin-user-image-file/1ebae6a7405da95fb2633a3512294cb1?x-expires=1731610800&x-signature=fI7E3NVqdhaBvIuFFQ9afCdJXsk%3D&from=2064092626&s=sticker_comment&se=false&sc=sticker_heif&biz_tag=aweme_comment&l=202411142138312F77099E145987055E9E // @require https://cdn.jsdelivr.net/npm/sm-crypto@0.3.13/dist/sm2.min.js // @require https://fastly.jsdelivr.net/npm/sm-crypto@0.3.13/dist/sm2.min.js // @require https://cdn.jsdelivr.net/npm/sm-crypto@0.3.13/dist/sm3.min.js // @require https://fastly.jsdelivr.net/npm/sm-crypto@0.3.13/dist/sm3.min.js // @resource https://cdn.staticfile.org/limonte-sweetalert2/11.7.1/sweetalert2.min.css // @require https://cdn.jsdelivr.net/npm/sweetalert2@11.12.2/dist/sweetalert2.all.min.js // @require https://fastly.jsdelivr.net/npm/sweetalert2@11.12.2/dist/sweetalert2.all.min.js // @run-at document-start // @grant GM_getValue // @grant GM_setValue // @grant GM_deleteValue // @license Apache-2.0 // @downloadURL none // ==/UserScript== // 2222 4615502 22220 22223 2666 33233 22266 11222 (function () { 'use strict'; let requestObject = { updateUserVideoTime: { url: 'https://elearning.tcsasac.com/prod-api/study/study/updateUserVideoTime', method: 'POST', data: { "fileId": "", "videoTime": 0, "viewDuration": 0, "courseId": 0 }, res: { code: 200, msg: "操作成功", } }, asynUpdateCourseSchedule: { url: 'https://elearning.tcsasac.com/prod-api/study/study/asynUpdateCourseSchedule', method: 'GET', data: { courseId: 0, } }, addUserVideoLog: { url: 'https://elearning.tcsasac.com/prod-api/study/study/addUserVideoLog', method: 'POST', data: {} }, addUserCourseLog: { url: 'https://elearning.tcsasac.com/prod-api/study/study/addUserCourseLog', method: 'POST', data: {} }, /** * 获取课程总时间 */ getUserVideoLogListByCourseId: { url: 'https://elearning.tcsasac.com/prod-api/study/userVideo/getUserVideoLogListByCourseId', method: 'GET', data: { courseId: 0 } } } /** * 公钥 * @type {string} */ const transferSM2Public = '04dc384b738d4261dcaf2e042f68cd037d3536df5286cc2059bc20a0a81f1c42abf85b7fd7f4eb5ae2f3a8476297ffdcc53d64d9551d5a7da8a761a871c897728d' const transferSM2Private = "7ea7cec7043b1eb7a8c4110bb643725216cb2dc1d26dfdb2acffc1e486ba8483" const backSM2Public = "04a90d1f589d45e9d7dd45e98c6ef86742354619d846414ecff18557f20f65763d2e95e3c0e20e3d7d8601abfa289aa63ea0c8a71647c99668f5b19a7abb8cc6c2" const backSM2Private = '9ffd52bc4e2cd0464ba19be8215531eaab0b56e276a6f8208c68e30917e7a5a' /** * sm加密 * @param data payload * @returns {string} 密文 */ const encrypt = (data) => { let n = Date.now(); const o = sm3("".concat(data, "lifeismovie").concat(n)) , a = sm2.doEncrypt("".concat(data, "lifeismovie").concat(n, "heykong").concat(o), transferSM2Public); return "04".concat(a, "0g5z2w5j_whatsup") } /** * 解密 * @param secret 密文 * @param t * @param n * @returns {object | null} */ const decrypt = (secret, t = transferSM2Private, n = "request") => { let o = null; const a = secret.match(/^04(\S*)/); if (a) { const e = "request" === n ? a[1].match(/(\S*)8l6z9c3j_whatsup$/) : a[1].match(/(\S*)0g5z2w5j_whatsup$/); if (e) { const a = sm2.doDecrypt(e[1], t) , r = a.split("heykong")[0] , l = a.split("heykong")[1]; if (r && l) { const e = r.split("lifeismovie")[1] , t = r.split("lifeismovie")[0]; // console.log('t:', t) if (sm3(r) === l) { o = JSON.parse(t) } else o = null } else o = null } else o = null } else o = null; return o } /** * 获取当前课程ID * @returns {number|null} */ const initCourseInfo = () => { const curUrl = location.href.split('eparams=')[1]; if (!curUrl) { console.error('cant get courseId!') return null } let obj = decrypt(curUrl, undefined, "other"); console.log("init obj", obj) return obj.id } /** * 获取当前课程信息 * @param courseId * @returns {Promise} */ const getCourseInfo = async (courseId) => { let obj = {...requestObject.getUserVideoLogListByCourseId} obj.data.courseId = courseId const res = await request(obj.url, "GET", obj.data); if (res) { return res } else { console.error('getCourseInfo error:', courseId) } } /** * 更新视频观看时长 * @param courseId * @param fileId * @param videoTime * @returns {Promise} */ const updateCourseTime = async (courseId, fileId, videoTime) => { let obj = {...requestObject.updateUserVideoTime} obj.data.courseId = courseId; obj.data.fileId = fileId; obj.data.videoTime = videoTime; obj.data.viewDuration = videoTime - 1; try { return await xhrRequest(obj.url, "POST", obj.data); } catch (error) { console.error('updateCourseTime error:', error); return null; } } /** * 同步更新课程表 * @param courseId * @returns {Promise} */ const updateCourseSchedule = async (courseId) => { let obj = {...requestObject.asynUpdateCourseSchedule} obj.data.courseId = courseId const res = await request(obj.url, "GET", obj.data) } /** * fetch请求 * @param url * @param method * @param data * @returns {Promise} */ const request = async (url, method = "GET", data) => { url = method === "GET" ? (url + '?edata=' + encrypt(new URLSearchParams(data).toString())) : url let res = await fetch(url, { "headers": { "accept": "application/json, text/plain, */*", "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6", "authorization": `Bearer ${document.cookie.split('user-Token=')[1].split(';')[0]}`, "cache-control": "no-cache", "pragma": "no-cache", "sec-ch-ua": "\"Chromium\";v=\"130\", \"Microsoft Edge\";v=\"130\", \"Not?A_Brand\";v=\"99\"", "sec-ch-ua-mobile": "?0", "sec-ch-ua-platform": "\"Windows\"", "sec-fetch-dest": "empty", "sec-fetch-mode": "cors", "sec-fetch-site": "same-origin" }, "referrer": `${location.href}`, "referrerPolicy": "strict-origin-when-cross-origin", "body": method === "GET" ? null : (JSON.stringify({ edata: encrypt(JSON.stringify(data)) })), "method": method, "mode": "cors", "credentials": "include" }); if (res.ok) { res = await res.text() res = (decrypt(res, backSM2Private)) console.log('url', url) console.log(res) return res } return null } /** * xhr请求,同步时间使用 * @param url * @param method * @param data * @returns {Promise} */ const xhrRequest = (url, method = "GET", data) => { return new Promise((resolve, reject) => { let xhr = new XMLHttpRequest(); xhr.open(method, url, true); xhr.setRequestHeader("Accept", "application/json, text/plain, */*"); xhr.setRequestHeader("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6"); xhr.setRequestHeader("Authorization", `Bearer ${document.cookie.split('user-Token=')[1].split(';')[0]}`); xhr.setRequestHeader("Cache-Control", "no-cache"); xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8;"); xhr.setRequestHeader("Pragma", "no-cache"); xhr.referrer = `${location.href}` xhr.onreadystatechange = () => { if (xhr.readyState === 4) { if (xhr.status === 200) { try { let res = decrypt(xhr.responseText, backSM2Private); if (res) { console.log(`${url} ->`, res); resolve(res); } else { console.error("Decrypt failed:", res); reject("Decryption failed"); } } catch (e) { console.error("Error in decryption:", e); reject(e); } } else { console.error(`Request failed with status ${xhr.status}:`, xhr.responseText); reject(xhr.responseText); } } }; let body = JSON.stringify({ "edata": encrypt(JSON.stringify(data)) }); xhr.send(body); }) } /** * 主模块 * @param courseId * @returns {Promise} */ const run = async (courseId) => { try { const courseInfo = await getCourseInfo(courseId); if (Array.isArray(courseInfo.data.chapterList[0].fileInfoList)) { for (const file of courseInfo.data.chapterList[0].fileInfoList) { if (file.viewDuration <= file.videoTime) { console.log(file.fileId) const res = await updateCourseTime(courseId, file.fileId, file.videoTime); if (res.code === 200) { console.log('updateCourseTime: ' + res.msg + '--' + file.fileName) await updateCourseSchedule(courseId) } else { console.error('updateCourseTime error:', res) } } } } if(Swal){ Swal.fire({ title: "刷课成功!", text: `当前课程列表已全部完成!点击确定立即刷新页面,显示最新结果!`, icon: 'success', showCancelButton: true, confirmButtonColor: "#FF4DAFFF", cancelButtonText: "取消,等会刷新", confirmButtonText: "确定,立即刷新", }).then((result) => { if (result.isConfirmed) { location.reload(); } }); } console.log("操作成功!!!!"); }catch (e) { console.error(e); if(Swal){ Swal.fire({ title: "错误!", text: e.toString()+"请在视频播放页面使用!!!", icon: 'error', confirmButtonColor: "#eb00fd", confirmButtonText: "确定", }) } } } //样式 let style = `.button-3 { appearance: none; background-color: #e52b13; border: 1px solid rgba(27, 31, 35, .15); border-radius: 6px; box-shadow: rgba(27, 31, 35, .1) 0 1px 0; box-sizing: border-box; color: #ffffff; cursor: pointer; display: inline-block; font-family: -apple-system,system-ui,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji"; font-size: 14px; font-weight: 600; line-height: 20px; padding: 6px 16px; position: absolute; left: 20px; top: 300px; text-align: center; text-decoration: none; user-select: none; -webkit-user-select: none; touch-action: manipulation; vertical-align: middle; white-space: nowrap; } .button-3:focus:not(:focus-visible):not(.focus-visible) { box-shadow: none; outline: none; } .button-3:hover { background-color: #2c974b; } .button-3:focus { box-shadow: rgba(46, 164, 79, .4) 0 0 0 3px; outline: none; } .button-3:disabled { background-color: #94d3a2; border-color: rgba(27, 31, 35, .1); color: rgba(255, 255, 255, .8); cursor: default; } .button-3:active { background-color: #298e46; box-shadow: rgba(20, 70, 32, .2) 0 1px 0 inset; }` window.onload = () => { let myStyle = document.createElement('style') myStyle.innerHTML = style; document.head.appendChild(myStyle); /*let intercept=GM_GetValue*/ let div = document.createElement('div'); div.innerHTML = `
即刻开刷
2222
` document.body.appendChild(div); let isClick = false; let my1 = document.getElementById('my1') let my2 = document.getElementById('my2') my1.addEventListener("click", () => { isClick = !isClick; if (isClick) { const courseId = initCourseInfo() my1.innerText = "刷课中" run(courseId) my1.innerText = "点击开刷" } else { my1.innerText = "点击开刷" } }) my2.addEventListener("click", () => { if(Swal){ Swal.fire({ title: "彩蛋OvO", icon: 'info', confirmButtonColor: "rgb(253,0,135)", confirmButtonText: "感谢~", html: `2222
小小彩蛋,制作不易!
为保证每个人都能有更好的体验,恳请大家合理使用哦。
若您觉得脚本还不错,不妨留下您的 好评与鼓励
您的每一份支持,都是我不断前行的动力!🧡💪
感谢您的理解与支持!😊✨
点击前往评论` }) } }) } })();