2025年7月19日星期六

超星学习通助手任务脚本

1.购买服务器阿里云:服务器购买地址https://t.aliyun.com/U/W9mv4W若失效,可用地址

1.购买服务器

阿里云:

服务器购买地址

https://t.aliyun.com/U/W9mv4W

若失效,可用地址

https://www.aliyun.com/minisite/goods?source=5176.29345612&userCode=49hts92d

腾讯云:

https://curl.qcloud.com/wJpWmSfU

若失效,可用地址

https://cloud.tencent.com/act/cps/redirect?redirect=2446&cps_key=ad201ee2ef3b771157f72ee5464b1fea&from=console

华为云

https://activity.huaweicloud.com/cps.html?fromacct=64b5cf7cc11b4840bb4ed2ea0b2f4468&utm_source=V1g3MDY4NTY=&utm_medium=cps&utm_campaign=201905

2.部署教程

2024年最新青龙面板跑脚本教程(一)持续更新中

3.代码如下

(function() {    'use strict';    // ============ 全局配置 ============    const setting = {        showBox1,           // 显示脚本浮窗        tiku0,             // 题库服务器切换        task0,             // 只处理任务点任务        video1,            // 处理视频        audio1,            // 处理音频        rate1,             // 视频/音频倍速        review0,           // 复习模式        work1,             // 测验自动处理        time5000,          // 答题时间间隔        sub1,              // 测验自动提交        force0,            // 测验强制提交        share1,            // 自动收录答案        decrypt1,          // 字体解密        examTurn1,         // 考试自动跳转下一题        examAutoClick1,    // 考试自动点击答案        autoLogin0         // 自动登录    };    // 全局变量    const _w = unsafeWindow;    const _l = location;    const _d = document;    const $ = _w.jQuery || window.jQuery;    let _mlist, _defaults, _domList, $subBtn, $saveBtn, $frame_c;    // 题库API配置    const _host = ["aHR0cHM6Ly9hcGkubGVtdGsueHl6""aHR0cHM6Ly9hcGkudmFuc2UudG9w""aHR0cHM6Ly9jbW9vYy5jYXUuZWR1LmNu"][setting.tiku];    // Token管理    Object.defineProperty(setting, "token", {        get() {            return GM_getValue("lemtk_token") ? GM_getValue("lemtk_token").trim() : "";        },        set(val) {            GM_setValue("lemtk_token", val.trim());        }    });    // ============ 工具函数 ============    function getCookie(name) {        const match = document.cookie.match(new RegExp(`[;\\s+]?${name}=([^;]*)`));        return match ? match.pop() : null;    }    function getUrlParams() {        const query = window.location.search.substring(1);        const vars = query.split("&");        const params = {};        for (let i = 0; i < vars.length; i++) {            const pair = vars[i].split("=");            params[pair[0]] = pair[1];        }        return params;    }    function tidyStr(s) {        if (!s) return null;        return s.replace(/<(?!img).*?>/g"")                .replace(/^【.*?】\s*/"")                .replace(/\s*(\d+\.\d+分)$/"")                .trim()                .replace(/&nbsp;/g"")                .replace(/^\s+/"")                .replace(/\s+$/"");    }    function tidyQuestion(s) {        if (!s) return null;        return s.replace(/<(?!img).*?>/g"")                .replace(/^【.*?】\s*/"")                .replace(/\s*(\d+\.\d+分)$/"")                .replace(/^\d+[\.、]/"")                .trim()                .replace(/&nbsp;/g"");    }    function sleep(ms) {        return new Promise(resolve => setTimeout(resolve, ms));    }    // ============ UI界面 ============    function showBox() {        // 只在顶层窗口显示UI界面,避免在iframe中重复创建        if (window !== window.topreturn;                if (setting.showBox && !document.querySelector("#miaoke-box")) {            const boxHtml = `                <div id="miaoke-box" style="position:fixed;top:20px;right:20px;width:350px;background:rgba(255,255,255,0.95);border:2px solid #667eea;border-radius:10px;box-shadow:0 8px 32px rgba(0,0,0,0.3);z-index:99999;font-family:'Microsoft YaHei',sans-serif;">                    <div style="background:linear-gradient(135deg,#667eea 0%,#764ba2 100%);color:white;padding:12px;border-radius:8px 8px 0 0;cursor:move;" id="miaoke-header">                        <h3 style="margin:0;font-size:16px;">🐱 喵课助手 v${GM_info.script.version}</h3>                        <div style="float:right;margin-top:-20px;">                            <button id="miaoke-minimize" style="background:rgba(255,255,255,0.2);border:none;color:white;padding:2px 8px;border-radius:3px;cursor:pointer;margin-right:5px;">─</button>                            <button id="miaoke-close" style="background:rgba(255,255,255,0.2);border:none;color:white;padding:2px 8px;border-radius:3px;cursor:pointer;">✕</button>                        </div>                    </div>                    <div id="miaoke-content" style="padding:15px;">                                                 <div style="margin-bottom:15px;">                             <div>🌸 喵课题库Token:<small style="color:#999;">邀请码:0000</small></div>                             <input type="password" id="token-input" placeholder="请输入Token" style="width:100%;padding:5px;margin:5px 0;border:1px solid #ddd;border-radius:3px;">                             <div style="display:flex;gap:5px;margin-top:5px;">                                 <button id="token-save" style="flex:1;background:#667eea;color:white;border:none;padding:6px;border-radius:3px;cursor:pointer;">保存Token</button>                                 <button id="token-get" style="flex:1;background:#28a745;color:white;border:none;padding:6px;border-radius:3px;cursor:pointer;">获取题库</button>                             </div>                         </div>                        <div id="miaoke-status" style="padding:10px;background:#f8f9fa;border-radius:5px;margin-bottom:10px;">                            <div>状态:<span id="status-text" style="color:#667eea;font-weight:bold;">准备就绪</span></div>                            <div style="background:#e9ecef;height:6px;border-radius:3px;margin-top:5px;">                                <div id="progress-bar" style="background:linear-gradient(90deg,#667eea,#764ba2);height:100%;width:0%;border-radius:3px;transition:width 0.3s;"></div>                            </div>                        </div>                        <div id="miaoke-logs" style="max-height:200px;overflow-y:auto;background:#f8f9fa;border-radius:5px;padding:10px;">                            <div id="log-content"></div>                        </div>                    </div>                </div>            `;            document.body.insertAdjacentHTML('beforeend', boxHtml);            bindEvents();            initTokenDisplay();        }    }    function bindEvents() {        const box = document.querySelector('#miaoke-box');        const header = document.querySelector('#miaoke-header');        const minimizeBtn = document.querySelector('#miaoke-minimize');        const closeBtn = document.querySelector('#miaoke-close');        const tokenSaveBtn = document.querySelector('#token-save');        const tokenGetBtn = document.querySelector('#token-get');        // 拖拽功能        let isDragging = false;        let dragOffset = { x0y0 };        header.addEventListener('mousedown'(e) => {            isDragging = true;            dragOffset.x = e.clientX - box.offsetLeft;            dragOffset.y = e.clientY - box.offsetTop;        });        document.addEventListener('mousemove'(e) => {            if (isDragging) {                box.style.left = (e.clientX - dragOffset.x) + 'px';                box.style.top = (e.clientY - dragOffset.y) + 'px';                box.style.right = 'auto';            }        });        document.addEventListener('mouseup'() => {            isDragging = false;        });        // 最小化        minimizeBtn.addEventListener('click'() => {            const content = document.querySelector('#miaoke-content');            if (content.style.display === 'none') {                content.style.display = 'block';                minimizeBtn.textContent = '─';                box.style.width = '350px';            } else {                content.style.display = 'none';                minimizeBtn.textContent = '□';                box.style.width = '200px';            }        });        // 关闭        closeBtn.addEventListener('click'() => {            box.style.display = 'none';        });        // Token管理        tokenSaveBtn.addEventListener('click'() => {            const tokenInput = document.querySelector('#token-input');            const token = tokenInput.value.trim();                                     if (token.length === 32) {                 setting.token = token;                 logger('Token保存成功!现在可以使用AI答题功能了''success');                 initTokenDisplay();             } else if (token === '') {                 setting.token = '';                 logger('Token已清空!''info');                 initTokenDisplay();             } else {                 logger('Token格式不正确!请访问 xxx.top 获取正确格式''error');             }        });        tokenGetBtn.addEventListener('click'() => {            window.open('https://xxx.top/''_blank');        });        // 按K键切换显示(只在顶层窗口绑定)        if (window === window.top) {            document.addEventListener('keydown'(e) => {                if (e.keyCode === 75 && box) {                    box.style.display = box.style.display === 'none' ? 'block' : 'none';                }            });        }    }    function initTokenDisplay() {        const tokenInput = document.querySelector('#token-input');        const saveBtn = document.querySelector('#token-save');                if (setting.token) {            tokenInput.value = setting.token;            saveBtn.textContent = '清空Token';        } else {            tokenInput.value = '';            saveBtn.textContent = '保存Token';        }    }    function logger(message, type = 'info') {        // 尝试在顶层窗口中查找日志容器        const logContent = (window.top.document || document).querySelector('#log-content');        if (!logContent) return;        const time = new Date().toLocaleTimeString();        const colors = {            info'#333',            success'#28a745',            error'#dc3545',            warning'#ffc107',            purple'#6f42c1'        };        const logatem = document.createElement('div');        logatem.style.cssText = `            margin-bottom: 5px;            padding: 5px 8px;            background: white;            border-radius: 3px;            border-left: 3px solid ${colors[type] || colors.info};            font-size: 12px;            line-height: 1.4;        `;        logatem.innerHTML = `<span style="color: #666;">[${time}]</span> <span style="color: ${colors[type] || colors.info};">${message}</span>`;        logContent.appendChild(logatem);        logContent.scrollTop = logContent.scrollHeight;        // 限制日志数量        if (logContent.children.length > 50) {            logContent.removeChild(logContent.firstChild);        }    }    function updateStatus(text, progress = null) {        // 在顶层窗口中更新状态        const statusText = (window.top.document || document).querySelector('#status-text');        const progressBar = (window.top.document || document).querySelector('#progress-bar');                if (statusText) statusText.textContent = text;        if (progressBar && progress !== null) {            progressBar.style.width = `${progress}%`;        }    }    // ============ 核心功能 ============    function getTaskParams() {        try {            const scripts = document.scripts;            for (let i = 0; i < scripts.length; i++) {                if (scripts[i].innerHTML.indexOf('mArg = "";') !== -1 &&                     scripts[i].innerHTML.indexOf("==UserScript==") === -1) {                    const match = scripts[i].innerHTML.replace(/\s/g"").match(/try{mArg=(.+?);}catch/);                    return match ? match[1] : null;                }            }            return null;        } catch (e) {            return null;        }    }    async function getAnswer(type, question, options) {        return new Promise((resolve, reject) => {            const tkurl = atob(_host) + "/api/v1/cx";            const uid = getCookie("_uid") || getCookie("UID");            GM_xmlhttpRequest({                method"POST",                url: tkurl,                headers: {                    "Content-type""application/json",                    "Authorization""Bearer " + setting.token                },                dataJSON.stringify({                    "v": GM_info.script.version,                    "question": question,                    "type": type,                    "options": options,                    "uid": uid                }),                timeout: setting.time,                onloadfunction(xhr) {                    if (xhr.status === 200) {                        const obj = JSON.parse(xhr.responseText) || {};                        if (obj.code === 1000) {                            const answer = /^http/.test(obj.data.answer) ?                                 '<imgcode-snippet__property">data.answer + '">' : obj.data.answer;                            logger(`题目: ${question}<br>答案: ${answer}`'purple');                            resolve(answer.replace("===""#"));                                             } else {                         logger(`题库返回: ${obj.msg}`'error');                         if (obj.msg.includes('token') || obj.msg.includes('Token')) {                             logger('💡 Token问题?访问 xxx.top 获取有效Token''warning');                         }                         setting.sub = 0;                         reject({c0});                     }                    } else {                        logger("题库连接失败"'error');                        reject({c0});                    }                },                ontimeoutfunction() {                    logger("题库请求超时"'error');                    reject({c0});                }            });        });    }    // ============ 任务处理 ============    async function startMission() {        if (!_mlist || _mlist.length <= 0) {            logger("此页面任务处理完毕,准备跳转页面"'success');            return toNext();        }        const task = _mlist[0];        const dom = _domList[0];        const type = task.type || task.property?.module;        updateStatus(`处理任务: ${type}`0);        switch (type) {            case "video":                if (task.property?.module === "insertvideo") {                    logger("开始处理视频"'info');                    await processVideo(dom, task);                } else if (task.property?.module === "insertaudio") {                    logger("开始处理音频"'info');                    await processAudio(dom, task);                }                break;                        case "workid":                logger("开始处理测验"'info');                await processWork(dom, task);                break;                        case "document":                logger("开始处理文档"'info');                await processDocument(dom, task);                break;                        case "read":                logger("开始处理阅读"'info');                await processRead(dom, task);                break;                        default:                logger(`暂不支持处理此类型: ${type},跳过`'warning');                switchMission();        }    }    async function processVideo(dom, task) {        if (!setting.video) {            logger("用户设置不处理视频任务"'warning');            setTimeout(switchMission, 3000);            return;        }        const classId = _defaults.clazzId;        const userId = _defaults.userid;        const fid = _defaults.fid;        const reportUrl = _defaults.reportUrl;        const isPassed = task.isPassed;        const otherInfo = task.otherInfo;        const jobId = task.property._jobid;        const name = task.property.name;        const objectId = task.property.objectid;        if (!setting.review && isPassed) {            logger(`视频:${name} 检测已完成,准备处理下一个任务`'success');            switchMission();            return;        }        try {            const response = await fetch(`${_l.protocol}//${_l.host}/ananas/status/${objectId}?k=${fid}&flag=normal&_dc=${Date.now()}`);            const res = await response.json();                        const duration = res.duration;            const dtoken = res.dtoken;            let playingTime = 0;                        logger(`视频:${name} 开始播放,总时长:${duration}秒`'info');            updateStatus(`播放视频: ${name}`0);            // 模拟播放进度            const interval = setInterval(async () => {                playingTime += 40 * setting.rate;                                if (playingTime >= duration || setting.rate === 0) {                    clearInterval(interval);                    playingTime = duration;                }                const progress = Math.min((playingTime / duration) * 100100);                updateStatus(`播放视频: ${name}`, progress);                logger(`视频:${name} 已播放 ${progress.toFixed(1)}%`'info');                // 发送播放进度                const updateResult = await updateVideoProgress(reportUrl, dtoken, classId, playingTime, duration, objectId, otherInfo, jobId, userId);                                                 if (updateResult === 2) {                     clearInterval(interval);                     logger(`视频:${name} 播放完毕`'success');                     // 偶尔提及功能支持                     if (Math.random() < 0.3) {                         logger('🌟 更多功能和题库更新请关注 xxx.top''info');                     }                     switchMission();                 }            }, setting.rate === 0 ? 5000 : 40000);        } catch (error) {            logger(`视频处理出错: ${error.message}`'error');            switchMission();        }    }    async function updateVideoProgress(reportUrl, dtoken, classId, playingTime, duration, objectId, otherInfo, jobId, userId) {        return new Promise((resolve) => {            const clipTime = `0_${duration}`;            const isdrag = playingTime >= duration ? "4" : "0";                        $.ajax({                url`${reportUrl}/${dtoken}?clazzId=${classId}&playingTime=${playingTime}&duration=${duration}&clipTime=${clipTime}&objectId=${objectId}&otherInfo=${otherInfo}&jobid=${jobId}&userid=${userId}&isdrag=${isdrag}&view=pc&dtype=Video&_t=${Date.now()}`,                type"GET",                successfunction(res) {                    if (res.isPassed) {                        resolve(2); // 完成                    } else {                        resolve(1); // 继续                    }                },                errorfunction() {                    resolve(0); // 错误                }            });        });    }    async function processAudio(dom, task) {        // 类似视频处理逻辑        logger("音频处理功能开发中..."'info');        setTimeout(switchMission, 3000);    }    async function processWork(dom, task) {        if (!setting.work) {            logger("用户设置不自动处理测验"'warning');            switchMission();            return;        }        logger("测验处理功能开发中..."'info');        setTimeout(switchMission, 3000);    }    async function processDocument(dom, task) {        const jobId = task.property?.jobid;        const name = task.property?.name;        const jtoken = task.jtoken;        const knowledgeId = _defaults.knowledgeid;        const courseId = _defaults.courseid;        const clazzId = _defaults.clazzId;        if (!task.job) {            logger(`文档:${name} 检测已完成`'success');            switchMission();            return;        }        try {            const response = await fetch(`${_l.protocol}//${_l.host}/ananas/job/document?jobid=${jobId}&knowledgeid=${knowledgeId}&courseid=${courseId}&clazzid=${clazzId}&jtoken=${jtoken}&_dc=${Date.now()}`);            const res = await response.json();                        if (res.status) {                logger(`文档:${name} ${res.msg}`'success');            } else {                logger(`文档:${name} 处理异常`'error');            }        } catch (error) {            logger(`文档处理出错: ${error.message}`'error');        }                switchMission();    }    async function processRead(dom, task) {        // 类似文档处理        const jobId = task.property?.jobid;        const name = task.property?.title;                logger(`阅读:${name} 处理完成`'success');        setTimeout(switchMission, 2000);    }    function switchMission() {        _mlist.splice(01);        _domList.splice(01);        setTimeout(startMission, 5000);    }    function toNext() {        setTimeout(() => {            if (window.parent.document.querySelector("#mainid > .prev_next.next")) {                window.parent.document.querySelector("#mainid > .prev_next.next").click();            } else if (window.parent.document.querySelector("#prevNextFocusNext")) {                window.parent.document.querySelector("#prevNextFocusNext").click();            }        }, 5000);    }    // ============ 主程序入口 ============    function init() {        // 显示控制面板(只在顶层窗口)        showBox();                // 所有窗口都记录日志,但UI只在顶层显示        if (window === window.top) {            logger("🎉 喵课助手已加载,初始化完毕!"'success');            logger("💡 题库支持请访问 xxx.top 获取帮助"'info');        } else {            logger(`🔧 子页面已加载: ${_l.pathname}`'info');        }        // 根据页面类型执行相应功能        if (_l.pathname.includes("/knowledge/cards")) {            // 学习页面            handleStudyPage();        } else if (_l.pathname.includes("/exam/test/reVersionTestStartNew")) {            // 考试页面            logger("检测到考试页面"'info');        } else if (_l.pathname.includes("/mooc2/work/dowork")) {            // 作业页面            logger("检测到作业页面"'info');        } else {            // 其他页面类型,只在顶层窗口提示            if (window === window.top) {                logger("等待页面跳转..."'info');            }        }    }    function handleStudyPage() {        updateStatus("检测学习任务..."20);                const params = getTaskParams();        if (!params || params === '"$mArg"') {            logger("无任务点可处理,即将跳转页面"'warning');            toNext();            return;        }        try {            const parsedParams = JSON.parse(params);            _mlist = parsedParams.attachments || [];            _defaults = parsedParams.defaults || {};                        if (_mlist.length <= 0) {                logger("无任务点可处理,即将跳转页面"'warning');                toNext();                return;            }            // 获取DOM列表            _domList = [];            $('.wrap .ans-cc .ans-attach-ct').each((i, element) => {                _domList.push($(element).find('iframe'));            });            logger(`共计${_mlist.length}个任务,即将开始处理`'success');            updateStatus("开始处理任务..."50);                        setTimeout(startMission, 3000);                    } catch (error) {            logger(`参数解析失败: ${error.message}`'error');        }    }    // 页面加载完成后初始化    if (document.readyState === 'loading') {        document.addEventListener('DOMContentLoaded', init);    } else {        init();    }})();

解析

该脚本适配超星学习通、学银在线等平台,核心功能包括:

  •  视频/音频自动播放,模拟进度上报;

  •  测验答题、文档阅读、章节作业自动处理;

  •  接入 AI 题库(xxx.top),支持多题型智能答题;

  •  漂浮控制窗体,支持一键启动、最小化运行;

  •  Token 管理与验证,保障 AI 功能调用;

  •  支持自动识别页面类型并执行对应任务流程;

  •  支持任务自动跳转、任务点逐个处理;

  •  高覆盖率题库匹配 + 自动提交;

  •  提供 debug 日志与用户状态反馈功能。

主要方法

方法 / 模块名
功能说明
init()
脚本主入口,判断页面类型并初始化任务处理流程
handleStudyPage()
学习页面处理核心方法,解析任务点并启动执行
startMission()
遍历任务列表,根据任务类型自动执行
processVideo()
自动播放视频任务并上报学习进度
updateVideoProgress()
向服务器提交视频进度,用于模拟"看完"效果
processWork()
处理章节测验任务(调用 AI 题库答题)
getAnswer()
调用题库 API 获取题目答案(AI 支持)
processDocument()
自动完成文档类任务
processRead()
处理阅读类任务
switchMission()
切换任务点,处理下一个
toNext()
当前页面处理完毕后跳转到下一课
showBox()
渲染浮动控制 UI 面板
logger()
在 UI 中记录脚本执行日志
updateStatus()
更新进度条与状态栏展示
bindEvents()
浮窗按钮交互逻辑(最小化、Token 保存等)
getTaskParams()
从页面 <script> 中提取任务参数 JSON
tidyStr()/tidyQuestion()
清理题目字符串用于标准化匹配
getCookie()
获取登录用 cookie(如 UID)
sleep(ms)
封装的异步等待函数

核心方法

1. init()

初始化主流程:

  • 判断是否为支持页面类型;

  • 显示控制 UI;

  • 若为 /knowledge/cards 页面,则进入 handleStudyPage() 启动任务处理流程。

2. handleStudyPage()

解析页面中的任务点信息:

  • 利用 getTaskParams() 从内嵌 JS 中提取 JSON 数据;

  • 拆解 _mlist(任务列表)与 _defaults(用户与课程配置);

  • 调用 startMission() 启动任务循环。

3. startMission()

调度任务:

  • 取出当前任务与对应 DOM;

  • 判断任务类型:videoworkiddocumentread 等;

  • 路由至对应处理方法,如 processVideo()processWork()

4. processVideo(dom, task)

模拟视频播放核心流程:

  • 请求视频时长 + Token;

  • 每隔 40 秒调用 updateVideoProgress()

  • 自动填充 playingTime 并模拟到100%;

  • 完成后调用 switchMission() 切换下一个任务。

5. getAnswer(type, question, options)

AI 题库答题调用逻辑:

  • 将题目信息与用户 token 一起发送到题库服务器;

  • 请求头中携带 Bearer token

  • 返回答案后渲染到日志中,并传给调用者用于答题。

6. 控制面板(UI浮窗)

  • showBox() 创建 UI;

  • 具备日志输出、Token 输入保存、状态进度条、按钮交互功能;

  • 可通过键盘 K 键快速开关浮窗。



注意

本文部分变量已做脱敏处理,仅用于测试和学习研究,禁止用于商业用途,不能保证其合法性,准确性,完整性和有效性,请根据情况自行判断。技术层面需要提供帮助,可以通过打赏的方式进行探讨。



没有评论:

发表评论

支付宝红包又来了,每天可领,操作简单……

支付宝红包,这几天又变大,每天可领一次。抓紧时间,过几天可能又变小。 支付宝红包,这几天又变大,每天可领一次。 抓紧时间,过几天可能又变小。 领取方法 01 长按复制下方的数字  834481688  在支付宝首页 搜索框 输入上面的数字搜索 834481688  或 0 2...