Last active
September 11, 2025 01:17
-
-
Save zhangyingda/7c0591d065b3a6a7ae9c04cb332d2152 to your computer and use it in GitHub Desktop.
2025暑期研修.user.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name 2025国家智慧中小学-暑期研修(免费,秒刷),各类继续教育/定制【山东山西广东、河北、湖南、四川、吉林继教、重庆赤峰宁夏包头梅河口青岛等专技、现代远程教育、双融双创、超星,好医生,教育干部,智慧普法等,文档查看更多 | |
| // @namespace http://tampermonkey.net/zzzzzzys_国家中小学 | |
| // @version 2.8.8 | |
| // @copyright zzzzzzys.All Rights Reserved. | |
| // @description 适用2025国家智慧教育平台、山东教师教育、河北继续教育等.【河北继续教育(师学通、奥鹏、电视台、高教社等)】【吉林继教(中盛佳源|)】【中小学D校】【国家开发大学】【四川继教、四川创联】【重庆、内蒙古、赤峰、宁夏、包头、梅河口、桦甸教育、中山专技(chinahrt、chinamde)等软件】【广东双融双创、继续教育】【人教社义教】【云继教】【沃希学苑(山东中小学人工智能研修包含考试)】【名师学堂】【中山教师研修】【河北专业技术人员继续教育、湖南师范大学专业技术人员继续教育网】【广西广东干部网络学院、山东灯塔网络学院、凉山专技继续教育】【湖南人社】,凉山、河南专技、鸡西教师平台、民用无人驾驶航空器管理平台,好医生,中国教育干部、法宣在线、吉林高邦等自动化挂机/刷课 更多请前往:https://zzzzzzys.lovestoblog.com/,还有软件支持更加便捷的学习课程!注意:禁止二次发布!加QQ群获取更新 | |
| // @author zzzzzzys | |
| // @match *://basic.smartedu.cn/* | |
| // @match *://core.teacher.vocational.smartedu.cn/* | |
| // @match *://test3.ykt.eduyun.cn/* | |
| // @match *://pn202413060.stu.teacher.com.cn/studyPlan/* | |
| // @match *://pn202413060.stu.teacher.com.cn/course/* | |
| // @match *://cn202511002.stu.t-px.cn/* | |
| // @match *://cas.study.yanxiu.jsyxsq.com/auth/selfHost/studyPlace/index.html* | |
| // @match *://learn.ourteacher.com.cn/StepLearn/StepLearn/* | |
| // @match *://vc.chinabett.com/studyduration/index* | |
| // @match *://www.ttcdw.cn/p* | |
| // @match *://*.besteacher.com.cn/activity/curriculum/* | |
| // @match *://*.webtrn.cn/learnspace/learn/learn/templateeight/index.action* | |
| // @match *://cqrl.21tb.com/els/html/courseStudyItem/courseStudyItem.learn.do* | |
| // @match *://*.nmgdbrc.com/* | |
| // @match *://wp.pep.com.cn/web/index.php?/px/* | |
| // @match *://bjpep.gensee.com/webcast/site/vod/* | |
| // @match *://srsc.gdedu.gov.cn/course/study* | |
| // @match *://gp.chinahrt.com/index.html* | |
| // @match *://videoadmin.chinahrt.com/videoPlay/playEncrypt* | |
| // @match *://saas.yunteacher.com/module/* | |
| // @match *://saas.yunteacher.com/coursePlay* | |
| // @match *://jlzj.ylxue.net/LearningCenter/LearningCourseVideo* | |
| // @match *://*.chinamde.cn/play/* | |
| // @match *://p.bokecc.com/playhtml.bo* | |
| // @match https://jsxx.gdedu.gov.cn/*/study/course/* | |
| // @match https://jsxx.gdedu.gov.cn/study/course/* | |
| // @match https://m.zsjsjy.com/teacher/train/train/online/study.do* | |
| // @match https://trplayer.sctce.cn/* | |
| // @match https://study.seewoedu.cn/tCourse/group/* | |
| // @match https://cpb-m.cvte.com/* | |
| // @match https://saas.mingshiclass.com/* | |
| // @require https://scriptcat.org/lib/637/1.4.6/ajaxHooker.js#sha256=FBIJAmqSt3/bUHAiAFBFd2YvGHENrBQGfe1b4c+UBYs= | |
| // @require https://scriptcat.org/lib/637/1.4.6/ajaxHooker.js | |
| // @require https://fastly.jsdelivr.net/npm/crypto-js@4.2.0/crypto-js.min.js | |
| // @resource https://cdn.staticfile.org/limonte-sweetalert2/11.7.1/sweetalert2.min.css | |
| // @require https://fastly.jsdelivr.net/npm/sweetalert2@11.12.2/dist/sweetalert2.all.min.js | |
| // @connect basic.smartedu.cn | |
| // @connect x-study-record-api.ykt.eduyun.cn | |
| // @connect fc-mp-8ba0e2a3-d9c9-45a0-a902-d3bde09f5afd.next.bspapp.com | |
| // @connect mp-8ba0e2a3-d9c9-45a0-a902-d3bde09f5afd.cdn.bspapp.com | |
| // @connect manage.yzspeixun.com | |
| // @connect videoadmin.chinahrt.com | |
| // @connect api.mingshiclass.com | |
| // @connect cpb-m.cvte.com | |
| // @grant unsafeWindow | |
| // @grant GM_getValue | |
| // @grant GM_setValue | |
| // @grant GM_deleteValue | |
| // @grant GM_xmlhttpRequest | |
| // @grant GM_info | |
| // @grant GM_openInTab | |
| // @grant GM_addStyle | |
| // @run-at document-end | |
| // ==/UserScript== | |
| const web_url="https://zzzzzzys.lovestoblog.com/" | |
| const web_list = [ | |
| { name: "备用地址1", url: "https://zzzzzzys.lovestoblog.com/" }, | |
| ] | |
| class ScriptCore { | |
| constructor() { | |
| this.modules = new Map(); | |
| this.initModules(); | |
| this.execute(); | |
| } | |
| initModules() { | |
| // 多站点匹配配置 | |
| this.modules.set('国家智慧教育平台', { | |
| match: [ | |
| /^(https?:\/\/)?(basic\.smartedu\.cn)/, | |
| /^(https?:\/\/)?(core\.teacher\.vocational\.smartedu\.cn)/, | |
| /^(https?:\/\/)?(test3\.ykt\.eduyun\.cn)/, | |
| /localhost:\d+(\/.*)?$/ // 本地开发环境 | |
| ], | |
| module: SmartEduModule, | |
| config: { | |
| refreshInterval: 5000, | |
| apiEndpoints: { | |
| }}}); | |
| } | |
| execute() { | |
| const currentUrl = new URL(window.location.href); | |
| for (const [moduleName, { match, module: Module, config }] of this.modules) { | |
| if (this.matchChecker(currentUrl, match)) { | |
| Logger.moduleLoaded(moduleName) | |
| const executor = () => new Module().run(config); | |
| if (config.runAt && config.runAt === 'document-start') { | |
| executor(); | |
| } else { | |
| // 延迟到DOM加载完成后执行 | |
| if (document.readyState === 'loading') { | |
| window.addEventListener('DOMContentLoaded', executor); | |
| } else { | |
| executor(); // 兜底:如果已经加载完成则直接执行 | |
| } | |
| } | |
| return; // 单例模式运行 | |
| } | |
| } | |
| console.warn('[Core] 未找到匹配模块'); | |
| } | |
| matchChecker(currentUrl, matcher) { | |
| // 处理多种匹配类型 | |
| if (Array.isArray(matcher)) { | |
| return matcher.some(pattern => | |
| pattern instanceof RegExp ? pattern.test(currentUrl.href) | |
| : typeof pattern === 'function' ? pattern(currentUrl) | |
| : false | |
| ); | |
| } | |
| return typeof matcher === 'function' | |
| ? matcher(currentUrl) | |
| : matcher.test(currentUrl.href); | |
| } | |
| } | |
| class Logger { | |
| static #styles = { | |
| core: ['font-size: 11px', 'font-family: monospace', 'padding: 2px 8px', 'border-radius: 4px', 'background: linear-gradient(145deg, #2196F3 20%, #1976D2)', 'color: white', 'text-shadow: 0 1px 1px rgba(0,0,0,0.3)'].join(';'), | |
| module: ['background: #FFEB3B', 'color: #212121', 'padding: 1px 4px', 'border-radius: 2px', 'margin-left: 4px'].join(';'), | |
| status: ['background: #4CAF50', 'color: white', 'padding: 1px 6px', 'border-left: 2px solid #388E3C'].join(';') | |
| }; | |
| static moduleLoaded(name) { | |
| const timestamp = performance.now().toFixed(2); | |
| try { | |
| Swal.fire({title: '<span style="font-size:1.5em; color:#FF4DAF;">🎉 脚本加载成功!</span>', html: ` <div style="text-align:left; line-height:1.6;"> <p style="font-size:1.1em; margin-bottom:15px;">✅ 脚本已正确加载!</p> <div style="background:#f8f9fa; padding:12px; border-radius:8px;"> <p style="color:#666; margin:5px 0;">⚠️ 如未加载成功:</p> <ul style="margin:5px 0; padding-left:20px;"> <li>请尝试使用 <strong style="color:#FF4DAF;">篡改猴插件</strong></li> <li>脚本猫可能导致兼容性问题</li> <li>同时使用时需关闭脚本猫</li> </ul> </div> <p style="margin-top:20px;"> <a href="https://zzzzzzys.lovestoblog.com/" target="_blank" style="color:#FF4DAF; text-decoration:underline;"> 🔗 https://zzzzzzys.lovestoblog.com/查看更多适配网站 </a> </p> </div> `, icon: 'success', width: '800px', padding: '2em', customClass: {popup: 'custom-swal-popup', title: 'custom-swal-title', htmlContainer: 'custom-swal-html'}, confirmButtonColor: "#FF4DAF", confirmButtonText: '<span style="font-size:1.1em;">🚀 关闭弹窗</span>', showCloseButton: true, timerProgressBar: false, backdrop: 'rgba(0,0,0,0.7)'}); }catch (e) { | |
| console.error(e); | |
| } | |
| console.log( | |
| `%cCORE%c${name}%c ✔ LOADED %c+${timestamp}ms`, | |
| this.#styles.core, | |
| this.#styles.module, | |
| this.#styles.status, | |
| 'color: #757575; font-size: 0.8em;' | |
| );}} | |
| const web_id="68809edc4b92476e9720044a" | |
| class SmartEduModule { | |
| constructor() { | |
| } | |
| run(config) { | |
| this.setupCoreFeatures(config); | |
| } | |
| setupCoreFeatures({refreshInterval}) { | |
| const qqGroup = [ | |
| {customName: "群1", id: "570337037", link: "https://qm.qq.com/q/rDCbvTiV9K", isFull: true, priority: 0}, | |
| ] | |
| const originalXHR = unsafeWindow.XMLHttpRequest; | |
| let fullDatas = null | |
| ajaxHooker.filter([ | |
| {url: 'fulls.json'} | |
| ]) | |
| ajaxHooker.hook(request => { | |
| if (request.url.includes('fulls.json')) { | |
| request.response = res => { | |
| console.log(res); | |
| fullDatas = JSON.parse(res.responseText); | |
| }; | |
| } | |
| }); | |
| const renderQQGroups = () => { | |
| try { | |
| const activeGroups = qqGroup | |
| .filter(group => { | |
| // 添加数据校验 | |
| if (!group.customName || !group.id) { | |
| console.warn('Invalid group:', group); | |
| return false; | |
| } | |
| return !group.isFull; | |
| }) | |
| .sort((a, b) => a.priority - b.priority); | |
| // 添加空状态提示 | |
| if (activeGroups.length === 0) { | |
| return `<div style="color: #ff9999; text-align:center; margin:12px 0"></div>`; | |
| } | |
| const title = `<div style="background: linear-gradient(135deg, #FF4DAF 0%, #FF6B6B 100%);display: flex; align-items: center; gap:15px;"> | |
| <img src="https://qzonestyle.gtimg.cn/qzone/qzact/act/external/tiqq/logo.png" style="height:36px; border-radius:6px;"> | |
| <div> | |
| <div style="font-size:12px; opacity:0.9;">JavaScript</div> | |
| </div> | |
| </div>` | |
| let content = title + activeGroups.map(group => ` <a href="${group.link}" target="_blank" style="display: block; margin-top: 12px; padding: 10px; background: rgba(255,255,255,0.2); border-radius: 6px; text-align: center; text-decoration: none; color: white !important; transition: 0.3s; font-weight: 500; cursor: pointer;" aria-label="加入QQ群${group.customName}(群号:${group.id})"> 🎯 点击加入${group.customName}:${group.id} <!-- 移除群号显示 --> </a> `).join(''); | |
| return `<div style="background: linear-gradient(135deg, #FF4DAF 0%, #FF6B6B 100%); padding:15px; border-radius:8px; color:white;"> | |
| ${content} | |
| </div>` | |
| } catch (error) { | |
| console.error('QQ群渲染错误:', error); | |
| return ''; // 静默失败 | |
| } | |
| }; | |
| let requestObj = { | |
| fullsData: { | |
| url: "https://s-file-2.ykt.cbern.com.cn/teach/s_course/v2/activity_sets/3efdb592-138e-4854-8964-5e10f6011f33/fulls.json", | |
| method: "GET", | |
| }, resourceLearningPositions: { | |
| url: "https://x-study-record-api.ykt.eduyun.cn/v1/resource_learning_positions/", method: "PUT" | |
| }, /* 职业教育 | 高等教育 */ | |
| progress: { | |
| url: "https://core.teacher.vocational.smartedu.cn/p/course/services/member/study/progress", | |
| method: "POST", | |
| } | |
| } | |
| /******************************************************** | |
| * 职业教育/高等教育 | |
| *******************************************************/ | |
| const SWAL_CONFIG = { | |
| title: '课程进度控制', html: ` <div style="margin-bottom: 5px"> <label>v${GM_info.script.version}</label> </div> <div style=" padding: 12px; background: #e8f4ff; border-radius: 8px; margin-bottom: 15px; border: 1px solid #b3d4fc; text-align: center; "> <span style=" font-size: 14px; color: #ff4daf; display: inline-flex; align-items: center; gap: 6px; "> <span style="font-size: 16px">🎯</span> 老师您好,点击开始按钮,开始减负之旅<br> 脚本会自动学习当前页所有视频,您可安心休息片刻 </span> </div> <div style="margin-bottom: 15px"> <label>当前视频:</label> <div id="currentVideo" style=" font-size: 16px; color: #3498db; font-weight: 500; margin: 8px 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; ">尚未开始</div> </div> <div class="progress-container" style=" background: #f0f0f0; height: 20px; border-radius: 10px; margin: 15px 0; overflow: hidden; "> <div id="swalProgressBar" style=" height: 100%; background: linear-gradient(90deg, #4CAF50 0%, #8BC34A 100%); width: 0; transition: width 0.3s ease; "></div> </div> <div style=" display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 15px; "> <div> <label>当前进度</label> <div id="currentProgress" style=" font-size: 18px; font-weight: bold; color: #2c3e50; ">0:00</div> </div> <div> <label>大概需要时间</label> <div id="needTime" style=" font-size: 14px; color: #2efd00; ">还未开始</div> </div> <div> <label>总时长</label> <div id="totalTime" style=" font-size: 14px; color: #7f8c8d; ">还未开始</div> </div> </div> <div id="statusMessage" style=" padding: 10px; border-radius: 5px; margin: 10px 0; background: #f8f9fa; text-align: center; ">准备就绪</div> <div style=" padding: 12px; background: #f5f7fa; border-radius: 8px; margin: 12px 0; border: 1px solid #e4e7ed; "> ${renderQQGroups()} </div> <div id="author" style=" padding: 8px 16px; /* 适当的上下左右内边距 */ border-radius: 10px; margin: 10px 0; background: #f8f9fa; text-align: center; font-size: 12px; /* 稍微增大字体 */ font-weight: bold; /* 加粗字体 */ color: #495057; /* 更深的字体颜色,增强可读性 */ border: 1px solid #dee2e6; /* 添加边框 */ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); /* 轻微阴影效果 */ letter-spacing: 1px; /* 增加字母间距 */ "> By YoungthZou. 盗码可耻! zzzzzzys </div> `, showConfirmButton: false, allowOutsideClick: false, allowEscapeKey: false, width: 600, willOpen: () => { | |
| document.querySelector('.swal2-close').remove(); | |
| } | |
| }; | |
| // 状态管理 | |
| let currentProgress = 60; | |
| let isRunning = false; | |
| let swalInstance = null; | |
| let totalTime = 1000; | |
| let checkInterval = null | |
| // 工具函数 | |
| const formatTime = (seconds) => { | |
| const mins = Math.floor(seconds / 60); | |
| const secs = seconds % 60; | |
| return `${mins}:${secs.toString().padStart(2, '0')}`; | |
| }; | |
| const updateUI = (progress, status) => { | |
| if (!swalInstance) return; | |
| const progressBar = swalInstance.querySelector('#swalProgressBar'); | |
| const percent = (progress / totalTime * 100).toFixed(1); | |
| progressBar.style.width = `${Math.min(parseFloat(percent), 100)}%`; | |
| swalInstance.querySelector('#currentProgress').textContent = formatTime(progress); | |
| swalInstance.querySelector('#totalTime').textContent = formatTime(totalTime); | |
| swalInstance.querySelector('#needTime').textContent = formatTime(parseInt(((totalTime - progress) / 3).toFixed(0))); | |
| const statusEl = swalInstance.querySelector('#statusMessage'); | |
| statusEl.textContent = {loading: '🔄 正在同步进度...', success: '✅ 同步成功,stand by...', error: '❌ 同步失败(长时间失败,请反馈)', idle: '⏸ 已暂停', finished: '✅已学完,跳过...', finishAll: '已全部学完,请手动刷新,给个好评吧~', next: "🔄 此视频已学完,准备学习下一个..."}[status] || '准备就绪'; | |
| statusEl.style.color = {loading: '#f39c12', success: '#2ecc71', error: '#e74c3c', idle: '#7f8c8d', finished: '#0022fd', finishAll: '#ff4daf', next: '#f39c12',}[status]; | |
| }; | |
| const sendProgress = async (videoId) => { | |
| updateUI(currentProgress, 'loading'); | |
| let oriData = { | |
| courseId: unsafeWindow.courseId, | |
| itemId: unsafeWindow.p.itemId, | |
| videoId: videoId, | |
| playProgress: currentProgress, | |
| segId: unsafeWindow.p.segId, | |
| type: unsafeWindow.p.type, | |
| tjzj: 1, | |
| clockInDot: currentProgress,//后台要求此参数为视频播放的位置 | |
| sourceId: unsafeWindow.p.projectId, | |
| timeLimit: unsafeWindow.timilistParam.timeLimit || -1, | |
| originP: unsafeWindow.p.originP === 1 ? 2 : 1, // 硬编码,等待修改 | |
| } | |
| try { | |
| const response = await fetch(`${requestObj.progress.url}?orgId=${unsafeWindow.p.orgId}`, { | |
| method: "POST", headers: { | |
| "content-type": "application/x-www-form-urlencoded; charset=UTF-8", | |
| "x-requested-with": "XMLHttpRequest", | |
| "u-platformId": unsafeWindow.platformInfo.id | |
| }, credentials: "include", body: new URLSearchParams(oriData) | |
| }); | |
| const data = await response.json(); | |
| console.log(data) | |
| if (data.data?.videoProgress > 0) { | |
| currentProgress = parseInt(data.data.videoProgress); | |
| updateUI(currentProgress, 'success'); | |
| return data.data.progress; | |
| } else { | |
| throw new Error('无效的服务器响应'); | |
| } | |
| } catch (error) { | |
| console.error('请求失败:', error); | |
| updateUI(currentProgress, 'error'); | |
| } | |
| }; | |
| // 创建控制界面 | |
| function createControlPanel() { | |
| console.log('Swal create control panel') | |
| unsafeWindow.DialogRuning=true | |
| Swal.fire({ | |
| ...SWAL_CONFIG, didOpen: (modal) => { | |
| swalInstance = modal; | |
| const actions = document.createElement('div'); | |
| actions.style = `display: grid;grid-template-columns: 1fr 1fr;gap: 10px;margin-top: 15px;`; | |
| const startBtn = createButton('▶ 开始', '#2ecc71', async () => { | |
| if (!isRunning) { | |
| try { | |
| try { | |
| document.querySelector('video').pause() | |
| } catch (e) {} | |
| isRunning = true; | |
| startBtn.textContent = '⏸ 暂停'; | |
| startBtn.style.background = '#e74c3c'; | |
| let courseData = getCourseData(); | |
| for (const courseDatum of courseData) { | |
| if (!isRunning) { | |
| return | |
| } | |
| const videoId=courseDatum.videoId | |
| if (!videoId) continue; | |
| await sleep(2000) | |
| console.log(courseDatum.name) | |
| swalInstance.querySelector('#currentVideo').textContent = courseDatum.name | |
| currentProgress = 0; | |
| totalTime = parseInt(courseDatum.duration); | |
| if (parseInt(courseDatum.progress) === 1) { | |
| console.log(" 已学完,跳过...") | |
| updateUI(currentProgress, 'finished'); | |
| continue; | |
| } | |
| do { | |
| const progress = await sendProgress(videoId, currentProgress); // 立即执行 | |
| if (progress === "1.0") { | |
| break; | |
| } | |
| await interruptibleWait(21000); | |
| } while (currentProgress < totalTime && isRunning) | |
| updateUI(currentProgress, 'next'); | |
| await sleep(20000); | |
| } | |
| // 非暂停结束 | |
| if (isRunning) { | |
| currentProgress = 1; | |
| totalTime = 1; | |
| updateUI(currentProgress, 'finishAll'); | |
| startBtn.textContent = '▶ 开始'; | |
| startBtn.style.background = '#2ecc71'; | |
| } | |
| } catch (e) { | |
| console.error(e) | |
| if (Swal) { | |
| Swal.fire({ | |
| title: "失败!", | |
| text: e.toString() + "请在视频播放页面使用!!!", | |
| icon: 'error', // showCancelButton: true, | |
| confirmButtonColor: "#FF4DAFFF", // cancelButtonText: "取消,等会刷新", | |
| confirmButtonText: "点击去反馈", | |
| }).then((result) => { | |
| if (result.isConfirmed) { | |
| window.open("https://greasyfork.org/zh-CN/scripts/525037/feedback") | |
| } | |
| }); | |
| } | |
| } finally { | |
| isRunning = false; | |
| } | |
| } else { | |
| isRunning = false; | |
| startBtn.textContent = '▶ 继续'; | |
| startBtn.style.background = '#2ecc71'; | |
| if (checkInterval) { | |
| clearTimeout(checkInterval.timer); | |
| checkInterval.resolve(); // 立即结束等待 | |
| } | |
| updateUI(currentProgress, 'idle'); | |
| setTimeout(() => { | |
| updateUI(currentProgress, 'idle'); | |
| }, 2000) | |
| } | |
| }); | |
| const resetBtn = createButton('→去好评', '#dbba34', () => { | |
| window.open("https://scriptcat.org/zh-CN/script-show-page/2789/comment") | |
| }); | |
| actions.append(startBtn, resetBtn); | |
| modal.querySelector('.swal2-html-container').append(actions); | |
| } | |
| }); | |
| } | |
| const sleep = function (time) { | |
| return new Promise(resolve => setTimeout(resolve, time)); | |
| } | |
| function interruptibleWait(ms) { | |
| return new Promise(resolve => { | |
| const timer = setTimeout(resolve, ms); | |
| // 暴露清除方法以便立即暂停 | |
| checkInterval = {timer, resolve}; | |
| }); | |
| } | |
| function createButton(text, color, onClick) { | |
| const btn = document.createElement('button'); | |
| btn.textContent = text; | |
| btn.style = `padding: 10px 15px;border: none;border-radius: 5px;background: ${color};color: white;cursor: pointer;transition: opacity 0.3s;`; | |
| btn.addEventListener('click', onClick); | |
| btn.addEventListener('mouseenter', () => btn.style.opacity = 0.8); | |
| btn.addEventListener('mouseleave', () => btn.style.opacity = 1); | |
| return btn; | |
| } | |
| function getCourseData() { | |
| let courseData = unsafeWindow.initlessons | |
| console.log(courseData) | |
| if (!courseData) { | |
| updateUI(currentProgress, 'error'); | |
| console.error("no course data!"); | |
| return | |
| } | |
| courseData = courseData.filter(item => { | |
| return item?.type !== "1"; | |
| }); | |
| return [...courseData]; | |
| } | |
| /******************************************************** | |
| * 打赏 | |
| *******************************************************/ | |
| GM_addStyle(`.donate-panel { position: fixed; left: 30%; top:50%; background: linear-gradient(135deg, #fff5f5 0%, #fff0f7 100%); border-radius: 16px; box-shadow: 0 8px 32px rgba(255, 77, 175, 0.2); padding: 24px; width: 520px; z-index: 2147483647; transform: translateY(-100); /* 初始隐藏位置 */ opacity: 1; /* 确保初始可见性 */ border: 1px solid #ffe6f0; backdrop-filter: blur(8px); transition: none; /* 禁用transition改用animation */ }.donate-header { position: relative; font-size: 18px; color: #ff4daf; margin-bottom: 20px; font-weight: 600; display: flex; align-items: center; gap: 12px; padding-bottom: 12px; border-bottom: 2px solid rgba(255, 77, 175, 0.1); } .donate-header::after { content: "✨"; position: absolute; right: 0; top: -8px; font-size: 24px; animation: sparkle 2s infinite; } .motivation-text { font-size: 13px; color: #666; line-height: 1.6; margin: 12px 0; background: rgba(255, 255, 255, 0.9); padding: 12px; border-radius: 8px; border: 1px solid #ffebf3; } @keyframes heartbeat { 0% { transform: scale(1); } 50% { transform: scale(1.05); } 100% { transform: scale(1); } } @keyframes sparkle { 0% { opacity: 0.3; } 50% { opacity: 1; } 100% { opacity: 0.3; } } @keyframes panelSlideIn { from { transform: translateY(100%); opacity: 0; } to { transform: translateY(-50%); opacity: 1; } } @keyframes panelSlideOut { from { transform: translateY(0); opacity: 1; } to { transform: translateY(100%); opacity: 0; } } @keyframes heartbeat { 0% { transform: scale(1); } 50% { transform: scale(1.1); } 100% { transform: scale(1); } } .qr-grid { display: grid; grid-template-columns: 1fr; /* 改为单列布局 */ gap: 24px; margin: 24px auto; max-width: 300px; /* 增大容器宽度 */ } .qr-item { position: relative; overflow: hidden; border-radius: 12px; transition: 0.3s; padding: 12px; /* 增加内边距 */ background: #fff; box-shadow: 0 4px 12px rgba(255, 77, 175, 0.1); } .qr-item:hover { transform: translateY(-4px); box-shadow: 0 6px 16px rgba(255, 77, 175, 0.2); } .qr-item img { width: 100%; height: auto; /* 保持比例 */ border-radius: 8px; border: 1px solid #ffe5f0; min-height: 280px; /* 最小高度保证 */ } .qr-item p { text-align: center; margin: 16px 0 8px; font-size: 16px; /* 增大文字 */ color: #ff4daf; font-weight: 600; } /* 新增文字样式 */ .qr-tips { text-align: center; margin: 8px 0; font-size: 14px; color: #ff7ab8; /* 更柔和的粉色 */ } .qr-proverb { font-style: italic; color: #ff9ec7; /* 更浅的粉色 */ font-size: 13px; margin-top: 4px; } /* 修改原有.qr-item p样式 */ .qr-item p { margin: 12px 0 4px; /* 减小下边距 */ /* 其他样式保持不变 */ } /* 手机横屏/平板适配 */ @media (min-width: 600px) { .qr-grid { grid-template-columns: 1fr 1fr; /* 大屏幕恢复双列 */ max-width: 600px; } .qr-item img { min-height: 240px; } } .third-party { margin-top: 20px; } .platform-btn { display: flex; align-items: center; justify-content: center; gap: 8px; padding: 12px; background: linear-gradient(135deg, #fff0f5 0%, #fff8fb 100%); border-radius: 8px; text-decoration: none; color: #ff6699 !important; font-size: 14px; margin: 8px 0; transition: 0.3s; border: 1px solid #ffe6ee; } .donate-panel.active { animation: panelSlideIn 0.4s cubic-bezier(0.22, 0.61, 0.36, 1) forwards; } .donate-panel.exit { animation: panelSlideOut 0.3s ease forwards; } /* 触发按钮动画 */ #donate-trigger { animation: heartbeat 1.8s ease-in-out infinite; } .platform-btn:hover { background: linear-gradient(135deg, #ffe6ee 0%, #fff1f7 100%); box-shadow: 0 4px 12px rgba(255, 77, 175, 0.1); } .close-btn { /* 保持原有样式 */ }`); | |
| // 激励文案库 | |
| const motivationTexts = ["您的每一份支持都将转化为:", "❤️ 服务器续费 ", "🛠️ 持续开发维护 ", "☕ 深夜码农的咖啡燃料", "🐈 小猫最爱的水煮鸡胸肉",]; | |
| // 动态生成激励文案 | |
| function generateMotivation() { | |
| const fragments = ['<div class="motivation-text">', '🌟 <strong>感谢使用本脚本!</strong>', ...motivationTexts.map(t => `• ${t}`), '</div>'].join('<br>'); | |
| return fragments | |
| .replace('${donateCount}', '1,234') | |
| .replace('${updateDays}', '365'); | |
| } | |
| // 打赏面板HTML结构 | |
| const donateHTML = ` | |
| <div id="donate-panel"> ${generateMotivation()} <div class="donate-header"> <svg viewBox="0 0 24 24" width="20" height="20" fill="#1e62ec"> <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/> </svg> 支持开发者 </div> <div class="qr-grid"> <div class="qr-item"> <p>微信扫码支持</p> <img style="width: 200px;height: 266px" src="https://mp-8ba0e2a3-d9c9-45a0-a902-d3bde09f5afd.cdn.bspapp.com/monkey-pic/wechat2.jpg" alt="微信赞赏码"> <div class="qr-tips"> <p>❤️持续创作需要您的支持</p> <p class="qr-proverb">星火相聚,终成光芒</p> </div> </div> <div class="qr-item"> <p>支付宝扫码支持</p> <img style="width: 200px;height: 266px" src="https://mp-8ba0e2a3-d9c9-45a0-a902-d3bde09f5afd.cdn.bspapp.com/monkey-pic/alipay2.jpg" alt="支付宝收款码"> <div class="qr-tips"> <p>🌸每一份心意都值得珍惜</p> <p class="qr-proverb">不啻微芒,造矩成阳</p> </div> </div> </div> <div class="donate-header"> <svg viewBox="0 0 24 24" width="20" height="20" fill="#1e62ec"> <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/> </svg> 感谢您的支持! </div> <div class="third-party"> <!--<a href="https://afdian.net/@yourid" class="platform-btn" target="_blank"> <svg viewBox="0 0 1024 1024" width="14" height="14" style="vertical-align:-2px;"> <path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372zm218-572.1h-50.4c-4.4 0-8 3.6-8 8v384.2c0 4.4 3.6 8 8 8h145.7c4.4 0 8-3.6 8-8V319.9c0-4.4-3.6-8-8-8h-50.4c-4.4 0-8 3.6-8 8v151.7H730V319.9c0-4.4-3.6-8-8-8zM328.1 703.9c-4.4 0-8-3.6-8-8v-384c0-4.4 3.6-8 8-8h50.4c4.4 0 8 3.6 8 8v151.7h116.7V319.9c0-4.4 3.6-8 8-8h50.4c4.4 0 8 3.6 8 8v384.2c0 4.4-3.6 8-8 8h-145c-4.4 0-8-3.6-8-8v-151H344v151c0 4.4-3.6 8-8 8H328.1z"/> </svg> 爱发电支持 </a>--> <div class="platform-btn" id="donate-panel-close">感谢开发者,已支持~</div> </div> </div> | |
| `; | |
| // 初始化打赏面板 | |
| function initDonate() { | |
| if (document.getElementById('donate-panel')) return; | |
| const panel = document.createElement('div'); | |
| panel.innerHTML = donateHTML; | |
| panel.className = 'donate-panel'; | |
| document.body.appendChild(panel); | |
| // 强制重排触发动画 | |
| void panel.offsetWidth; // 触发CSS重绘 | |
| panel.classList.add('active'); | |
| // 关闭按钮事件 | |
| panel.querySelector('#donate-panel-close').addEventListener('click', () => { | |
| panel.classList.remove('active'); | |
| panel.classList.add('exit'); | |
| panel.addEventListener('animationend', () => { | |
| panel.remove(); | |
| }, {once: true}); | |
| }); | |
| // 点击外部关闭 | |
| const clickHandler = (e) => { | |
| if (!panel.contains(e.target) && e.target.id !== 'donate-trigger') { | |
| panel.classList.add('exit'); | |
| panel.addEventListener('animationend', () => { | |
| panel.remove(); | |
| }, {once: true}); | |
| document.removeEventListener('click', clickHandler); | |
| } | |
| }; | |
| setTimeout(() => document.addEventListener('click', clickHandler), 100); | |
| } | |
| // 显示触发按钮 | |
| const trigger = document.createElement('div'); | |
| trigger.innerHTML = '❤️ 打赏支持'; | |
| Object.assign(trigger.style, { | |
| position: 'fixed', left: '10px', top: '415px', background: '#ff6b6b', color: 'white', padding: '8px 16px', borderRadius: '20px', cursor: 'pointer', zIndex: '999999999999999', boxShadow: '0 2px 8px rgba(0,0,0,0.2)', fontSize: '14px' | |
| }); | |
| // 触发按钮增强 | |
| Object.assign(trigger.style, { | |
| background: 'linear-gradient(135deg, #ff4daf 0%, #ff6b6b 100%)', fontWeight: '600', padding: '12px 24px', boxShadow: '0 4px 24px rgba(255, 77, 175, 0.3)', animation: 'heartbeat 1.5s ease-in-out infinite', border: '1px solid #ffb3d9' | |
| }); | |
| trigger.addEventListener('click', initDonate); | |
| //document.body.appendChild(trigger); | |
| /******************************************************** | |
| * 中小学智慧教育平台 * 寒假研修 | |
| *******************************************************/ | |
| //样式 | |
| let style = `.button-3 { position: fixed; appearance: none; background-color: #ed5822; 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; 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; z-index: 2147483647; } .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; }` | |
| const createFloatingButton = () => { | |
| // 如果按钮已存在则先移除旧实例 | |
| const existingBtn = document.getElementById('zs-helper-btn'); | |
| if (existingBtn) existingBtn.remove(); | |
| // 直接创建按钮元素(去掉外层div嵌套) | |
| const btn = document.createElement('div'); | |
| btn.id = 'zs-helper-btn'; // 确保唯一ID直接设置在元素上 | |
| btn.style.cssText = ` position: fixed; left: 10px; top: 250px; transform: translateY(-50%); background: #ed5822; color: white; padding: 12px 24px; border-radius: 30px; cursor: pointer; box-shadow: 0 4px 12px rgba(255,77,175,0.3); z-index: 2147483647; /* 使用最大z-index值 */ transition: 0.3s; font-family: 'Microsoft Yahei', sans-serif; white-space: nowrap; display: flex; align-items: center; gap: 8px; `; | |
| // 添加内部HTML内容 | |
| btn.innerHTML = ` | |
| <svg style="width:18px;height:18px;fill:white;" viewBox="0 0 24 24"> | |
| <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z"/> | |
| </svg> | |
| <span>使用指南</span> | |
| `; | |
| // 使用更可靠的事件监听方式 | |
| const handleHover = () => { | |
| btn.style.transform = 'translateY(-50%) scale(1.05)'; | |
| btn.style.boxShadow = '0 6px 16px rgba(255,77,175,0.4)'; | |
| }; | |
| const handleLeave = () => { | |
| btn.style.transform = 'translateY(-50%) scale(1)'; | |
| btn.style.boxShadow = '0 4px 12px rgba(255,77,175,0.3)'; | |
| }; | |
| btn.addEventListener('mouseenter', handleHover); | |
| btn.addEventListener('mouseleave', handleLeave); | |
| btn.addEventListener('click', showGuideDialog); | |
| //document.body.appendChild(btn); | |
| return btn; | |
| }; | |
| // 显示操作指南弹窗 | |
| const showGuideDialog = () => { | |
| if (Swal) { | |
| Swal.fire({title: `<span style="color: #FF4DAF; font-size:26px; display: flex; align-items: center; gap:8px;">📚 智能刷课指南 <div style="font-size:12px; color:#95a5a6; margin-left:auto;">v${GM_info.script.version}</div></span>`, html: ` <div style="text-align: left; max-width: 720px; line-height: 1.8;"> <!-- 操作步骤 --> <div style="background: #f8f9fa; padding: 15px; border-radius: 8px; margin-bottom: 20px;"><div style="color: blue; font-weight:800; margin-bottom:10px;"> 需要更高自动化?批量学习多个账号?已有成熟软件,详见:<a target="_blank" href="${web_url}?webId=${web_id}">${web_url}?webId=${web_id} </a> </div> <div style="color: red; font-weight:500; margin-bottom:10px;"> 播放页面未正常生效请刷新页面!播放页面左侧无红色按钮请刷新页面! </div> <div style="color: #2c3e50; font-weight:500; margin-bottom:10px;"> 🚀 极速操作流程<br> </div> <div style="display: grid; grid-template-columns: 32px 1fr; gap: 10px; align-items: center;"> <div style="background: #FF4DAF; color: white; width:24px; height:24px; border-radius:50%; text-align:center; line-height:24px;">1</div> <div>进入2025研修课程播放页面 / 课程目录页面</div> <div style="background: #FF4DAF; color: white; width:24px; height:24px; border-radius:50%; text-align:center; line-height:24px;">2</div> <div>直接点击相应按钮,等待操作完成后,刷新页面</div> <div style="background: #FF4DAF; color: white; width:24px; height:24px; border-radius:50%; text-align:center; line-height:24px;">3</div> <div><span style="color:#FF4DAF; font-weight:bold">诶个点击视频,看完最后几秒,安全保留日志信息</span></div> </div> </div> <!-- 注意事项 --> <div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; margin-bottom:20px;"> <div style="border-left: 3px solid #FF4DAF; padding-left:12px;"> <div style="color: #e74c3c; font-weight:500; margin-bottom:8px;">⚠️ 重要提醒</div> <ul style="margin:0; padding-left:18px; color:#7f8c8d; font-size:14px;"> <li>视频最后剩下5秒需要看完</li> <li>刷课时勿播放视频</li> <li>建议刷完全部视频再刷新,观看最后的几秒</li> </ul> </div> <div style="border-left: 3px solid #27ae60; padding-left:12px;"> <div style="color: #27ae60; font-weight:500; margin-bottom:8px;">💡 高效技巧</div> <ul style="margin:0; padding-left:18px; color:#7f8c8d; font-size:14px;"> <li>中小学,在目录或播放页。点击按钮直接开刷</li> <li>职业/高等,挂机即可,可最小化浏览器</li> </ul> </div> </div> ${renderQQGroups()} </div> `, | |
| confirmButtonText: "已了解,开始减负之旅 →", | |
| confirmButtonColor: "#FF4DAF", | |
| showCancelButton: true, | |
| cancelButtonText: "不在显示此窗口", | |
| cancelButtonColor: "#95a5a6", | |
| width: 760, | |
| customClass: { | |
| popup: 'animated pulse', title: 'swal-title-custom' | |
| }, | |
| footer: '<div style="color:#bdc3c7; font-size:12px;">请合理使用本工具</div>' | |
| }).then((result) => { | |
| // console.log(result); | |
| // console.log(Swal.DismissReason.cancel); | |
| if (result.dismiss === Swal.DismissReason.cancel) { | |
| // 跳转到课程列表页或其他操作 | |
| localStorage.setItem('noMoreDialog', "ture") | |
| } | |
| }); | |
| } | |
| } | |
| // 初始化逻辑 | |
| // 初始化逻辑优化 | |
| const init = () => { | |
| // 创建悬浮按钮 | |
| const floatBtn = createFloatingButton(); | |
| // 添加防DOM清理监听(优化版) | |
| const observer = new MutationObserver(mutations => { | |
| if (!document.body.contains(floatBtn)) { | |
| createFloatingButton(); | |
| } | |
| }); | |
| observer.observe(document.body, {childList: true}); | |
| // 添加CSS保护 | |
| const style = document.createElement('style'); | |
| style.textContent = ` #zs-helper-btn { pointer-events: auto !important; opacity: 1 !important; visibility: visible !important; } #zs-helper-btn:hover { transform: translateY(-50%) scale(1.05) !important; } `; | |
| document.head.appendChild(style); | |
| }; | |
| function getVideoTime() { | |
| return Math.round(document.querySelector('video').duration) | |
| } | |
| function getResourceIdFromFullData() { | |
| if (!fullDatas || fullDatas.nodes?.length === 0) { | |
| throw Error("can't get fullDatas!") | |
| } | |
| const result = []; | |
| // 递归遍历节点 | |
| const traverse = (node) => { | |
| if (node.node_type === 'catalog' && node.child_nodes?.length > 0) { | |
| // 如果是目录节点,继续遍历子节点 | |
| node.child_nodes.forEach(child => traverse(child)); | |
| } else if (node.node_type === 'activity') { | |
| // 如果是活动节点,提取资源 | |
| const resources = node.relations?.activity?.activity_resources || []; | |
| resources.forEach(resource => { | |
| result.push({ | |
| name: node.node_name || '未命名课程', | |
| resource_id: resource.resource_id || '', | |
| studyTime: resource.study_time | |
| }); | |
| }); | |
| } | |
| }; | |
| // 遍历初始节点数组 | |
| fullDatas.nodes.forEach(node => traverse(node)); | |
| return result.filter(item => item.resource_id); // 过滤无效项 | |
| } | |
| function getDynamicToken() { | |
| try { | |
| const pattern = /^ND_UC_AUTH-([0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12})&ncet-xedu&token$/; | |
| for (let key of Object.keys(localStorage)) { | |
| if (pattern.test(key)) { | |
| return { | |
| key: key, | |
| appId: key.match(pattern)[1], | |
| token: JSON.parse(JSON.parse(localStorage.getItem(key)).value) | |
| }; | |
| } | |
| } | |
| throw Error("Invalid token! can not get loginInfo!"); | |
| } catch (err) { | |
| throw Error("At:getDynamicToken>>" + err); | |
| } | |
| } | |
| const getMACAuthorizationHeaders = function (url, method) { | |
| let n = getDynamicToken().token | |
| return He(url, method, { | |
| accessToken: n.access_token, macKey: n.mac_key, diff: n.diff | |
| }); | |
| } | |
| function Ze(e) { | |
| for (var t = "0123456789ABCDEFGHIJKLMNOPQRTUVWXZYS".split(""), n = "", r = 0; r < e; r++) n += t[Math.ceil(35 * Math.random())]; | |
| return n | |
| } | |
| function Fe(e) { | |
| return (new Date).getTime() + parseInt(e, 10) + ":" + Ze(8) | |
| } | |
| function ze(e, t, n, r) { | |
| let o = { | |
| relative: new URL(e).pathname, authority: new URL(e).hostname | |
| } | |
| let i = t + "\n" + n.toUpperCase() + "\n" + o.relative + "\n" + o.authority + "\n"; | |
| return CryptoJS.HmacSHA256(i, r).toString(CryptoJS.enc.Base64) | |
| } | |
| function He(e) { | |
| let t = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : "GET", | |
| n = arguments.length > 2 ? arguments[2] : void 0, r = n.accessToken, o = n.macKey, i = n.diff, | |
| s = Fe(i), a = ze(e, s, t, o); | |
| return 'MAC id="'.concat(r, '",nonce="').concat(s, '",mac="').concat(a, '"') | |
| } | |
| const setProgress = function (url, duration) { | |
| const info = getDynamicToken() | |
| return new Promise((resolve, reject) => { | |
| GM_xmlhttpRequest({ | |
| 'url': url, | |
| method: 'PUT', | |
| "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": getMACAuthorizationHeaders(url, 'PUT'), | |
| "cache-control": "no-cache", | |
| "pragma": "no-cache", | |
| "content-type": "application/json", | |
| "sdp-app-id": info.appId, | |
| "sec-ch-ua": "\"Not A(Brand\";v=\"8\", \"Chromium\";v=\"132\", \"Microsoft Edge\";v=\"132\"", | |
| "sec-ch-ua-mobile": "?0", | |
| "sec-ch-ua-platform": "\"Windows\"", | |
| "sec-fetch-dest": "empty", | |
| "sec-fetch-mode": "cors", | |
| "sec-fetch-site": "cross-site", | |
| "host": "x-study-record-api.ykt.eduyun.cn", | |
| "origin": "https://basic.smartedu.cn", | |
| "referer": "https://basic.smartedu.cn/", | |
| "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 Edg/121.0.0.0"}, | |
| data: JSON.stringify({position: duration - 3}), // fetch:true, | |
| onload: function (res) { | |
| //console.log('请求成功',url) | |
| if (res.status === 200) { | |
| console.log(res.finalUrl,"刷课成功!") | |
| resolve(res) | |
| } else { | |
| reject('服务器拒绝:' + res.response) | |
| } | |
| }, onerror: function (err) { | |
| reject('请求错误!' + err.toString()) | |
| }})})}// end setProgress | |
| function main() { | |
| init() | |
| if (!localStorage.getItem("noMoreDialog")) { | |
| showGuideDialog() | |
| } | |
| let myStyle = document.createElement('style') | |
| myStyle.innerHTML = style; | |
| document.head.appendChild(myStyle); | |
| let div = document.createElement('div'); | |
| div.innerHTML = `<div style="left: 10px;top: 280px;" id="my1" class="button-3" >即刻开刷(中小学)</div> | |
| <div style="position: fixed; left: 10px;top: 320px;;background: #ed5822;color: white; padding: 10px 20px; border-radius: 25px; cursor: pointer; box-shadow: 0 3px 15px rgba(0,0,0,0.2); z-index: 999999999999; transition: transform 0.3s;" id="my3" >职业教育/高等教育 刷课</div> | |
| <div style="left: 10px;top: 370px;" id="my2" class="button-3" >2222</div>` | |
| document.body.appendChild(div); | |
| const trigger = document.getElementById('my3') | |
| trigger.addEventListener('click', () => { | |
| if (location.href.includes("core.teacher.vocational.smartedu.cn")) { | |
| createControlPanel() | |
| } else { | |
| Swal.fire({ | |
| title: "注意", | |
| text: "请在职业/高等教育的视频播放页面使用,中小学请用上面的按钮!", | |
| icon: 'info', // showCancelButton: true, | |
| confirmButtonColor: "#FF4DAFFF", // cancelButtonText: "取消,等会刷新", | |
| confirmButtonText: "了解~", | |
| })}}); | |
| trigger.addEventListener('mouseenter', () => trigger.style.transform = 'scale(1.05)'); | |
| trigger.addEventListener('mouseleave', () => trigger.style.transform = 'none'); | |
| let isProcessing = false; | |
| const button = document.getElementById('my1'); | |
| button.addEventListener("click", async () => { | |
| if (isProcessing) { | |
| Swal.fire({title: "操作进行中", text: "正在刷课中,请勿重复点击!", icon: "warning", confirmButtonColor: "#FF4DAFFF", confirmButtonText: "知道了"}); | |
| return; | |
| } | |
| try { | |
| isProcessing = true; // 标记开始处理 | |
| button.disabled = true; // 禁用按钮 | |
| button.textContent = "刷课进行中..."; // 修改按钮文字 | |
| let resId | |
| const allResults = []; | |
| if (!resId) { | |
| console.log("二次获取resId...") | |
| resId = getResourceIdFromFullData() | |
| } | |
| if (resId && typeof resId === 'string') { | |
| // 单个视频 | |
| await setProgress(requestObj.resourceLearningPositions.url + resId + '/' + getDynamicToken().token["user_id"], getVideoTime()) | |
| allResults.push({name: '单个课程', status: 'success'}); | |
| } else if (Array.isArray(resId) && resId.length > 0) { | |
| // 多个视频 使用 Promise.allSettled 并行处理每个视频的进度更新 即使有处理失败,其他视频仍能继续 | |
| const pLimit=require('p-limit'); | |
| const limit=pLimit(9); | |
| const results = await Promise.allSettled(resId.map(item => limit(async()=>{ | |
| try { | |
| await setProgress(requestObj.resourceLearningPositions.url + item.resource_id + '/' + getDynamicToken().token["user_id"], item.studyTime) | |
| return {name: item.name, status: 'success'}; | |
| } catch (e) { | |
| console.error(`${item.name} 失败!`, e); | |
| return {name: item.name, status: 'fail', error: e}; | |
| } | |
| }) | |
| )); | |
| console.log(results) | |
| results.forEach(r => { | |
| if (r.status === 'fulfilled') allResults.push(r.value); else allResults.push(r.reason); // 捕获未处理的意外错误 | |
| }); | |
| } | |
| if (Swal) { | |
| Swal.fire({ | |
| title: "刷课成功!", html: ` <div style="text-align: left; max-height: 20vh; overflow-y: auto;"> <p>总计:${allResults.filter(r => r.status === 'success').length} 成功 / ${allResults.filter(r => r.status === 'fail').length} 失败</p> <hr> <ul style="padding-left: 20px; list-style-type: none;"> ${allResults.map(result => ` <li> ${result.status === 'success' ? '✅' : '❌'} <strong>${result.name}</strong> ${result.error ? `<br><code style="color:red">${result.error.message || result.error}</code>` : ''} </li> `).join('')} </ul> </div> <div style="text-align: left;"> <p>视频只剩下最后5s,需要看完,请刷新后再观看!</p> `, icon: 'success', confirmButtonColor: "#FF4DAFFF", // cancelButtonText: "取消,等会刷新", | |
| confirmButtonText: "确定", | |
| }) | |
| } | |
| } catch (e) { | |
| console.error(e) | |
| if (Swal) { | |
| Swal.fire({ | |
| title: "失败!", | |
| text: e.toString() + " 请在视频播放页面使用!", | |
| icon: 'error', // showCancelButton: true, | |
| confirmButtonColor: "#FF4DAFFF", // cancelButtonText: "取消,等会刷新", | |
| confirmButtonText: "点击去反馈", | |
| }).then((result) => { | |
| if (result.isConfirmed) { | |
| window.open("https://greasyfork.org/zh-CN/scripts/525037/feedback") | |
| } | |
| }); | |
| } | |
| } finally { | |
| isProcessing = false; // 重置处理状态 | |
| button.disabled = false; // 恢复按钮 | |
| button.textContent = "即刻开刷(中小学)"; // 恢复按钮文字 | |
| }}) | |
| document.getElementById('my2').addEventListener('click', function () { | |
| Swal.fire({title: '<span style="font-size:24px; color: #FF4DAF;">欢迎加入交流群</span>', html: ` <div style="text-align: left; max-width: 580px; line-height: 1.7; font-size: 14px;"> <!-- 社群入口 --> ${renderQQGroups()} <!-- 核心价值 --> <div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 15px;"> <!-- 左列 --> <div style="padding-right:15px; border-right:1px dashed #eee;"> <div style="color: #27ae60; margin-bottom:15px;"> <h4 style="margin:0 0 8px 0; font-size:15px;">📚 减负工具</h4> <!-- <ul style="margin:0; padding-left:18px;">--> <!-- <li>自动化备课工具套件</li>--> <!-- <li>智能学情分析报告</li>--> <!-- <li>教学资源智能检索</li>--> <!-- </ul>--> </div> <div style="color: #2980b9; margin-top:15px;"> <h4 style="margin:0 0 8px 0; font-size:15px;">🛡️ 使用规范</h4> <ul style="margin:0; padding-left:18px;"> <li>仅限个人使用</li> <li>禁止商业倒卖行为</li> <li>禁止利用此脚本收费代刷</li> <li>请勿批量自动化操作大量刷课(如需要请联系我,更加高效安全)</li> </ul> </div> </div> <!-- 右列 --> <div style="padding-left:15px;"> <div style="color: #e67e22;"> <h4 style="margin:0 0 8px 0; font-size:15px;">⚖️ 版权声明</h4> <ul style="margin:0; padding-left:18px;"> <li>本工具完全免费</li> <li>源码禁止二次传播</li> <!-- <li>保留原创法律权利</li>--> </ul> </div> <div style="color: #9b59b6; margin-top:15px;"> <h4 style="margin:0 0 8px 0; font-size:15px;">💌 联系我们</h4> <ul style="margin:0; padding-left:18px;"> <!-- <li>反馈建议:edu@service.com</li>--> <li>紧急问题:请私聊群管理员</li> </ul> </div> </div> </div> </div> `, | |
| icon: 'info', | |
| confirmButtonColor: "#FF4DAF", | |
| confirmButtonText: "2222", | |
| showCloseButton: true, | |
| width: 680, | |
| showDenyButton: true, | |
| denyButtonText: '<img src="https://img.icons8.com/fluency/24/star--v1.png" style="height:18px; vertical-align:middle;"> 前往好评', // 带图标的按钮 | |
| denyButtonColor: '#FFC107', | |
| focusDeny: false, | |
| showCancelButton: false, | |
| // 新增按钮回调 | |
| preDeny: () => { | |
| window.open("https://scriptcat.org/zh-CN/script-show-page/2789/comment", "_blank"); | |
| return false; // 阻止弹窗关闭 | |
| }, | |
| customClass: { | |
| denyButton: 'swal-custom-deny', popup: 'swal-custom-popup', title: 'swal-custom-title' | |
| }, | |
| footer: '<div style="color:#95a5a6; font-size:12px;">请合理使用。</div>' | |
| });}); | |
| document.getElementById('my2').style.display = 'none'; // 隐藏按钮 | |
| document.getElementById('my3').style.display = 'none'; // 隐藏按钮 | |
| } | |
| main() | |
| console.log('智慧教育平台 模块启动!'); | |
| } | |
| } | |
| new ScriptCore() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment