Greasy Fork

Greasy Fork is available in English.

智慧树刷课脚本(增强版-优化进度显示) -by这是哪头猪?

智慧树自动刷课程序,支持自动获取课程信息和控制功能,优化日志显示和进度显示

当前为 2024-11-09 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         智慧树刷课脚本(增强版-优化进度显示) -by这是哪头猪?
// @namespace    http://tampermonkey.net/
// @version      2.2
// @description  智慧树自动刷课程序,支持自动获取课程信息和控制功能,优化日志显示和进度显示
// @author       Anony
// @match        http://www.learning.mil.cn/student/study
// @grant        GM_xmlhttpRequest
// @connect      armystudy.zhihuishu.com
// @connect      www.learning.mil.cn
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    let courses = [];
    let currentTask = null;

    // 添加控制面板
    function addPanel() {
        const panel = document.createElement('div');
        panel.style.cssText = `
            position: fixed;
            top: 20px;
            right: 20px;
            background: #fff;
            padding: 15px;
            border: 1px solid #ccc;
            border-radius: 5px;
            z-index: 9999;
            box-shadow: 0 0 10px rgba(0,0,0,0.1);
            width: 300px;
        `;

        const panelHTML = `
            <div style="margin-bottom: 10px;">
                <h3 style="margin: 0 0 10px 0;">智慧树课程列表</h3>
                <div id="courseList" style="max-height: 300px; overflow-y: auto;"></div>
            </div>
            <div id="status" style="margin-top: 10px; color: #666; font-size: 12px;"></div>
            <div id="log" style="margin-top: 10px; max-height: 150px; overflow-y: auto; font-size: 12px; border-top: 1px solid #eee; padding-top: 10px;"></div>
        `;

        panel.innerHTML = panelHTML;
        document.body.appendChild(panel);

        // 添加日志头部
        addLogHeader();
    }

    // 格式化日志条目
    function formatLogEntry(chapterName, progress, result) {
        return `
            <div style="display: grid; grid-template-columns: 40% 30% 30%; gap: 10px; padding: 8px 0; border-bottom: 1px solid #eee;">
                <div style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">${chapterName}</div>
                <div>${progress}</div>
                <div style="color: ${result.success ? '#4CAF50' : '#ff4444'}">${result.msg || '完成'}</div>
            </div>
        `;
    }

    // 添加日志头部
    function addLogHeader() {
        const logElement = document.getElementById('log');
        if (logElement) {
            const header = document.createElement('div');
            header.className = 'log-header';
            header.style.cssText = `
                display: grid;
                grid-template-columns: 40% 30% 30%;
                gap: 10px;
                padding: 8px 0;
                font-weight: bold;
                border-bottom: 2px solid #ddd;
                background: #f5f5f5;
                position: sticky;
                top: 0;
            `;
            header.innerHTML = `
                <div>课程章节</div>
                <div>观看进度</div>
                <div>观看结果</div>
            `;
            logElement.insertBefore(header, logElement.firstChild);
        }
    }

    // 添加日志
    function addLog(entry) {
        const logElement = document.getElementById('log');
        if (logElement) {
            // 如果是文本日志(比如状态更新),添加到最顶部
            if (typeof entry === 'string') {
                const textEntry = document.createElement('div');
                textEntry.style.cssText = `
                    padding: 5px 0;
                    border-bottom: 1px solid #eee;
                    color: #666;
                `;
                textEntry.textContent = `[${new Date().toLocaleTimeString()}] ${entry}`;
                // 将文本日志添加到标题行的后面
                if (logElement.children.length > 1) {
                    logElement.insertBefore(textEntry, logElement.children[1]);
                } else {
                    logElement.appendChild(textEntry);
                }
            }
            // 如果是课程观看记录,添加到文本日志后面
            else if (entry.type === 'watch') {
                const logEntry = document.createElement('div');
                logEntry.innerHTML = formatLogEntry(entry.chapter, entry.progress, entry.result);
                // 确保课程记录添加到文本日志的后面
                logElement.appendChild(logEntry);
            }
        }
    }

    // 获取课程列表
    async function fetchCourseList() {
        try {
            const response = await makeRequest('http://www.learning.mil.cn/api/web/student/enrollment/course/?limit=7&offset=0', {
                method: 'GET',
                headers: {
                    'Cookie': document.cookie
                }
            });

            courses = response.results.filter(course => course.platform === 33);
            updateCourseList();
        } catch (error) {
            addLog('获取课程列表失败: ' + error.message);
        }
    }

    // 更新课程列表显示
    function updateCourseList() {
        const courseListElement = document.getElementById('courseList');
        if (!courseListElement) return;

        courseListElement.innerHTML = courses.map((course, index) => `
            <div style="padding: 10px; border-bottom: 1px solid #eee; display: flex; justify-content: space-between; align-items: center;">
                <div style="flex: 1;">
                    <div style="font-weight: bold;">${course.course_title}</div>
                    <div style="font-size: 12px; color: #666;">进度: ${course.learned_process}</div>
                </div>
                <button
                    id="courseBtn_${index}"
                    data-index="${index}"
                    style="padding: 5px 10px; border-radius: 3px; cursor: pointer; border: none; background: ${currentTask && currentTask.courseIndex === index ? '#ff4444' : '#4CAF50'}; color: white;"
                >
                    ${currentTask && currentTask.courseIndex === index ? '停止' : '开始'}
                </button>
            </div>
        `).join('');

        courses.forEach((_, index) => {
            const btn = document.getElementById(`courseBtn_${index}`);
            if (btn) {
                btn.addEventListener('click', () => toggleCourse(index));
            }
        });
    }

    // 切换课程状态
    function toggleCourse(index) {
        if (currentTask && currentTask.courseIndex === index) {
            currentTask = null;
            updateCourseList();
            addLog(`停止处理课程: ${courses[index].course_title}`);
        } else {
            if (currentTask) {
                currentTask = null;
                updateCourseList();
            }
            const course = courses[index];
            currentTask = {
                courseIndex: index,
                userId: course.user_id,
                courseId: course.course_id
            };
            updateCourseList();
            addLog(`开始处理课程: ${course.course_title}`);
            zhihuishu(course.user_id, course.course_id);
        }
    }

    // 更新状态显示
    function updateStatus(message) {
        const statusElement = document.getElementById('status');
        if (statusElement) {
            statusElement.textContent = message;
        }
        addLog(message);
    }

    // 使用GM_xmlhttpRequest进行请求
    function makeRequest(url, options) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: options.method || 'GET',
                url: url,
                headers: options.headers || {},
                data: options.body,
                responseType: 'json',
                onload: function(response) {
                    if (response.status >= 200 && response.status < 300) {
                        resolve(response.response);
                    } else {
                        reject(new Error('请求失败: ' + response.status));
                    }
                },
                onerror: function(error) {
                    reject(error);
                }
            });
        });
    }

    // 主处理函数
    async function zhihuishu(userId, courseId) {
        if (!currentTask) return;

        const queryUrl = `http://armystudy.zhihuishu.com/armystudy/queryChapterInfos?userId=${userId}&courseId=${courseId}&ts&token`;
        const setWichTime = 'http://armystudy.zhihuishu.com/armystudy/stuRecord';

        try {
            updateStatus('正在获取课程信息...');
            const chapterInfo = await makeRequest(queryUrl, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded'
                },
                body: new URLSearchParams({
                    userId: userId.toString(),
                    courseId: courseId.toString()
                }).toString()
            });

            if (!chapterInfo.chapterInfoDtoList) {
                throw new Error('获取课程信息失败,请检查ID是否正确');
            }

            const chapterInfoDtoList = chapterInfo.chapterInfoDtoList;
            let totalLessons = 0;
            let completedLessons = 0;

            chapterInfoDtoList.forEach(chapter => {
                totalLessons += chapter.lessonInfoDtos.length;
            });

            for (const chapter of chapterInfoDtoList) {
                if (!currentTask) return;

                for (const lesson of chapter.lessonInfoDtos) {
                    if (!currentTask) return;

                    completedLessons++;
                    updateStatus(`正在处理: ${lesson.lessonName} (${completedLessons}/${totalLessons}课)`);

                    const videoTime = lesson.videoTime;
                    let s = videoTime.split(':')[0];
                    let watchTime = 0;

                    if (s.startsWith('0')) {
                        watchTime = (parseInt(s.replace('0', '')) + 1) * 60;
                    } else {
                        watchTime = (parseInt(s) + 1) * 60;
                    }

                    let hh = Math.floor(watchTime / 3);
                    let lastResult = null;

                    // 执行三次观看,但只记录最后一次的结果
                    for (let i = 0; i < 3; i++) {
                        if (!currentTask) return;

                        const formData = new URLSearchParams({
                            userId: userId.toString(),
                            courseId: courseId.toString(),
                            videoId: lesson.videoId.toString(),
                            exitwatchTime: '0',
                            lessonId: lesson.lessonId.toString(),
                            measureId: lesson.measureId.toString(),
                            videoNum: '53',
                            watchTime: hh.toString()
                        }).toString();

                        lastResult = await makeRequest(setWichTime, {
                            method: 'POST',
                            headers: {
                                'Content-Type': 'application/x-www-form-urlencoded'
                            },
                            body: formData
                        });

                        hh++;
                        await new Promise(resolve => setTimeout(resolve, 2000 + Math.random() * 2000));
                    }

                    // 只在完成三次观看后添加一次日志
                    addLog({
                        type: 'watch',
                        chapter: lesson.lessonName,
                        progress: `${completedLessons}/${totalLessons}`,
                        result: lastResult
                    });
                }
            }

            addLog('课程处理完成!');
            updateStatus('完成!');
            currentTask = null;
            updateCourseList();
        } catch (error) {
            console.error('处理过程中出错:', error);
            addLog('处理过程中出错: ' + error.message);
            updateStatus('出错: ' + error.message);
            currentTask = null;
            updateCourseList();
        }
    }

    // 初始化
    addPanel();
    fetchCourseList();

})();