// ==UserScript== // @name 锦江享学堂全能自动学习(最终版) // @namespace https://i-learning.jinjianghotels.com.cn/o2o/ // @version 3.0 // @description 自动识别视频/文档/图片课程,自动点击“下一个/完成”,集成防挂机 // @author Assistant // @match https://i-learning.jinjianghotels.com.cn/o2o/* // @grant none // @license MIT // @icon https://www.google.com/s2/favicons?sz=64&domain=jinjianghotels.com.cn // ==/UserScript== (function() { 'use strict'; console.log('%c[锦江全能刷课] 最终版启动,正在初始化...', 'color: #00adb5; font-size: 16px; font-weight: bold;'); /************************************************************************ * 1. 防挂机检测模块(阻止视频暂停、切屏检测、模拟鼠标移动) ************************************************************************/ // 阻止视频暂停 const originalPause = HTMLVideoElement.prototype.pause; HTMLVideoElement.prototype.pause = function() { console.warn('[防挂机] 拦截视频暂停指令'); return false; }; // 拦截页面可见性变化事件 const blockedEvents = ['visibilitychange', 'blur', 'focus', 'focusin', 'focusout', 'pagehide', 'pageshow']; const blockEvent = (e) => { e.stopImmediatePropagation(); e.preventDefault(); }; blockedEvents.forEach(event => { window.addEventListener(event, blockEvent, true); document.addEventListener(event, blockEvent, true); }); // 强制覆盖可见性 API Object.defineProperty(document, 'hidden', { get: () => false }); Object.defineProperty(document, 'visibilityState', { get: () => 'visible' }); document.hasFocus = () => true; // 模拟鼠标移动(每30秒) setInterval(() => { const event = new MouseEvent('mousemove', { view: window, bubbles: true, cancelable: true, clientX: Math.random() * window.innerWidth, clientY: Math.random() * window.innerHeight }); document.dispatchEvent(event); }, 30000); // 模拟页面滚动(用于文档课程) setInterval(() => { if (document.querySelector('.document-content, .pdf-viewer, .doc-content, .file-preview')) { window.scrollBy(0, 50 + Math.random() * 100); if (Math.random() > 0.7) window.scrollBy(0, -30); } }, 5000); /************************************************************************ * 2. 通用按钮点击函数(支持多种查找方式) ************************************************************************/ function findAndClickButton(keywords) { // 方法1:通过文本匹配(包括子元素文本) const allElements = document.querySelectorAll('button, a, span, div, input[type="button"], input[type="submit"]'); for (let el of allElements) { const text = (el.innerText || el.value || '').trim(); if (keywords.some(kw => text === kw || text.includes(kw))) { if (el.offsetParent !== null || el.style.display !== 'none') { console.log(`[点击] 通过文本“${text}”找到按钮,点击`); el.scrollIntoView({ block: 'center' }); el.click(); return true; } } } // 方法2:通过常见类名/ID 查找 const selectors = [ '.next-btn', '.next', '.next-lesson', '.next-chapter', '.complete-btn', '.complete', '.finish', '.done', '.submit-btn', '.confirm-btn', '#next', '#complete', '#finish', '#submit' ]; for (let sel of selectors) { const el = document.querySelector(sel); if (el && el.offsetParent !== null) { console.log(`[点击] 通过选择器“${sel}”找到按钮,点击`); el.scrollIntoView({ block: 'center' }); el.click(); return true; } } // 方法3:遍历所有元素,查找 aria-label 或 title 属性 const all = document.querySelectorAll('*'); for (let el of all) { const label = el.getAttribute('aria-label') || el.getAttribute('title') || ''; if (keywords.some(kw => label.includes(kw))) { if (el.offsetParent !== null) { console.log(`[点击] 通过 aria-label/title “${label}” 找到元素,点击`); el.scrollIntoView({ block: 'center' }); el.click(); return true; } } } return false; } // 预定义按钮关键词(可根据实际页面增减) const NEXT_WORDS = ['下一个', '下一课', '下一节', '继续', '下一步', 'next']; const COMPLETE_WORDS = ['完成', '确定', '保存', '返回', '关闭', 'done', 'complete']; const ALL_WORDS = [...NEXT_WORDS, ...COMPLETE_WORDS]; /************************************************************************ * 3. 课程类型识别 ************************************************************************/ function detectCourseType() { // 如果有视频元素且已加载 const video = document.querySelector('video'); if (video && video.readyState > 0 && video.duration) { return 'video'; } // 如果有文档阅读器 if (document.querySelector('.pdf-viewer, .document-content, .doc-content, .file-preview, .reader-container')) { return 'document'; } // 如果有图片轮播或单张图片(常见于图片课程) if (document.querySelector('img.course-img, .slide-image, .picture-content, .image-slider')) { return 'image'; } // 默认按图片处理(简单翻页) return 'image'; } /************************************************************************ * 4. 各类型处理函数 ************************************************************************/ // 视频处理 let lastVideoProgress = 0; function handleVideo() { const video = document.querySelector('video'); if (!video) return false; // 自动播放 video.muted = false; video.play().catch(() => {}); // 设置倍速(1.5倍) if (video.playbackRate) { video.playbackRate = 1.5; } // 监控进度 if (video.duration) { const percent = (video.currentTime / video.duration * 100).toFixed(2); if (Math.abs(percent - lastVideoProgress) > 1) { console.log(`[视频] 进度 ${percent}%`); lastVideoProgress = percent; } if (percent > 98 && !video.paused) { console.log('[视频] 播放完成,点击下一个'); findAndClickButton(NEXT_WORDS) || findAndClickButton(ALL_WORDS); lastVideoProgress = 0; setTimeout(() => { handleVideo(); }, 3000); return true; } } return true; } // 文档处理 let docStartTime = Date.now(); let docCompleted = false; function handleDocument() { // 尝试直接点击完成/下一个 const clicked = findAndClickButton(COMPLETE_WORDS) || findAndClickButton(NEXT_WORDS); if (clicked) { console.log('[文档] 点击完成/下一个'); docCompleted = true; return true; } // 如果未点击,检查是否已完成(通过标记) if (document.querySelector('.complete-tag, .finished, .done, .completed')) { console.log('[文档] 检测到已完成标记,尝试返回'); findAndClickButton(['返回', '关闭']); docCompleted = true; return true; } // 否则模拟阅读等待 const elapsed = (Date.now() - docStartTime) / 1000; if (elapsed < 30) { console.log(`[文档] 阅读中... ${Math.floor(elapsed)}s / 30s`); return false; } // 阅读时间到,再次尝试完成 console.log('[文档] 阅读时间到,尝试完成'); findAndClickButton(COMPLETE_WORDS) || findAndClickButton(NEXT_WORDS); docCompleted = true; return true; } // 图片处理 function handleImage() { console.log('[图片] 尝试点击下一个'); return findAndClickButton(NEXT_WORDS) || findAndClickButton(ALL_WORDS); } /************************************************************************ * 5. 列表页处理(自动点击未学习项) ************************************************************************/ function handleListPage() { // 查找未完成的任务项(文档/课程/视频等) const items = document.querySelectorAll('.task-item, .doc-item, .file-item, .tree-node, li, .item'); for (let item of items) { const text = item.innerText.trim(); // 如果包含学习内容关键词且未完成 if ((text.includes('文档') || text.includes('课程') || text.includes('视频') || text.includes('章节')) && !text.includes('已完成') && !text.includes('已学') && !text.includes('100%')) { console.log(`[列表] 点击未完成项: ${text.substring(0, 30)}`); item.scrollIntoView({ block: 'center' }); item.click(); return true; } } // 如果没有未完成项,尝试点击“下一个”或刷新 console.log('[列表] 所有任务可能已完成'); findAndClickButton(NEXT_WORDS); return false; } /************************************************************************ * 6. 主循环(每8秒执行一次) ************************************************************************/ let isProcessing = false; setInterval(() => { if (isProcessing) return; isProcessing = true; const url = window.location.href; console.log(`[主循环] 当前URL: ${url}`); // 判断页面类型 if (url.includes('playinfo') || url.includes('preview') || url.includes('document')) { // 课程详情页 const type = detectCourseType(); console.log(`[主循环] 课程类型: ${type}`); let handled = false; switch (type) { case 'video': handled = handleVideo(); break; case 'document': handled = handleDocument(); // 如果文档已完成,重置计时器 if (handled) { docStartTime = Date.now(); docCompleted = false; } break; case 'image': default: handled = handleImage(); break; } // 如果处理失败,尝试通用点击 if (!handled) { findAndClickButton(ALL_WORDS); } // 如果检测到已完成标记,尝试返回列表 if (document.querySelector('.complete-tag, .finished, .done, .completed')) { console.log('[主循环] 课程已完成,返回列表'); findAndClickButton(['返回', '关闭']); // 重置文档计时器 docStartTime = Date.now(); docCompleted = false; } setTimeout(() => { isProcessing = false; }, 3000); } else if (url.includes('project/detail') || url.includes('task')) { // 列表页 handleListPage(); setTimeout(() => { isProcessing = false; }, 5000); } else { // 其他页面,尝试通用按钮 findAndClickButton(ALL_WORDS); setTimeout(() => { isProcessing = false; }, 3000); } // 超时释放锁 setTimeout(() => { isProcessing = false; }, 15000); }, 8000); /************************************************************************ * 7. 兜底任务(定期点击、关闭弹窗) ************************************************************************/ // 每15秒尝试点击通用按钮(防止漏点) setInterval(() => { findAndClickButton(ALL_WORDS); }, 15000); // 每20秒尝试关闭弹窗 setInterval(() => { const closeBtn = document.querySelector('.close, .modal-close, .dialog-close, .popup-close'); if (closeBtn && closeBtn.offsetParent !== null) { console.log('[刷课] 关闭弹窗'); closeBtn.click(); } }, 20000); /************************************************************************ * 8. 初始化完成 ************************************************************************/ setTimeout(() => { console.log('[锦江全能刷课] 脚本已启动,正在监控课程...'); console.log('[提示] 若无法自动点击“下一个”,请按F12查看控制台日志,并反馈按钮文字。'); }, 2000); })();