地理信息安全在线培训自动化学习(活跃度增强版)

增强用户活跃度模拟,确保持续计算学时,平滑滚动和定期交互

目前為 2025-09-28 提交的版本,檢視 最新版本

// ==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或关注我们的公众号极客氢云获取最新地址