// ==UserScript== // @name 锦江享学堂o2o自动化学习(防检测版) // @namespace https://i-learning.jinjianghotels.com.cn/o2o/ // @version 0.4 // @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. 重写 pause 方法,阻止视频被强制暂停 const originalPause = HTMLVideoElement.prototype.pause; HTMLVideoElement.prototype.pause = function() { // 调用原始方法时,检查是否因为切屏等原因触发,根据实际情况决定是否真的暂停 // 这里我们完全阻止,让视频始终可以播放(谨慎使用,可能导致其他问题) console.warn('[防检测] 拦截到 pause 调用,已阻止'); return false; }; // 2. 拦截 visibilitychange 等切屏事件 // 移除所有已绑定的 visibilitychange 监听器(此方法效果有限) const removeAllListeners = () => { const events = ['visibilitychange', 'blur', 'focus', 'focusin', 'focusout', 'pagehide', 'pageshow']; events.forEach(event => { window.removeEventListener(event, () => {}); document.removeEventListener(event, () => {}); }); }; removeAllListeners(); // 阻止新的事件监听器注册 const originalAddEventListener = EventTarget.prototype.addEventListener; EventTarget.prototype.addEventListener = function(type, listener, options) { const blockedEvents = ['visibilitychange', 'blur', 'focus', 'focusin', 'focusout', 'pagehide', 'pageshow']; if (blockedEvents.includes(type)) { console.warn(`[防检测] 拦截了 ${type} 事件监听器的注册`); return; } return originalAddEventListener.call(this, type, listener, options); }; // 在捕获阶段终止事件传播 const blockEvent = (e) => { e.stopImmediatePropagation(); e.preventDefault(); }; const eventsToBlock = ['visibilitychange', 'blur', 'focus', 'focusin', 'focusout', 'pagehide', 'pageshow']; eventsToBlock.forEach(event => { window.addEventListener(event, blockEvent, true); document.addEventListener(event, blockEvent, true); }); // 3. 覆盖页面可见性 API 的返回值 Object.defineProperty(document, 'hidden', { get: () => false }); Object.defineProperty(document, 'visibilityState', { get: () => 'visible' }); document.hasFocus = () => true; // 可选:模拟鼠标移动 const simulateMouseMove = () => { const event = new MouseEvent('mousemove', { view: window, bubbles: true, cancelable: true, clientX: Math.random() * window.innerWidth, clientY: Math.random() * window.innerHeight }); document.dispatchEvent(event); }; // 每 30 秒模拟一次鼠标移动 setInterval(simulateMouseMove, 30000); /******************************* 视频播放与自动切换模块 *******************************/ function findVideoElement() { let video = document.querySelector('video'); if (video && video.readyState > 0) return video; const videoPlayer = document.querySelector('.video-js, .prism-player, .plyr, .jwplayer'); if (videoPlayer) { video = videoPlayer.querySelector('video'); if (video) return video; } return null; } function setupVideo() { const video = findVideoElement(); if (!video) { console.log('[锦江刷课] 未找到有效的视频元素'); return false; } video.muted = false; video.play().catch(e => console.warn('[锦江刷课] 自动播放被阻止:', e)); if (video.playbackRate) { video.playbackRate = 1.5; console.log('[锦江刷课] 播放速度设为 1.5 倍'); } return true; } function findAndClickNext() { const nextKeywords = ['下一个', '下一课', '下一节', '完成学习', '确定', '继续']; const allButtons = document.querySelectorAll('button, a, span, div'); for (let btn of allButtons) { const btnText = btn.innerText.trim(); if (nextKeywords.some(keyword => btnText === keyword || btnText.includes(keyword))) { console.log(`[锦江刷课] 检测到“${btnText}”按钮,尝试点击`); btn.click(); setTimeout(() => setupVideo(), 3000); return true; } } return false; } let lastReportedProgress = 0; setInterval(() => { const video = findVideoElement(); if (video && video.duration) { const currentPercent = (video.currentTime / video.duration * 100).toFixed(2); if (Math.abs(currentPercent - lastReportedProgress) > 1) { console.log(`[锦江刷课] 当前播放进度:${currentPercent}%`); lastReportedProgress = currentPercent; } if (currentPercent > 98 && !video.paused) { findAndClickNext(); } } }, 10000); /******************************* 脚本启动 *******************************/ setTimeout(() => { setupVideo(); }, 3000); })();