// ==UserScript==
// @name UOOC 优课联盟助手 - 全能版 (自动刷课/测验AI/讨论区)
// @namespace http://tampermonkey.net/
// @version 1.0.0
// @description 【作者:afdsafg】优课联盟(UOOC)全能辅助工具:1. 视频自动刷课(倍速/静音/连播/防暂停);2. 视频中途弹题秒杀;3. 章节测验AI助手(支持DeepSeek/ChatGPT等多模型并发会诊/一键复制题目);4. 讨论区自动复读刷屏。一站式解决刷课烦恼。
// @author afdsafg
// @match *://*.uooc.net.cn/*
// @match *://*.uooconline.com/*
// @connect *
// @grant GM_xmlhttpRequest
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_setClipboard
// @grant GM_addStyle
// @license MIT
// @downloadURL https://update.greasyfork.icu/scripts/558809/UOOC%20%E4%BC%98%E8%AF%BE%E8%81%94%E7%9B%9F%E5%8A%A9%E6%89%8B%20-%20%E5%85%A8%E8%83%BD%E7%89%88%20%28%E8%87%AA%E5%8A%A8%E5%88%B7%E8%AF%BE%E6%B5%8B%E9%AA%8CAI%E8%AE%A8%E8%AE%BA%E5%8C%BA%29.user.js
// @updateURL https://update.greasyfork.icu/scripts/558809/UOOC%20%E4%BC%98%E8%AF%BE%E8%81%94%E7%9B%9F%E5%8A%A9%E6%89%8B%20-%20%E5%85%A8%E8%83%BD%E7%89%88%20%28%E8%87%AA%E5%8A%A8%E5%88%B7%E8%AF%BE%E6%B5%8B%E9%AA%8CAI%E8%AE%A8%E8%AE%BA%E5%8C%BA%29.meta.js
// ==/UserScript==
(function() {
'use strict';
// ==========================================
// 核心调度中心
// ==========================================
if (window.self === window.top) {
// --- 主窗口环境 ---
initVideoHelper(); // 模块A: 视频刷课 & 弹题
initDiscussionHelper(); // 模块B: 讨论区自动回复
} else {
// --- Iframe 环境 ---
initQuizHelper(); // 模块C: 章节测验 AI 助手
}
// ==========================================
// 模块A:视频刷课 + 悬浮球 + 弹题秒杀
// ==========================================
function initVideoHelper() {
console.log("[UOOC全能助手] 视频模块加载...");
// 1. 注入视频面板样式
const css = `
#uooc-video-panel { position:fixed; top:100px; right:50px; width:220px; background:#2c3e50; color:#fff; z-index:99999; border-radius:6px; font-size:12px; box-shadow:0 4px 15px rgba(0,0,0,0.5); display:flex; flex-direction:column; }
#uooc-drag-bar { padding:8px; background:#34495e; cursor:move; border-radius:6px 6px 0 0; display:flex; justify-content:space-between; align-items:center; user-select:none; }
#uooc-min-ball { position:fixed; top:100px; right:50px; width:45px; height:45px; background:#34495e; border-radius:50%; z-index:99999; display:none; align-items:center; justify-content:center; cursor:move; box-shadow:0 4px 15px rgba(0,0,0,0.5); font-size:20px; user-select:none; border:2px solid #ecf0f1; }
.uooc-row { display:block; margin-bottom:5px; cursor:pointer; }
.uooc-row input { vertical-align:middle; margin-right:5px; }
`;
GM_addStyle(css);
// 2. 创建 UI
const div = document.createElement('div');
div.innerHTML = `
🤖
`;
document.body.appendChild(div);
const panel = document.getElementById('uooc-video-panel');
const ball = document.getElementById('uooc-min-ball');
const minBtn = document.getElementById('uooc-min-btn');
const dragBar = document.getElementById('uooc-drag-bar');
// 默认显示大面板
panel.style.display = 'flex';
// 拖拽逻辑
function makeDraggable(trigger, target, partner) {
let isDragging = false, startX, startY, initLeft, initTop, hasMoved = false;
trigger.addEventListener('mousedown', e => { isDragging=true; hasMoved=false; startX=e.clientX; startY=e.clientY; initLeft=target.offsetLeft; initTop=target.offsetTop; });
document.addEventListener('mousemove', e => {
if (isDragging) {
if (Math.abs(e.clientX - startX) > 3) hasMoved = true;
target.style.left = initLeft + (e.clientX - startX) + 'px'; target.style.top = initTop + (e.clientY - startY) + 'px';
if(partner) { partner.style.left = target.style.left; partner.style.top = target.style.top; }
}
});
document.addEventListener('mouseup', () => { isDragging = false; });
return () => hasMoved;
}
makeDraggable(dragBar, panel, ball);
const checkBallMoved = makeDraggable(ball, ball, panel);
minBtn.onclick = () => { panel.style.display = 'none'; ball.style.display = 'flex'; };
ball.onclick = () => { if (!checkBallMoved()) { ball.style.display = 'none'; panel.style.display = 'flex'; } };
function log(msg) {
const el=document.getElementById('uooc-log');
if(el){el.innerHTML+=`>${msg}
`;el.scrollTop=el.scrollHeight;}
}
// --- 核心循环 (只在有视频的页面生效) ---
setInterval(() => {
let video = document.querySelector('video');
// 只有当前页面有视频标签时,才执行视频逻辑
if(!video) return;
// 1. 弹题处理
if(document.getElementById('cb-pop-quiz').checked) autoAnswerPopQuiz();
// 2. 视频控制
if(document.getElementById('cb-rate').checked) {
if(video.playbackRate !== 2.0) video.playbackRate = 2.0;
if(!video.muted) video.muted = true;
}
if(video.paused && !video.ended) video.play().catch(()=>{});
// 3. 监听结束
if(!video.getAttribute('hooked')) {
video.addEventListener('ended', () => {
if(document.getElementById('cb-auto').checked) { log("播放结束, 找下一集"); findNext(); }
});
video.setAttribute('hooked', 'true');
log("捕获到视频对象");
}
}, 2000);
// 自动答视频内弹题
function autoAnswerPopQuiz() {
let quizLayer = document.querySelector('#quizLayer');
if (!quizLayer || quizLayer.style.display === 'none') return;
log("⚡ 检测到弹题,正在秒杀...");
try {
let videoDiv = document.querySelector('div[uooc-video]');
if (!videoDiv) return;
let sourceData = JSON.parse(videoDiv.getAttribute('source'));
let quizList = sourceData.quiz;
let questionEl = document.querySelector('.smallTest-view .ti-q-c');
if (!questionEl) return;
let currentQuestionHTML = questionEl.innerHTML;
let targetQuiz = quizList.find(q => q.question === currentQuestionHTML);
if (targetQuiz) {
let correctAnswers = [];
try { correctAnswers = eval(targetQuiz.answer); } catch(e) { correctAnswers = targetQuiz.answer; }
if (!Array.isArray(correctAnswers)) correctAnswers = [correctAnswers];
let optionsContainer = quizLayer.querySelector('div.ti-alist');
if (optionsContainer) {
correctAnswers.forEach(ans => {
let index = ans.charCodeAt(0) - 65;
if(optionsContainer.children[index]) optionsContainer.children[index].click();
});
setTimeout(() => {
let submitBtn = quizLayer.querySelector('button');
if (submitBtn) { submitBtn.click(); log("✅ 弹题已自动提交"); }
}, 500);
}
}
} catch (err) { console.error(err); }
}
// 自动下一集
function findNext() {
let all = document.querySelectorAll('.basic');
let activeIdx = -1;
for(let i=0; inext.click(), 1500);
} else if(!isTask) {
log("展开目录..."); next.click(); setTimeout(findNext, 2000);
} else {
log("⚠️ 遇到测验/作业!");
setTimeout(() => {
next.click();
if(document.getElementById('cb-quiz-alert').checked) alertUser("遇到测验,请使用AI助手!");
}, 1500);
}
}
}
function alertUser(msg) {
try { let u = new SpeechSynthesisUtterance(msg); window.speechSynthesis.speak(u); } catch(e){}
log(msg);
}
}
// ==========================================
// 模块B:讨论区自动回复助手 (从独立脚本合并)
// ==========================================
function initDiscussionHelper() {
console.log("[UOOC全能助手] 讨论区模块监听中...");
let autoTimer = null;
let isRunning = false;
// 监听 DOM 变化,等待讨论框出现
const observer = new MutationObserver((mutations) => {
const editorArea = document.querySelector('.replay-editor');
if (editorArea && !document.getElementById('uooc-auto-reply-panel')) {
console.log("[UOOC全能助手] 检测到讨论区,注入控制面板");
addControlPanel(editorArea);
}
});
observer.observe(document.body, { childList: true, subtree: true });
function addControlPanel(targetElement) {
const panel = document.createElement('div');
panel.id = 'uooc-auto-reply-panel';
panel.style.cssText = `margin-top: 15px; padding: 15px; background: #f9f9f9; border: 1px dashed #0B99FF; border-radius: 5px; display: flex; align-items: center; gap: 10px; flex-wrap: wrap;`;
panel.innerHTML = `
`;
targetElement.appendChild(panel);
document.getElementById('auto-reply-toggle').addEventListener('click', toggleAutoReply);
}
function toggleAutoReply() {
const btn = document.getElementById('auto-reply-toggle');
const textInput = document.getElementById('auto-reply-text');
const intervalInput = document.getElementById('auto-reply-interval');
const statusDiv = document.getElementById('auto-reply-status');
if (isRunning) {
clearInterval(autoTimer);
isRunning = false;
btn.innerText = "开启刷屏";
btn.style.background = "#ccc";
textInput.disabled = false;
intervalInput.disabled = false;
statusDiv.innerText = "已停止。";
} else {
const content = textInput.value;
const interval = parseInt(intervalInput.value);
if (!content) { alert("请输入内容!"); return; }
if (interval < 3) { alert("间隔不能太短!"); return; }
isRunning = true;
btn.innerText = "停止刷屏";
btn.style.background = "#ff4d4f";
textInput.disabled = true;
intervalInput.disabled = true;
statusDiv.innerText = `运行中... 每 ${interval} 秒发送一次`;
doReplyAction(content);
autoTimer = setInterval(() => { doReplyAction(content); }, interval * 1000);
}
}
function doReplyAction(content) {
const textarea = document.querySelector('.replay-editor-area textarea');
const submitBtn = document.querySelector('.replay-editor-btn');
if (textarea && submitBtn) {
textarea.value = content;
textarea.dispatchEvent(new Event('input', { bubbles: true }));
textarea.dispatchEvent(new Event('change', { bubbles: true }));
setTimeout(() => {
submitBtn.click();
const statusDiv = document.getElementById('auto-reply-status');
if(statusDiv) statusDiv.innerText = `已发送: ${new Date().toLocaleTimeString()}`;
}, 200);
}
}
}
// ==========================================
// 模块C:章节测验 AI 助手 (Iframe)
// ==========================================
function initQuizHelper() {
console.log("[UOOC全能助手] AI测验模块启动");
let apiConfigs = GM_getValue('ai_configs_v2', [{ name: "DeepSeek", url: "https://api.deepseek.com/chat/completions", key: "", model: "deepseek-chat" }]);
const css = `
#uooc-ai-panel { position:fixed; top:20px; left:20px; width:360px; background:#fff; border:1px solid #ddd; z-index:999999; box-shadow:0 0 20px rgba(0,0,0,0.2); font-family:'Segoe UI', sans-serif; border-radius:8px; display:flex; flex-direction:column; max-height:85vh; }
#uooc-ai-header { padding:12px; background:#2980b9; color:white; font-weight:bold; cursor:move; border-radius:8px 8px 0 0; display:flex; justify-content:space-between; align-items:center; user-select:none; }
#uooc-ai-body { padding:10px; overflow-y:auto; flex:1; background:#f5f6fa; }
#uooc-ai-ball { position:fixed; top:20px; left:20px; width:45px; height:45px; background:#2980b9; border-radius:50%; z-index:99999; display:none; align-items:center; justify-content:center; cursor:move; box-shadow:0 4px 15px rgba(0,0,0,0.3); color:white; font-weight:bold; user-select:none; border:2px solid white; }
.ai-btn { width:100%; padding:10px; background:#2980b9; color:white; border:none; border-radius:4px; cursor:pointer; font-weight:bold; transition:0.2s; margin-bottom:10px; }
.btn-group { display:flex; gap:10px; margin-bottom:10px; }
.btn-group .ai-btn { margin-bottom:0; flex:1; }
#ai-results-grid { display:grid; grid-template-columns: repeat(auto-fit, minmax(100px, 1fr)); gap:8px; margin-top:10px; }
.ai-result-card { background:#fff; border-radius:6px; padding:8px; text-align:center; border:1px solid #dcdde1; box-shadow:0 2px 5px rgba(0,0,0,0.05); }
.ai-name { font-size:11px; color:#7f8c8d; margin-bottom:5px; font-weight:bold; border-bottom:1px solid #eee; padding-bottom:3px;}
.ai-ans { font-size:16px; font-weight:bold; color:#2c3e50; line-height:1.4; word-break: break-all; }
#ai-config-area { display:none; background:#fff; padding:10px; border-radius:4px; border:1px solid #ddd; margin-bottom:10px; }
.cfg-item { border-bottom:1px dashed #eee; padding-bottom:8px; margin-bottom:8px; }
.cfg-input { width:100%; padding:4px; margin:2px 0; border:1px solid #ddd; border-radius:3px; font-size:11px; box-sizing:border-box; }
.cfg-actions { display:flex; justify-content:space-between; margin-top:5px; align-items:center; }
.btn-xs { padding:2px 8px; font-size:10px; cursor:pointer; border:1px solid #ddd; background:#fff; border-radius:3px; }
.btn-del { color: red; border-color: #ffcccc; }
`;
GM_addStyle(css);
const div = document.createElement('div');
div.innerHTML = `
AI
`;
document.body.appendChild(div);
const panel = document.getElementById('uooc-ai-panel');
const ball = document.getElementById('uooc-ai-ball');
const header = document.getElementById('uooc-ai-header');
function makeDraggable(trigger, target, partner) {
let isDragging = false, startX, startY, initLeft, initTop, hasMoved = false;
trigger.addEventListener('mousedown', e => { isDragging=true; hasMoved=false; startX=e.clientX; startY=e.clientY; initLeft=target.offsetLeft; initTop=target.offsetTop; });
document.addEventListener('mousemove', e => {
if (isDragging) {
if (Math.abs(e.clientX - startX) > 3) hasMoved = true;
const l = initLeft + (e.clientX - startX) + 'px';
const t = initTop + (e.clientY - startY) + 'px';
target.style.left = l; target.style.top = t;
if(partner) { partner.style.left = l; partner.style.top = t; }
}
});
document.addEventListener('mouseup', () => isDragging=false);
return () => hasMoved;
}
makeDraggable(header, panel, ball);
const checkBallMoved = makeDraggable(ball, ball, panel);
document.getElementById('ai-min-btn').onclick = () => { panel.style.display='none'; ball.style.display='flex'; };
ball.onclick = () => { if(!checkBallMoved()) { ball.style.display='none'; panel.style.display='flex'; } };
// 配置与答题逻辑
const configArea = document.getElementById('ai-config-area');
const cfgList = document.getElementById('cfg-list');
function renderConfigs() {
cfgList.innerHTML = '';
apiConfigs.forEach((cfg, index) => {
const item = document.createElement('div');
item.className = 'cfg-item';
const headerDiv = document.createElement('div');
headerDiv.className = 'cfg-actions';
headerDiv.innerHTML = `模型 #${index + 1}`;
if (apiConfigs.length > 0) {
const delBtn = document.createElement('button');
delBtn.className = 'btn-xs btn-del'; delBtn.innerText = '删除';
delBtn.onclick = function() { if(confirm('确定删除?')) { apiConfigs.splice(index, 1); renderConfigs(); } };
headerDiv.appendChild(delBtn);
}
item.appendChild(headerDiv);
const inputsDiv = document.createElement('div');
inputsDiv.innerHTML = ``;
item.appendChild(inputsDiv);
cfgList.appendChild(item);
});
}
document.getElementById('ai-toggle-config').onclick = () => { if(configArea.style.display==='none'||!configArea.style.display){ renderConfigs(); configArea.style.display='block'; } else { configArea.style.display='none'; } };
document.getElementById('btn-add-cfg').onclick = () => { apiConfigs.push({name:"New AI",url:"",key:"",model:""}); renderConfigs(); };
document.getElementById('btn-save-cfg').onclick = () => {
apiConfigs = [];
cfgList.querySelectorAll('.cfg-item').forEach(item => { apiConfigs.push({ name: item.querySelector('.name').value, url: item.querySelector('.url').value, key: item.querySelector('.key').value, model: item.querySelector('.model').value }); });
GM_setValue('ai_configs_v2', apiConfigs); alert("保存成功"); configArea.style.display='none';
};
function extractQuestionsText() {
let textBuffer = [];
let questions = document.querySelectorAll('.queContainer');
if(questions.length === 0) return document.body.innerText.replace(/\s+/g, ' ').substring(0, 5000);
questions.forEach((q, idx) => {
let indexText = q.querySelector('.index') ? q.querySelector('.index').innerText.trim() : "";
let bodyText = q.querySelector('.ti-q-c') ? q.querySelector('.ti-q-c').innerText.trim() : "";
let optionsText = "";
q.querySelectorAll('.ti-a').forEach(opt => {
let label = opt.querySelector('.ti-a-i') ? opt.querySelector('.ti-a-i').innerText.trim() : "";
let val = opt.querySelector('.ti-a-c') ? opt.querySelector('.ti-a-c').innerText.trim() : "";
optionsText += `\n${label} ${val}`;
});
textBuffer.push(`${indexText} ${bodyText}${optionsText}`);
});
return textBuffer.join('\n\n----------------\n\n');
}
const statusDiv = document.getElementById('ai-status');
document.getElementById('btn-copy').onclick = () => {
const fullText = extractQuestionsText();
if(fullText.length < 10) { statusDiv.innerText = "提取失败(太短)"; return; }
GM_setClipboard(fullText); statusDiv.innerText = "✅ 已复制到剪贴板";
};
const resultGrid = document.getElementById('ai-results-grid');
document.getElementById('btn-run').onclick = async () => {
const validConfigs = apiConfigs.filter(c => c.key && c.url);
if (validConfigs.length === 0) { alert("请配置API"); return; }
let contentText = extractQuestionsText();
if(contentText.length < 10) { statusDiv.innerText = "❌ 没提取到题目"; return; }
statusDiv.innerText = `请求 ${validConfigs.length} 个模型中...`; resultGrid.innerHTML = '';
validConfigs.forEach((cfg, idx) => {
const card = document.createElement('div'); card.className = 'ai-result-card'; card.id = `card-${idx}`;
card.innerHTML = `${cfg.name}
...
`; resultGrid.appendChild(card);
});
const promises = validConfigs.map((cfg, idx) => callSingleAI(cfg, contentText, idx));
await Promise.allSettled(promises); statusDiv.innerText = "✅ 完成";
};
function callSingleAI(cfg, question, idx) {
return new Promise((resolve, reject) => {
const systemPrompt = `你是一个做题助手。用户发给你试题。请识别【每一道题】,并按顺序输出答案。格式要求:1. 请用逗号分隔每个答案(例如:A, B, √, C)。2. 不要输出解释。3. 只要答案。`;
GM_xmlhttpRequest({
method: "POST", url: cfg.url, headers: { "Content-Type": "application/json", "Authorization": "Bearer " + cfg.key },
data: JSON.stringify({ model: cfg.model, messages: [ { role: "system", content: systemPrompt }, { role: "user", content: "试题:\n" + question } ], temperature: 0.1 }),
onload: function(response) {
const cardAns = document.querySelector(`#card-${idx} .ai-ans`);
if (response.status === 200) { try { const resJson = JSON.parse(response.responseText); let rawAns = resJson.choices[0].message.content.trim(); rawAns = rawAns.replace(/[。.]/g, ''); cardAns.innerText = rawAns; cardAns.style.color = "#e74c3c"; resolve(); } catch (e) { cardAns.innerText = "解析错"; reject(e); } } else { cardAns.innerText = "Err " + response.status; reject("Status " + response.status); }
}, onerror: function(err) { document.querySelector(`#card-${idx} .ai-ans`).innerText = "网路错"; reject(err); }
});
});
}
}
})();