您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
增强用户活跃度模拟,确保持续计算学时,平滑滚动和定期交互
当前为
// ==UserScript== // @name 地理信息安全在线培训自动化学习(活跃度增强版) // @namespace http://tampermonkey.net/ // @version 5.0 // @description 增强用户活跃度模拟,确保持续计算学时,平滑滚动和定期交互 // @author YourName // @match https://gistraining.webmap.cn/* // @grant GM_addStyle // @license MIT // @run-at document-idle // ==/UserScript== (function() { 'use strict'; console.log("[培训助手] 脚本开始执行"); // 样式省略...与之前相同 GM_addStyle(` #control-panel { position: fixed !important; bottom: 20px !important; right: 20px !important; width: 340px !important; background: linear-gradient(135deg, #1e88e5, #1565c0) !important; border-radius: 12px !important; box-shadow: 0 4px 20px rgba(0,0,0,0.3) !important; color: white !important; font-family: 'Segoe UI', Arial, sans-serif !important; z-index: 999999 !important; padding: 15px !important; transition: all 0.3s ease !important; } /* 其他样式省略... */ `); // 脚本状态管理 const scriptState = { isRunning: false, scrollInterval: null, // 大幅度滚动间隔 microScrollInterval: null, // 微小滚动间隔(保持活跃) clickInterval: null, // 点击间隔 moveInterval: null, // 鼠标移动间隔 activityInterval: null, // 活跃度检查间隔 idleTimeout: null, // 检测页面空闲的超时 nextContentInterval: null, // 下一个内容切换间隔 lastActivityTime: Date.now(),// 最后活动时间 currentContentIndex: -1, // 当前内容索引 courseList: [], // 课程列表 currentPage: 1, // 当前页码 totalPages: 1, // 总页数 completedCourses: new Set(), // 已完成课程集合 courseProgress: { // 课程进度 total: 0, // 总课程数 completed: 0, // 已完成数 current: null // 当前课程 }, activityStats: { // 活跃度统计 scrolls: 0, // 滚动次数 clicks: 0, // 点击次数 keystrokes: 0, // 按键次数 mousemoves: 0 // 鼠标移动次数 }, scrollPosition: 0, // 当前滚动位置 scrollDirection: 1, // 1向下,-1向上 bottomReachedCount: 0, // 到达底部次数 waitingForNextCourse: false, // 是否等待下一课 debugInfo: "", // 调试信息 currentContentId: null, // 当前内容ID scrollingContainer: null, // 滚动容器 startTime: null, // 开始时间 config: { majorScrollInterval: 30000, // 主要滚动间隔(毫秒) microScrollInterval: 10000, // 微小滚动间隔(毫秒) clickInterval: 15000, // 点击间隔(毫秒) moveInterval: 5000, // 鼠标移动间隔(毫秒) activityInterval: 20000, // 活跃度触发间隔(毫秒) idleThreshold: 60000, // 空闲检测阈值(毫秒) nextContentDelay: 5000, // 切换下一课延迟(毫秒) scrollSpeed: 2000, // 滚动速度(毫秒) scrollStep: 80, // 每次滚动步长(像素) microScrollStep: 10, // 微小滚动步长(像素) pauseAtBottom: 8000, // 底部暂停(毫秒) pauseAtTop: 3000, // 顶部暂停(毫秒) minReadTime: 180000, // 最短阅读时间(毫秒) scrollTimeThreshold: 2, // 底部达到次数阈值 activityTypes: ['scroll', 'click', 'mousemove', 'keypress', 'studytime'], autoNextContent: true, // 自动切换下一课 debug: true, // 调试模式 studyTimeInterval: 30000 // 每30秒触发一次学习时间更新 } }; // 调试日志 function log(message) { if (scriptState.config.debug) { console.log(`[培训助手] ${new Date().toLocaleTimeString()} - ${message}`); scriptState.debugInfo = message + "\n" + scriptState.debugInfo.substring(0, 300); updateStatus(message); } } // 记录活动统计 function recordActivity(type) { scriptState.lastActivityTime = Date.now(); switch(type) { case 'scroll': scriptState.activityStats.scrolls++; break; case 'click': scriptState.activityStats.clicks++; break; case 'keypress': scriptState.activityStats.keystrokes++; break; case 'mousemove': scriptState.activityStats.mousemoves++; break; } // 更新界面中的活跃度数据 updateActivityStats(); // 清除之前的空闲检测 if (scriptState.idleTimeout) { clearTimeout(scriptState.idleTimeout); } // 如果太久没活动,设置一个新的空闲检测 scriptState.idleTimeout = setTimeout(() => { if (scriptState.isRunning) { log("检测到可能的空闲,触发额外活动"); performRandomActivity(); } }, scriptState.config.idleThreshold); } // 执行随机活动 function performRandomActivity() { if (!scriptState.isRunning) return; const activities = [ () => simulateScrolling(true), // 强制滚动 () => simulateClick(), // 模拟点击 () => simulateMouseMovement(), // 鼠标移动 () => simulateKeyPress(), // 键盘按键 () => triggerStudyTime() // 触发学习时间 ]; // 随机选择1-3个活动执行 const count = Math.floor(Math.random() * 3) + 1; for (let i = 0; i < count; i++) { const activity = activities[Math.floor(Math.random() * activities.length)]; activity(); } } // 触发学习时间更新 function triggerStudyTime() { try { // 尝试触发页面自带的jstime函数 if (typeof window.jstime === 'function') { window.jstime(); log("触发页面jstime函数"); } // 使用submitAjax更新学习时间 if (typeof window.submitAjax === 'function') { const studyTime = Math.floor(Math.random() * 30) + 10; // 10-40秒 window.submitAjax({'url': `index.php?course-app-course-ajax-updatestudytime&studytime=${studyTime}`}); log(`通过AJAX更新学习时间: ${studyTime}秒`); } // 重置计时器 if (typeof window.timeUserFun === 'function') { window.timeUserFun(2); log("重置页面计时器"); } recordActivity('studytime'); return true; } catch (e) { log(`触发学习时间失败: ${e.message}`); return false; } } // 随机延迟 function randomDelay(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } // 平滑滚动 function smoothScroll(element, target, duration) { if (!element) return; const start = element.scrollTop; const change = target - start; const startTime = performance.now(); function animateScroll(currentTime) { const elapsed = currentTime - startTime; const progress = Math.min(elapsed / duration, 1); // 使用缓动函数让滚动更自然 const easeProgress = 0.5 - 0.5 * Math.cos(progress * Math.PI); element.scrollTop = start + change * easeProgress; // 更新滚动进度条 updateScrollProgressBar(element); if (progress < 1) { requestAnimationFrame(animateScroll); } else { // 滚动完成时记录活动 recordActivity('scroll'); } } requestAnimationFrame(animateScroll); } // 更新滚动进度条 function updateScrollProgressBar(element) { if (!element) return; const scrollBar = document.querySelector('.scroll-progress-bar'); if (scrollBar) { const maxScroll = element.scrollHeight - element.clientHeight; if (maxScroll > 0) { const progress = (element.scrollTop / maxScroll) * 100; scrollBar.style.width = `${progress}%`; } } } // 获取滚动容器 function getScrollContainer() { // 尝试找到滚动容器 const mainContainers = [ document.querySelector('.box.itembox[style*="overflow-y: scroll"]'), document.querySelector('.box.itembox[style*="overflow"]'), document.querySelector('div[style*="overflow-y: scroll"]'), document.querySelector('div[style*="overflow: auto"]'), document.querySelector('.box.itembox:not(.collapsed)') ]; // 找到第一个有效的容器 return mainContainers.find(container => container && container.scrollHeight > container.clientHeight); } // 检查是否已滚动到底部 function isScrolledToBottom(element) { if (!element) return false; return element.scrollHeight - element.scrollTop <= element.clientHeight + 100; } // 检查是否已滚动到顶部 function isScrolledToTop(element) { if (!element) return true; return element.scrollTop <= 50; } // 模拟微小滚动 - 保持活跃度 function simulateMicroScroll() { if (!scriptState.isRunning) return; const scrollContainer = scriptState.scrollingContainer || getScrollContainer(); if (!scrollContainer) return; // 当前滚动位置 const currentScroll = scrollContainer.scrollTop; const maxScroll = scrollContainer.scrollHeight - scrollContainer.clientHeight; if (maxScroll <= 0) return; // 产生一个微小的随机滚动偏移 const offset = (Math.random() > 0.5 ? 1 : -1) * Math.floor(Math.random() * scriptState.config.microScrollStep); // 确保不会滚出边界 let targetScroll = currentScroll + offset; if (targetScroll < 0) targetScroll = 0; if (targetScroll > maxScroll) targetScroll = maxScroll; // 执行微小滚动 smoothScroll(scrollContainer, targetScroll, 500); // 模拟用户还在阅读 recordActivity('scroll'); } // 模拟用户滚动 - 大幅度滚动 function simulateScrolling(force = false) { if (!scriptState.isRunning) return; // 查找滚动容器 const scrollContainer = scriptState.scrollingContainer || getScrollContainer(); if (!scrollContainer) { log("未找到可滚动容器,标记为已完成"); scriptState.bottomReachedCount = scriptState.config.scrollTimeThreshold; checkCourseCompletion(); return; } scriptState.scrollingContainer = scrollContainer; // 只在第一次或强制滚动时输出日志 if (force || scriptState.activityStats.scrolls === 0) { log("开始模拟滚动行为"); } const maxScroll = scrollContainer.scrollHeight - scrollContainer.clientHeight; if (maxScroll <= 0) { log("内容不足,无需滚动,直接标记为完成"); scriptState.bottomReachedCount = scriptState.config.scrollTimeThreshold; checkCourseCompletion(); return; } // 获取当前滚动位置 const currentScroll = scrollContainer.scrollTop; // 根据方向计算下一个滚动目标 let targetScroll; if (scriptState.scrollDirection === 1) { // 向下滚动 targetScroll = Math.min(currentScroll + randomDelay(100, 300), maxScroll); // 检查是否到达底部 if (targetScroll >= maxScroll - 100) { targetScroll = maxScroll; // 模拟用户滚动到底部后的行为 smoothScroll(scrollContainer, targetScroll, scriptState.config.scrollSpeed); scriptState.bottomReachedCount++; log(`到达底部第 ${scriptState.bottomReachedCount} 次,暂停 ${scriptState.config.pauseAtBottom/1000} 秒`); // 在底部停留一段时间,然后改变方向 setTimeout(() => { if (scriptState.isRunning) { scriptState.scrollDirection = -1; // 改变方向 log("开始向上滚动"); // 检查是否已经满足完成条件 if (scriptState.bottomReachedCount >= scriptState.config.scrollTimeThreshold) { // 检查是否阅读时间足够 const elapsed = Date.now() - (scriptState.startTime || 0); if (elapsed >= scriptState.config.minReadTime) { log(`已满足阅读条件,底部次数: ${scriptState.bottomReachedCount},时间: ${Math.floor(elapsed/1000)}秒`); checkCourseCompletion(); } else { log(`已到达底部足够次数,但阅读时间不足,还需 ${Math.floor((scriptState.config.minReadTime - elapsed)/1000)} 秒`); } } } }, scriptState.config.pauseAtBottom); return; } } else { // 向上滚动 targetScroll = Math.max(currentScroll - randomDelay(100, 300), 0); // 检查是否到达顶部 if (targetScroll <= 100) { targetScroll = 0; // 模拟用户滚动到顶部后的行为 smoothScroll(scrollContainer, targetScroll, scriptState.config.scrollSpeed); // 在顶部停留一段时间,然后改变方向 setTimeout(() => { if (scriptState.isRunning) { scriptState.scrollDirection = 1; // 改变方向 log("开始向下滚动"); } }, scriptState.config.pauseAtTop); return; } } // 执行滚动 smoothScroll(scrollContainer, targetScroll, scriptState.config.scrollSpeed); // 模拟用户行为,记录活动 recordActivity('scroll'); } // 模拟鼠标移动 - 兼容版 function simulateMouseMovement() { if (!scriptState.isRunning) return; const scrollContainer = scriptState.scrollingContainer || getScrollContainer(); if (!scrollContainer) return; try { const rect = scrollContainer.getBoundingClientRect(); const randomX = Math.round(rect.left + Math.random() * rect.width); const randomY = Math.round(rect.top + Math.random() * rect.height); // 方法1: 使用旧的事件API try { const evt = document.createEvent('MouseEvents'); evt.initMouseEvent('mousemove', true, true, window, 0, 0, 0, randomX, randomY, false, false, false, false, 0, null); scrollContainer.dispatchEvent(evt); recordActivity('mousemove'); return; } catch (e) { // 如果旧API失败,尝试新API } // 方法2: 使用新的MouseEvent API (不带view参数) try { const moveEvent = new MouseEvent('mousemove', { bubbles: true, cancelable: true, clientX: randomX, clientY: randomY }); scrollContainer.dispatchEvent(moveEvent); recordActivity('mousemove'); } catch (e) { // 忽略错误 } } catch (e) { // 忽略错误 } } // 模拟用户点击 - 兼容版 function simulateClick() { if (!scriptState.isRunning) return; try { const scrollContainer = scriptState.scrollingContainer || getScrollContainer(); let randomX, randomY; // 如果找到了滚动容器,优先在其中点击 if (scrollContainer) { const rect = scrollContainer.getBoundingClientRect(); randomX = Math.round(rect.left + Math.random() * rect.width * 0.8); randomY = Math.round(rect.top + Math.random() * rect.height * 0.8); } else { // 否则在页面随机位置点击 randomX = Math.round(Math.random() * window.innerWidth * 0.7); randomY = Math.round(Math.random() * window.innerHeight * 0.7); } // 获取目标元素 const element = document.elementFromPoint(randomX, randomY); if (!element) return; // 尝试不同的点击方法 // 方法1: 最简单的方法 try { element.click(); recordActivity('click'); log(`点击元素 ${element.tagName}`); return; } catch (e) { // 尝试下一种方法 } // 方法2: 使用旧的事件API try { const evt = document.createEvent('MouseEvents'); evt.initMouseEvent('click', true, true, window, 1, 0, 0, randomX, randomY, false, false, false, false, 0, null); element.dispatchEvent(evt); recordActivity('click'); return; } catch (e) { // 尝试下一种方法 } // 方法3: 使用新的MouseEvent API (不带view参数) try { const clickEvent = new MouseEvent('click', { bubbles: true, cancelable: true, clientX: randomX, clientY: randomY }); element.dispatchEvent(clickEvent); recordActivity('click'); } catch (err) { log(`点击模拟失败: ${err.message}`); } } catch (error) { log(`点击模拟完全失败: ${error.message}`); } } // 模拟键盘按键 function simulateKeyPress() { if (!scriptState.isRunning) return; // 安全的键码列表 const safeKeys = [ { key: 'ArrowDown', code: 'ArrowDown', keyCode: 40 }, { key: 'ArrowUp', code: 'ArrowUp', keyCode: 38 }, { key: 'ArrowLeft', code: 'ArrowLeft', keyCode: 37 }, { key: 'ArrowRight', code: 'ArrowRight', keyCode: 39 }, { key: 'PageDown', code: 'PageDown', keyCode: 34 }, { key: 'PageUp', code: 'PageUp', keyCode: 33 }, { key: 'Home', code: 'Home', keyCode: 36 }, { key: 'End', code: 'End', keyCode: 35 }, { key: 'Space', code: 'Space', keyCode: 32 } ]; // 随机选择一个键 const randomKey = safeKeys[Math.floor(Math.random() * safeKeys.length)]; try { // 方法1: 使用旧的事件API try { const evt = document.createEvent('KeyboardEvent'); // 尝试不同的初始化方法 if (typeof evt.initKeyboardEvent !== 'undefined') { evt.initKeyboardEvent('keydown', true, true, window, randomKey.key, 0, '', false, ''); } else if (typeof evt.initKeyEvent !== 'undefined') { evt.initKeyEvent('keydown', true, true, window, false, false, false, false, randomKey.keyCode, 0); } document.dispatchEvent(evt); recordActivity('keypress'); return; } catch (e) { // 尝试下一种方法 } // 方法2: 使用新的KeyboardEvent API try { const keyEvent = new KeyboardEvent('keydown', { key: randomKey.key, code: randomKey.code, keyCode: randomKey.keyCode, bubbles: true, cancelable: true }); document.dispatchEvent(keyEvent); recordActivity('keypress'); } catch (err) { // 忽略错误 } } catch (error) { // 忽略错误 } } // 维持活跃状态 - 定期执行的活跃度保持函数 function maintainActivity() { if (!scriptState.isRunning) return; // 检查距离上次活动的时间 const timeSinceLastActivity = Date.now() - scriptState.lastActivityTime; // 如果超过阈值,触发随机活动 if (timeSinceLastActivity > scriptState.config.idleThreshold / 2) { log("检测到活跃度下降,触发随机活动"); performRandomActivity(); } else { // 即使活跃,也随机执行一些小动作 if (Math.random() > 0.7) { const activities = [ () => simulateMouseMovement(), () => simulateMicroScroll(), () => triggerStudyTime() ]; const activity = activities[Math.floor(Math.random() * activities.length)]; activity(); } } } // 检查当前课程是否完成 function checkCourseCompletion() { if (!scriptState.isRunning || scriptState.waitingForNextCourse) return; // 检查滚动到底部次数和总阅读时间 const elapsed = Date.now() - (scriptState.startTime || 0); const hasReachedBottom = scriptState.bottomReachedCount >= scriptState.config.scrollTimeThreshold; const hasReadLongEnough = elapsed >= scriptState.config.minReadTime; log(`检查课程完成情况: 底部次数=${scriptState.bottomReachedCount}/${scriptState.config.scrollTimeThreshold}, 阅读时间=${Math.floor(elapsed/1000)}秒/${scriptState.config.minReadTime/1000}秒`); // 只有当两个条件都满足时,才认为课程完成 if (hasReachedBottom && hasReadLongEnough) { const currentUrl = window.location.href; const contentId = extractContentId(currentUrl); if (contentId && !scriptState.completedCourses.has(contentId)) { log(`课程ID ${contentId} 已完成,准备切换下一课`); scriptState.completedCourses.add(contentId); scriptState.courseProgress.completed++; // 强制发送学习时间 try { if (typeof window.submitAjax === 'function') { window.submitAjax({'url': "index.php?course-app-course-ajax-updatestudytime&studytime=50"}); log("提交最终学习时间记录"); } } catch(e) { // 忽略错误 } if (scriptState.config.autoNextContent) { scriptState.waitingForNextCourse = true; log(`等待 ${scriptState.config.nextContentDelay/1000} 秒后切换到下一课...`); setTimeout(() => { goToNextContent(); scriptState.waitingForNextCourse = false; scriptState.bottomReachedCount = 0; }, scriptState.config.nextContentDelay); } } else { // 重新获取课程列表并尝试查找当前课程 refreshCourseStatus(); } updateUI(); } } // 从URL中提取contentid function extractContentId(url) { const match = url.match(/contentid=(\d+)/); return match ? match[1] : null; } // 刷新课程状态 function refreshCourseStatus() { log("刷新课程状态"); // 从URL获取当前课程ID const currentUrl = window.location.href; const contentId = extractContentId(currentUrl); if (contentId) { scriptState.currentContentId = contentId; log(`当前课程ID: ${contentId}`); } // 刷新课程列表 scriptState.courseList = getContentList(); if (scriptState.courseList.length > 0) { log(`找到 ${scriptState.courseList.length} 个课程`); // 找到当前课程索引 if (contentId) { for (let i = 0; i < scriptState.courseList.length; i++) { const href = scriptState.courseList[i].getAttribute('href'); if (href && href.includes(`contentid=${contentId}`)) { scriptState.currentContentIndex = i; log(`当前课程索引: ${i}`); break; } } } // 如果没找到对应索引,尝试其他方法定位 if (scriptState.currentContentIndex === -1) { scriptState.currentContentIndex = findCurrentCourseIndex(); } scriptState.courseProgress.total = scriptState.courseList.length; updateUI(); // 检查是否可以切换到下一课 const nextIndex = scriptState.currentContentIndex + 1; if (nextIndex < scriptState.courseList.length) { log(`可以切换到下一课 (${nextIndex + 1}/${scriptState.courseList.length})`); } else { // 检查是否有下一页 checkAndNavigateToNextPage(); } } else { log("未找到任何课程,将尝试从分页导航"); tryFindPagination(); } } // 尝试查找分页导航 function tryFindPagination() { const pagination = document.querySelector('.pagination'); if (!pagination) { log("未找到分页导航"); return false; } const pageLinks = pagination.querySelectorAll('a'); if (pageLinks.length === 0) { log("分页导航中没有链接"); return false; } log(`找到分页链接 ${pageLinks.length} 个`); // 查找当前页码和下一页 let currentPage = 1; let nextPageLink = null; for (const link of pageLinks) { if (link.classList.contains('current')) { const pageNum = parseInt(link.textContent); if (!isNaN(pageNum)) { currentPage = pageNum; } } // 寻找下一页链接 if (link.textContent.includes('下一页') || (parseInt(link.textContent) === currentPage + 1)) { nextPageLink = link; } } scriptState.currentPage = currentPage; log(`当前页码: ${currentPage}`); if (nextPageLink) { log(`找到下一页链接: ${nextPageLink.getAttribute('href')}`); return nextPageLink; } return false; } // 检查并导航到下一页 function checkAndNavigateToNextPage() { const nextPageLink = tryFindPagination(); if (nextPageLink) { log("当前页所有课程已完成,将跳转到下一页"); scriptState.waitingForNextCourse = true; setTimeout(() => { const href = nextPageLink.getAttribute('href'); if (href) { log(`跳转到下一页: ${href}`); window.location.href = href; } }, scriptState.config.nextContentDelay); return true; } log("已完成所有课程,或无下一页"); return false; } // 查找当前课程索引 function findCurrentCourseIndex() { // 方法1:查找带有特殊样式或类的元素 for (let i = 0; i < scriptState.courseList.length; i++) { const course = scriptState.courseList[i]; if (course.classList.contains('active') || course.classList.contains('current') || course.style.fontWeight === 'bold' || course.style.color === 'rgb(255, 0, 0)') { return i; } } // 方法2:从页面标题匹配 const pageTitle = document.querySelector('h4.title'); if (pageTitle) { const title = pageTitle.textContent.trim(); for (let i = 0; i < scriptState.courseList.length; i++) { if (scriptState.courseList[i].textContent.includes(title)) { return i; } } } // 方法3:从主内容匹配 const mainContent = document.querySelector('.box.itembox[style*="overflow"]'); if (mainContent) { const content = mainContent.textContent.trim().substring(0, 50); for (let i = 0; i < scriptState.courseList.length; i++) { const courseTitle = scriptState.courseList[i].textContent.trim(); if (content.includes(courseTitle) || courseTitle.includes(content.substring(0, 10))) { return i; } } } // 如果无法确定,返回0(第一个) return 0; } // 获取右侧内容列表 function getContentList() { log("开始查找课程列表"); // 首先尝试获取右侧栏所有课程 const rightColumn = document.querySelector('.col-xs-5.pull-right'); if (rightColumn) { log("找到右侧栏"); // 尝试查找带有课程链接的容器 const courseContainers = rightColumn.querySelectorAll('.box.itembox'); const allLinks = []; courseContainers.forEach(container => { // 找到容器中的所有链接 const links = container.querySelectorAll('a[href*="contentid"]'); links.forEach(link => allLinks.push(link)); }); if (allLinks.length > 0) { log(`在右侧栏找到 ${allLinks.length} 个课程链接`); return filterCourseLinks(allLinks); } // 备选:获取右侧栏中所有链接 const allRightLinks = rightColumn.querySelectorAll('a'); if (allRightLinks.length > 0) { log(`在右侧栏找到 ${allRightLinks.length} 个链接`); return filterCourseLinks(Array.from(allRightLinks)); } } // 尝试找到课程盒子 const courseBoxes = document.querySelectorAll('.box.itembox'); for (const box of courseBoxes) { // 检查是否包含标题 const title = box.querySelector('h4.title'); if (title) { const links = box.querySelectorAll('a'); if (links.length > 0) { log(`从课程盒子找到 ${links.length} 个链接`); return filterCourseLinks(Array.from(links)); } } } // 尝试直接查找所有课程链接 const contentLinks = document.querySelectorAll('a[href*="contentid"]'); if (contentLinks.length > 0) { log(`直接查找contentid链接,找到 ${contentLinks.length} 个`); return filterCourseLinks(Array.from(contentLinks)); } // 备选:查找所有课程相关链接 const courseLinks = document.querySelectorAll('a[href*="course-app-course"]'); if (courseLinks.length > 0) { log(`查找course-app-course链接,找到 ${courseLinks.length} 个`); return filterCourseLinks(Array.from(courseLinks)); } log("未找到任何课程链接"); return []; } // 过滤有效的课程链接 function filterCourseLinks(links) { if (!links || links.length === 0) return []; // 收集所有可能的课程链接 const validLinks = []; const seenHrefs = new Set(); for (const link of links) { if (!link || !link.getAttribute) continue; const href = link.getAttribute('href') || ''; const text = link.textContent.trim(); // 排除明显不是课程的链接 if (text.includes('首页') || text.includes('下一页') || text.includes('上一页') || text.includes('共') || text === '' || text.match(/^\d+$/)) { continue; } // 检查是否是课程链接 if ((href.includes('contentid=') || href.includes('course-app-course')) && !seenHrefs.has(href)) { seenHrefs.add(href); validLinks.push(link); } } return validLinks; } // 切换到下一个内容 function goToNextContent() { if (!scriptState.isRunning) return; log("准备切换到下一课"); // 重新获取课程列表和当前状态 refreshCourseStatus(); if (scriptState.courseList.length === 0) { log("没有找到课程列表,尝试重新加载页面"); setTimeout(() => { window.location.reload(); }, 2000); return; } // 确定下一个课程索引 const nextIndex = scriptState.currentContentIndex + 1; if (nextIndex >= scriptState.courseList.length) { log("已达到当前页最后一个课程,检查是否有下一页"); if (!checkAndNavigateToNextPage()) { log("没有下一页或已是最后一页,停止自动化"); stopScript(); } return; } const nextCourse = scriptState.courseList[nextIndex]; if (!nextCourse) { log("无法获取下一课程信息"); return; } log(`切换到下一个内容 ${nextIndex+1}/${scriptState.courseList.length}: ${nextCourse.textContent.trim()}`); // 准备课程切换 scriptState.currentContentIndex = nextIndex; scriptState.courseProgress.current = nextCourse.textContent.trim(); scriptState.bottomReachedCount = 0; scriptState.scrollDirection = 1; // 重置滚动方向 scriptState.scrollCount = 0; // 重置滚动计数 // 尝试切换课程 const href = nextCourse.getAttribute('href'); if (!href) { log("错误:下一课程链接无效"); return; } // 强制使用href方式导航(更可靠) log(`使用href导航: ${href}`); window.location.href = href; updateUI(); } // 强制直接跳转到指定课程 function jumpToCourse(index) { if (index < 0 || index >= scriptState.courseList.length) { log(`无效的课程索引: ${index}`); return; } const course = scriptState.courseList[index]; if (!course) { log("无法获取课程信息"); return; } log(`直接跳转到课程 ${index+1}/${scriptState.courseList.length}: ${course.textContent.trim()}`); const href = course.getAttribute('href'); if (href) { window.location.href = href; } else { log("错误:课程链接无效"); } } // 手动切换到下一课 function manualGoToNextContent() { log("手动切换下一课"); scriptState.bottomReachedCount = scriptState.config.scrollTimeThreshold; checkCourseCompletion(); } // 手动触发学习时间记录 function manualTriggerStudyTime() { log("手动触发学习时间记录"); triggerStudyTime(); } // 手动滚动到底部 function manualScrollToBottom() { log("手动滚动到底部"); const scrollContainer = scriptState.scrollingContainer || getScrollContainer(); if (scrollContainer) { const maxScroll = scrollContainer.scrollHeight - scrollContainer.clientHeight; smoothScroll(scrollContainer, maxScroll, 1000); } } // 启动脚本 function startScript() { if (scriptState.isRunning) { log("脚本已在运行中"); return; } scriptState.isRunning = true; scriptState.bottomReachedCount = 0; scriptState.scrollDirection = 1; // 向下滚动 scriptState.scrollCount = 0; scriptState.startTime = Date.now(); // 重置活跃度统计 scriptState.activityStats = { scrolls: 0, clicks: 0, keystrokes: 0, mousemoves: 0 }; // 初始化课程状态 refreshCourseStatus(); updateUI(); log("启动培训助手脚本"); // 启动主要滚动模拟 scriptState.scrollInterval = setInterval(() => { simulateScrolling(); }, scriptState.config.majorScrollInterval); // 启动微小滚动(更频繁,模拟用户读书) scriptState.microScrollInterval = setInterval(() => { simulateMicroScroll(); }, scriptState.config.microScrollInterval); // 启动点击模拟 scriptState.clickInterval = setInterval(() => { simulateClick(); }, scriptState.config.clickInterval); // 启动鼠标移动模拟 scriptState.moveInterval = setInterval(() => { simulateMouseMovement(); }, scriptState.config.moveInterval); // 启动活跃度维持 scriptState.activityInterval = setInterval(() => { maintainActivity(); }, scriptState.config.activityInterval); // 定期更新学习时间 scriptState.studyTimeInterval = setInterval(() => { triggerStudyTime(); }, scriptState.config.studyTimeInterval); // 立即执行一次活动,启动滚动 simulateScrolling(); log("脚本已启动,自动化学习进行中,保持页面活跃状态"); } // 停止脚本 function stopScript() { if (!scriptState.isRunning) { log("脚本已停止"); return; } scriptState.isRunning = false; scriptState.waitingForNextCourse = false; updateUI(); // 清除所有定时器 if (scriptState.scrollInterval) { clearInterval(scriptState.scrollInterval); scriptState.scrollInterval = null; } if (scriptState.microScrollInterval) { clearInterval(scriptState.microScrollInterval); scriptState.microScrollInterval = null; } if (scriptState.clickInterval) { clearInterval(scriptState.clickInterval); scriptState.clickInterval = null; } if (scriptState.moveInterval) { clearInterval(scriptState.moveInterval); scriptState.moveInterval = null; } if (scriptState.activityInterval) { clearInterval(scriptState.activityInterval); scriptState.activityInterval = null; } if (scriptState.studyTimeInterval) { clearInterval(scriptState.studyTimeInterval); scriptState.studyTimeInterval = null; } if (scriptState.idleTimeout) { clearTimeout(scriptState.idleTimeout); scriptState.idleTimeout = null; } log("脚本已停止"); } // 更新活跃度统计 function updateActivityStats() { const activityStats = document.getElementById('activity-stats'); if (activityStats) { const elapsed = Date.now() - (scriptState.startTime || Date.now()); const minutes = Math.floor(elapsed / 60000); const seconds = Math.floor((elapsed % 60000) / 1000); activityStats.innerHTML = ` <div>运行时间: ${minutes}分${seconds}秒</div> <div>滚动次数: ${scriptState.activityStats.scrolls}</div> <div>点击次数: ${scriptState.activityStats.clicks}</div> <div>按键次数: ${scriptState.activityStats.keystrokes}</div> <div>鼠标移动: ${scriptState.activityStats.mousemoves}</div> `; } } // 更新UI状态 function updateUI() { const statusIndicator = document.querySelector('.status-indicator'); const statusText = document.querySelector('.status-text'); const progressInfo = document.querySelector('.progress-info'); const courseListEl = document.querySelector('.course-list'); const debugInfo = document.querySelector('.debug-info'); if (statusIndicator) { if (scriptState.isRunning) { statusIndicator.classList.add('active'); statusText.textContent = scriptState.waitingForNextCourse ? '即将切换下一课...' : '运行中 - 自动化学习进行中'; } else { statusIndicator.classList.remove('active'); statusText.textContent = '已停止 - 点击启动按钮开始'; } } if (progressInfo) { let progressText = `进度: ${scriptState.currentContentIndex + 1}/${scriptState.courseProgress.total} 课程`; if (scriptState.courseProgress.completed > 0) { progressText += ` (已完成: ${scriptState.courseProgress.completed})`; } if (scriptState.courseProgress.current) { progressText += `\n当前: ${scriptState.courseProgress.current}`; } if (scriptState.currentPage > 1 || document.querySelector('.pagination')) { progressText += `\n页面: ${scriptState.currentPage}`; } // 添加学习时间信息 if (scriptState.startTime) { const elapsed = Date.now() - scriptState.startTime; progressText += `\n学习时间: ${Math.floor(elapsed/60000)}分${Math.floor((elapsed%60000)/1000)}秒`; } progressText += `\n底部计数: ${scriptState.bottomReachedCount}/${scriptState.config.scrollTimeThreshold}`; progressInfo.textContent = progressText; } // 更新课程列表 if (courseListEl) { courseListEl.innerHTML = ''; scriptState.courseList.forEach((course, index) => { const item = document.createElement('div'); item.className = 'course-item'; if (index === scriptState.currentContentIndex) { item.classList.add('active'); } const href = course.getAttribute('href') || ''; const contentId = extractContentId(href); const isCompleted = contentId && scriptState.completedCourses.has(contentId); item.textContent = `${index + 1}. ${course.textContent.trim().substring(0, 20)} ${isCompleted ? '✓' : ''}`; // 添加点击课程的功能 item.addEventListener('click', () => jumpToCourse(index)); courseListEl.appendChild(item); }); } // 更新活跃度统计 updateActivityStats(); // 更新调试信息 if (debugInfo) { debugInfo.textContent = scriptState.debugInfo; } } // 更新状态文本 function updateStatus(message) { const statusText = document.querySelector('.status-text'); if (statusText) { statusText.textContent = message; } } // 创建控制面板 function createControlPanel() { console.log("[培训助手] 开始创建控制面板"); // 检查是否已存在控制面板 if (document.getElementById('control-panel')) { console.log("[培训助手] 控制面板已存在"); return; } const panel = document.createElement('div'); panel.id = 'control-panel'; panel.innerHTML = ` <div class="panel-header"> <div class="panel-title"> <span class="panel-icon">🎓</span> <span>培训助手 5.0</span> </div> <button class="toggle-btn" id="toggle-panel">−</button> </div> <div class="panel-content"> <div class="control-group"> <label class="control-label">滚动速度</label> <input type="range" class="control-input" id="scroll-speed" min="1000" max="5000" step="500" value="${scriptState.config.scrollSpeed}"> </div> <div class="control-group"> <label class="control-label">最短阅读时间 (分钟)</label> <input type="number" class="control-input" id="min-read-time" value="${scriptState.config.minReadTime/60000}" min="1" max="30"> </div> <div class="checkbox-group"> <input type="checkbox" class="checkbox-input" id="auto-next-content" ${scriptState.config.autoNextContent ? 'checked' : ''}> <label class="control-label" for="auto-next-content">自动切换下一课</label> </div> <div class="btn-group"> <button class="panel-btn start-btn" id="start-btn">启动</button> <button class="panel-btn stop-btn" id="stop-btn">停止</button> </div> <div class="progress-info" id="progress-info">进度: 0/0</div> <div class="scroll-progress"> <div class="scroll-progress-bar"></div> </div> <div id="activity-stats" style="font-size:12px; margin:5px 0; background:rgba(0,0,0,0.1); padding:5px; border-radius:4px;"> 活跃度数据载入中... </div> <div class="course-list" id="course-list"></div> <div style="display:flex; gap:5px; margin-top:5px;"> <button class="debug-btn" id="debug-next">切换下一课</button> <button class="debug-btn" id="debug-time">触发学时</button> </div> <div style="display:flex; gap:5px; margin-top:5px;"> <button class="debug-btn" id="debug-scroll">滚动到底部</button> <button class="debug-btn" id="debug-refresh">刷新课程</button> </div> <div class="status-text"> <span class="status-indicator"></span> 已停止 - 点击启动按钮开始 </div> <div class="debug-info" style="font-size:10px; margin-top:5px; color:#eee; max-height:60px; overflow-y:auto;"></div> </div> `; // 尝试添加到body if (document.body) { document.body.appendChild(panel); console.log("[培训助手] 控制面板已添加到body"); } else { console.error("[培训助手] document.body不存在"); document.documentElement.appendChild(panel); console.log("[培训助手] 控制面板已添加到documentElement"); } // 添加事件监听器 document.getElementById('start-btn').addEventListener('click', startScript); document.getElementById('stop-btn').addEventListener('click', stopScript); document.getElementById('debug-next').addEventListener('click', manualGoToNextContent); document.getElementById('debug-scroll').addEventListener('click', manualScrollToBottom); document.getElementById('debug-time').addEventListener('click', manualTriggerStudyTime); document.getElementById('debug-refresh').addEventListener('click', refreshCourseStatus); document.getElementById('toggle-panel').addEventListener('click', function() { panel.classList.toggle('collapsed'); this.textContent = panel.classList.contains('collapsed') ? '+' : '−'; }); // 配置更新 document.getElementById('scroll-speed').addEventListener('change', function() { scriptState.config.scrollSpeed = parseInt(this.value); log(`滚动速度已更新为 ${this.value}ms`); }); document.getElementById('min-read-time').addEventListener('change', function() { scriptState.config.minReadTime = parseInt(this.value) * 60000; // 转换为毫秒 log(`最短阅读时间已更新为 ${this.value} 分钟`); }); document.getElementById('auto-next-content').addEventListener('change', function() { scriptState.config.autoNextContent = this.checked; log(`自动切换下一课已${this.checked ? '启用' : '禁用'}`); }); // 初始刷新 refreshCourseStatus(); updateActivityStats(); console.log("[培训助手] 控制面板创建完成"); } // 初始化脚本 function initScript() { console.log("[培训助手] 初始化培训助手脚本"); // 创建控制面板 createControlPanel(); console.log("[培训助手] 控制面板已创建,请点击启动按钮开始"); } // 尝试多种方式初始化脚本 function tryInit() { if (document.readyState === 'complete' || document.readyState === 'interactive') { console.log("[培训助手] 文档已就绪,立即初始化"); initScript(); } else { console.log("[培训助手] 等待文档加载完成"); window.addEventListener('DOMContentLoaded', initScript); window.addEventListener('load', initScript); } } // 立即尝试初始化 tryInit(); // 备用初始化(如果上面的方法失败) setTimeout(() => { if (!document.getElementById('control-panel')) { console.log("[培训助手] 备用初始化"); initScript(); } }, 3000); console.log("[培训助手] 脚本加载完成"); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址