在您安裝前,Greasy Fork希望您了解本腳本包含“負面功能”,可能幫助腳本的作者獲利,而不能給你帶來任何收益。
此腳本只有在您 付費後才能使用全部的功能。 Greasy Fork尚未支付費用,因此無法驗證使用需要付費的商品,也無法幫助您獲得退款。
腳本的作者解釋:
脚本使用喵课题库进行AI答题,您可以访问官网 mk.zizizi.top 了解更多或停用答题功能
【重構優化版】支援超星學習通、學銀線上等平台的影片、章節測驗、文件、直播、作業、考試;腳本一鍵啟動、全自動運作、可最小化;喵課題庫覆蓋率99% | 官網:mk.zizizi.top 邀請碼:0000
// ==UserScript== // @name 超星学习通喵课助手[重构版][AI答题][一键启动][最小化运行] // @name:zh-TW 超星學習通喵課助手 [重構版][AI答題][一鍵啟動][最小化運行] // @name:en Chaoxing MiaoKe Learning Assistant [Refactored][AI Answer][One-click Start][Minimize Run] // @description 【完整功能版】支持超星学习通、学银在线等平台的视频、章节测试、文档、直播、作业、考试;脚本一键启动、全自动运行、可最小化;喵课题库覆盖率99%,支持各种题型 | 题库支持:mk.zizizi.top 邀请码:0000 // @description:zh-TW 【重構優化版】支援超星學習通、學銀線上等平台的影片、章節測驗、文件、直播、作業、考試;腳本一鍵啟動、全自動運作、可最小化;喵課題庫覆蓋率99% | 官網:mk.zizizi.top 邀請碼:0000 // @description:en 【Refactored】Supports videos, tests, documents, live broadcasts, homework, and exams on Chaoxing and other platforms; one-click start, fully automatic, minimizable; MiaoKe database 99% coverage | Website: mk.zizizi.top Code: 0000 // @antifeature payment 脚本使用喵课题库进行AI答题,您可以访问官网 mk.zizizi.top 了解更多或停用答题功能 // @namespace 喵课助手 // @version 3.0.3 // @author 喵课团队 // @run-at document-end // @storageName 喵课助手 // @match *://*.edu.cn/* // @match *://*.chaoxing.com/* // @match *://*.xueyinonline.com/* // @icon https://mk.zizizi.top/favicon.ico // @grant GM_xmlhttpRequest // @grant GM_setValue // @grant GM_getValue // @grant GM_info // @grant unsafeWindow // @connect mk.zizizi.top // @connect cx.icodef.com // @connect lemtk.xyz // @license MIT // @compatible firefox // @compatible chrome // @compatible edge // @supportURL https://mk.zizizi.top/ // ==/UserScript== (function() { 'use strict'; // ============ 全局配置 ============ const setting = { showBox: 1, // 显示脚本浮窗 tiku: 0, // 题库服务器切换 task: 0, // 只处理任务点任务 video: 1, // 处理视频 audio: 1, // 处理音频 rate: 1, // 视频/音频倍速 review: 0, // 复习模式 work: 1, // 测验自动处理 time: 5000, // 答题时间间隔 sub: 1, // 测验自动提交 force: 0, // 测验强制提交 share: 1, // 自动收录答案 decrypt: 1, // 字体解密 examTurn: 1, // 考试自动跳转下一题 examAutoClick: 1, // 考试自动点击答案 autoLogin: 0 // 自动登录 }; // 全局变量 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(/ /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(/ /g, ""); } function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } // ============ UI界面 ============ function showBox() { // 只在顶层窗口显示UI界面,避免在iframe中重复创建 if (window !== window.top) return; 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 = { x: 0, y: 0 }; 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格式不正确!请访问 mk.zizizi.top 获取正确格式', 'error'); } }); tokenGetBtn.addEventListener('click', () => { window.open('https://mk.zizizi.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 logItem = document.createElement('div'); logItem.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; `; logItem.innerHTML = `<span style="color: #666;">[${time}]</span> <span style="color: ${colors[type] || colors.info};">${message}</span>`; logContent.appendChild(logItem); 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 }, data: JSON.stringify({ "v": GM_info.script.version, "question": question, "type": type, "options": options, "uid": uid }), timeout: setting.time, onload: function(xhr) { if (xhr.status === 200) { const obj = JSON.parse(xhr.responseText) || {}; if (obj.code === 1000) { const answer = /^http/.test(obj.data.answer) ? '<img src="' + obj.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问题?访问 mk.zizizi.top 获取有效Token', 'warning'); } setting.sub = 0; reject({c: 0}); } } else { logger("题库连接失败", 'error'); reject({c: 0}); } }, ontimeout: function() { logger("题库请求超时", 'error'); reject({c: 0}); } }); }); } // ============ 任务处理 ============ 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) * 100, 100); 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('🌟 更多功能和题库更新请关注 mk.zizizi.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", success: function(res) { if (res.isPassed) { resolve(2); // 完成 } else { resolve(1); // 继续 } }, error: function() { 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(0, 1); _domList.splice(0, 1); 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("💡 题库支持请访问 mk.zizizi.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(); } })();