// ==UserScript== // @name 【限时免费使用题库】超星学习通学起plus视频作业助手 // @namespace http://tampermonkey.net/ // @version 0.3.6.2 // @description 【限时免费使用题库】【超星学习通】【学起plus】的自动挂机视频、章节测试、文档、直播、作业、考试;百亿题库;正确率高,限时免费使用 // @description 进群反馈QQ:923349555 // @author DevTools Helper // @match *://exam.chinaedu.net/* // @match *://mooc1-2.chaoxing.com/exam-ans/mooc2/exam/* // @match *://mooc1.chaoxing.com/mooc-ans/mooc2/work/* // @match *://mooc1-api.chaoxing.com/exam-ans/mooc2/exam* // @match *://mooc1.chaoxing.com/mycourse/studentstudy* // @match https://mooc1.chaoxing.com/mooc-ans/knowledge/* // @require https://scriptcat.org/lib/668/1.0/TyprMd5.js // @resource Table https://www.forestpolice.org/ttf/2.0/table.json // @run-at document-start // @grant GM_xmlhttpRequest // @grant GM_getResourceText // @grant unsafeWindow // @connect * // ==/UserScript== (function () { 'use strict'; const originalAddEventListener = EventTarget.prototype.addEventListener; const blockedEvents = ['visibilitychange', 'blur', 'focusout', 'mouseleave', 'beforeunload', 'pagehide']; EventTarget.prototype.addEventListener = function(type, listener, options) { if (blockedEvents.includes(type)) { return; } return originalAddEventListener.call(this, type, listener, options); }; try { Object.defineProperty(document, 'hidden', { get: () => false, configurable: true }); Object.defineProperty(document, 'visibilityState', { get: () => 'visible', configurable: true }); Object.defineProperty(document, 'webkitHidden', { get: () => false, configurable: true }); Object.defineProperty(document, 'mozHidden', { get: () => false, configurable: true }); Object.defineProperty(document, 'msHidden', { get: () => false, configurable: true }); Object.defineProperty(document, 'webkitVisibilityState', { get: () => 'visible', configurable: true }); Object.defineProperty(document, 'mozVisibilityState', { get: () => 'visible', configurable: true }); Object.defineProperty(document, 'msVisibilityState', { get: () => 'visible', configurable: true }); } catch (e) { } document.hasFocus = () => true; Object.defineProperty(HTMLMediaElement.prototype, 'muted', { get() { return false; }, set(value) { this._actualMuted = value; if (value === true) { setTimeout(() => { if (this._actualMuted === true) { this._actualMuted = false; } }, 100); } return true; } }); let videoMonitorInterval = null; let iframeMonitorInterval = null; const processedIframes = new WeakSet(); function injectHooksToDocument(doc, context = 'main') { if (!doc || doc._hooksInjected) return; try { const docWindow = doc.defaultView || doc.parentWindow; if (docWindow && docWindow.EventTarget) { const originalAdd = docWindow.EventTarget.prototype.addEventListener; docWindow.EventTarget.prototype.addEventListener = function(type, listener, options) { if (blockedEvents.includes(type)) { return; } return originalAdd.call(this, type, listener, options); }; } // === 应用防切屏设置到iframe的document === Object.defineProperty(doc, 'hidden', { get: () => false, configurable: true }); Object.defineProperty(doc, 'visibilityState', { get: () => 'visible', configurable: true }); // 支持各种浏览器前缀 Object.defineProperty(doc, 'webkitHidden', { get: () => false, configurable: true }); Object.defineProperty(doc, 'mozHidden', { get: () => false, configurable: true }); Object.defineProperty(doc, 'msHidden', { get: () => false, configurable: true }); Object.defineProperty(doc, 'webkitVisibilityState', { get: () => 'visible', configurable: true }); Object.defineProperty(doc, 'mozVisibilityState', { get: () => 'visible', configurable: true }); Object.defineProperty(doc, 'msVisibilityState', { get: () => 'visible', configurable: true }); doc.hasFocus = () => true; // === 应用防切屏设置到iframe的window对象 === if (docWindow) { // 页面可见性API Object.defineProperty(docWindow.document, 'hidden', { get: () => false, configurable: true }); Object.defineProperty(docWindow.document, 'visibilityState', { get: () => 'visible', configurable: true }); // 支持各种浏览器前缀 Object.defineProperty(docWindow.document, 'webkitHidden', { get: () => false, configurable: true }); Object.defineProperty(docWindow.document, 'mozHidden', { get: () => false, configurable: true }); Object.defineProperty(docWindow.document, 'msHidden', { get: () => false, configurable: true }); Object.defineProperty(docWindow.document, 'webkitVisibilityState', { get: () => 'visible', configurable: true }); Object.defineProperty(docWindow.document, 'mozVisibilityState', { get: () => 'visible', configurable: true }); Object.defineProperty(docWindow.document, 'msVisibilityState', { get: () => 'visible', configurable: true }); // 焦点状态 docWindow.document.hasFocus = () => true; if (docWindow.focus) { docWindow.focus = () => true; } // 视频静音状态(如果iframe内有HTMLMediaElement) if (docWindow.HTMLMediaElement) { Object.defineProperty(docWindow.HTMLMediaElement.prototype, 'muted', { get() { return false; }, set(value) { this._actualMuted = value; if (value === true) { setTimeout(() => { if (this._actualMuted === true) { this._actualMuted = false; } }, 100); } return true; } }); } // 拦截页面可见性变化事件 const blockedVisibilityEvents = ['visibilitychange', 'webkitvisibilitychange', 'mozvisibilitychange', 'msvisibilitychange']; if (docWindow.addEventListener) { const originalAddEventListener = docWindow.addEventListener; docWindow.addEventListener = function(type, listener, options) { if (blockedVisibilityEvents.includes(type.toLowerCase())) { console.log(`🛡️ [防切屏] 阻止iframe监听${type}事件`); return; // 不添加监听器 } return originalAddEventListener.call(this, type, listener, options); }; } // 拦截document的事件监听 if (docWindow.document && docWindow.document.addEventListener) { const originalDocAddEventListener = docWindow.document.addEventListener; docWindow.document.addEventListener = function(type, listener, options) { if (blockedVisibilityEvents.includes(type.toLowerCase())) { console.log(`🛡️ [防切屏] 阻止iframe document监听${type}事件`); return; // 不添加监听器 } return originalDocAddEventListener.call(this, type, listener, options); }; } console.log(`🛡️ [防切屏] iframe window对象防切屏设置已应用: ${context}`); } doc._hooksInjected = true; console.log(`🛡️ [防切屏] iframe document防切屏设置已应用: ${context}`); } catch (e) { console.warn(`🛡️ [防切屏] 应用到${context}失败:`, e.message); } } function monitorVideos(doc, context = 'main') { try { const videos = doc.querySelectorAll('video'); videos.forEach((video, index) => { if (video._videoMonitored) return; // 不设置 muted = true,因为防切屏设置会阻止静音 // video.muted = true; // 注释掉,与防切屏设置保持一致 video.volume = 0.01; // 设置极低音量 video.autoplay = true; video.addEventListener('pause', function() { if (!this.ended && this.readyState >= 2) { // 禁用旧的视频控制逻辑,改为使用handleVideoFrames系统 const shouldPlay = false; // checkIfVideoShouldPlay(this, `${context}-video-${index}`); if (shouldPlay) { setTimeout(() => { this.play().catch(e => { console.warn(`[${new Date().toLocaleTimeString()}] [${context}] 恢复播放失败:`, e); }); }, 100); } else { console.log(`[${new Date().toLocaleTimeString()}] [${context}] 视频 #${index} 等待轮到播放`); } } }); if (video.paused && !video.ended && video.readyState >= 2) { // 禁用旧的视频控制逻辑,改为使用handleVideoFrames系统 const shouldPlay = false; // checkIfVideoShouldPlay(video, `${context}-video-${index}`); if (shouldPlay) { video.play().catch(e => { console.warn(`[${new Date().toLocaleTimeString()}] [${context}] 初始播放失败:`, e); }); } } video._videoMonitored = true; }); } catch (e) { } } function processIframes(doc = document, context = 'main', depth = 0) { if (depth > 5) return; try { const iframes = doc.querySelectorAll('iframe'); iframes.forEach((iframe, index) => { if (processedIframes.has(iframe)) return; try { const iframeDoc = iframe.contentDocument || iframe.contentWindow?.document; if (!iframeDoc) return; const iframeContext = `${context}-iframe-${index}`; injectHooksToDocument(iframeDoc, iframeContext); monitorVideos(iframeDoc, iframeContext); processIframes(iframeDoc, iframeContext, depth + 1); processedIframes.add(iframe); } catch (e) { } }); } catch (e) { console.warn(`[${new Date().toLocaleTimeString()}] [iframe检测] iframe处理失败:`, e); } } function startAdvancedVideoMonitoring() { console.log(`[${new Date().toLocaleTimeString()}] [监控系统] 启动高级视频监控系统`); if (videoMonitorInterval) clearInterval(videoMonitorInterval); if (iframeMonitorInterval) clearInterval(iframeMonitorInterval); monitorVideos(document, 'main'); processIframes(document, 'main'); videoMonitorInterval = setInterval(() => { monitorVideos(document, 'main'); }, 3000); iframeMonitorInterval = setInterval(() => { processIframes(document, 'main'); }, 5000); } // 禁用旧的视频监控系统,改为使用handleVideoFrames系统 // if (document.readyState === 'loading') { // document.addEventListener('DOMContentLoaded', startAdvancedVideoMonitoring); // } else { // startAdvancedVideoMonitoring(); // } // // window.addEventListener('load', startAdvancedVideoMonitoring); const originalSetTimeout = window.setTimeout; window.setTimeout = function(callback, delay) { const callbackStr = callback.toString(); const pauseDetectionKeywords = ['pause', 'video']; const stateDetectionKeywords = ['hidden', 'muted', 'visibilityState']; if (pauseDetectionKeywords.every(keyword => callbackStr.includes(keyword)) && stateDetectionKeywords.some(keyword => callbackStr.includes(keyword))) { console.log(`[${new Date().toLocaleTimeString()}] [防暂停] 拦截学习通暂停检测 setTimeout`); return originalSetTimeout(() => {}, delay); } return originalSetTimeout.apply(this, arguments); }; const clearWindowHandlers = () => { if (window.onblur !== null) { window.onblur = null; } if (window.onfocus !== null) { window.onfocus = null; } if (window.onbeforeunload !== null) { window.onbeforeunload = null; } }; setInterval(clearWindowHandlers, 2000); clearWindowHandlers(); const oldRemove = EventTarget.prototype.removeEventListener; EventTarget.prototype.removeEventListener = function(...args) { if (args.length !== 0) { const eventType = args[0]; if (blockedEvents.includes(eventType)) { console.log(`[${new Date().toLocaleTimeString()}] [防切屏] 阻止移除 ${eventType} 监听器`); return; } } return oldRemove.call(this, ...args); }; const pageWindow = (typeof unsafeWindow !== 'undefined') ? unsafeWindow : window; const pageDocument = pageWindow.document; pageWindow._paq = []; const originalCreateElement = pageDocument.createElement; pageDocument.createElement = function (tagName) { if (tagName.toLowerCase() === 'script') { const script = originalCreateElement.call(pageDocument, tagName); const originalSrcSetter = Object.getOwnPropertyDescriptor(HTMLScriptElement.prototype, 'src').set; Object.defineProperty(script, 'src', { set: function (value) { if (value.includes('piwik.js')) { return; } originalSrcSetter.call(this, value); }, configurable: true }); return script; } return originalCreateElement.call(pageDocument, tagName); }; pageDocument.onkeydown = null; pageDocument.addEventListener('keydown', function (e) { if (e.keyCode === 123 || (e.ctrlKey && e.shiftKey && e.keyCode === 73) || (e.shiftKey && e.keyCode === 121)) { e.stopImmediatePropagation(); return; } }, true); pageWindow.oncontextmenu = null; pageWindow.addEventListener('contextmenu', function (e) { e.stopImmediatePropagation(); }, true); const originalSetInterval = pageWindow.setInterval; pageWindow.setInterval = function (callback, interval) { const callbackStr = callback.toString(); const detectionKeywords = [ 'window.outerWidth', 'window.outerHeight', 'detectZoom', 'getBrowserType', '请勿打开控制台' ]; const pauseDetectionKeywords = ['pause', 'video']; const stateDetectionKeywords = ['hidden', 'muted', 'visibilityState']; if (detectionKeywords.some(keyword => callbackStr.includes(keyword))) { return -1; } if (pauseDetectionKeywords.every(keyword => callbackStr.includes(keyword)) && stateDetectionKeywords.some(keyword => callbackStr.includes(keyword))) { console.log(`[${new Date().toLocaleTimeString()}] [防暂停] 拦截学习通暂停检测 setInterval`); return originalSetInterval(() => {}, interval); } return originalSetInterval.apply(this, arguments); }; pageWindow.alert = function (message) { if (message && message.includes("请勿打开控制台")) { return; } }; pageWindow.close = function () { }; Object.defineProperty(pageWindow, 'console', { value: pageWindow.console, writable: false, configurable: false }); const SERVER_CONFIG = { apiUrl: 'https://www.toptk.xyz/api', answerApiUrl: 'https://www.toptk.xyz/api', timeout: 30000 }; const GLOBAL_STATE = { isAnswering: false, isChapterTesting: false, lastAnswerTime: 0, currentPlayingVideoIndex: null // 当前播放的视频索引 }; const SITES = { XUEQI: { name: '学起', host: 'exam.chinaedu.net', getQuestions: getXueqiQuestions, selectAnswer: selectXueqiAnswer, }, CHAOXING: { name: '超星学习通', host: 'mooc1.chaoxing.com', getQuestions: getChaoxingQuestions, selectAnswer: selectChaoxingAnswer, }, CHAOXING_STUDY: { name: '超星学习通-课程学习', host: 'mooc1.chaoxing.com', path: '/mycourse/studentstudy', autoStudy: true, getQuestions: () => [], selectAnswer: () => false, } }; let currentSite = null; function detectSite() { const currentHost = window.location.hostname; const currentUrl = window.location.href; if (currentUrl.includes('/mycourse/studentstudy')) { currentSite = SITES.CHAOXING_STUDY; logMessage(`[站点检测] 检测到学习页面: ${currentSite.name}`, 'success'); return currentSite; } for (const key in SITES) { if (SITES[key].autoStudy) continue; if (currentHost.includes(SITES[key].host)) { currentSite = SITES[key]; logMessage(`[站点检测] 已识别: ${currentSite.name}`, 'success'); return currentSite; } } if (currentHost.includes('chaoxing') && !currentUrl.includes('/exam-ans/') && !currentUrl.includes('/mooc-ans/')) { const hasJobIcon = document.querySelector('.ans-job-icon'); const hasNextButton = document.querySelector('#prevNextFocusNext'); const hasCourseMain = document.querySelector('.course_main'); const hasVideoFrame = document.querySelector('iframe.ans-attach-online'); if (hasJobIcon || hasNextButton || hasCourseMain || hasVideoFrame) { currentSite = SITES.CHAOXING_STUDY; logMessage(`[站点检测] 通过DOM检测到学习页面: ${currentSite.name}`, 'success'); return currentSite; } } if (currentHost.includes('chaoxing') || currentUrl.includes('chaoxing') || document.querySelector('.questionLi') || document.querySelector('.mark_name') || document.querySelector('[typename]') || document.querySelector('.workTextWrap') || document.title.includes('超星') || document.title.includes('学习通') || currentUrl.includes('work') || currentUrl.includes('exam')) { currentSite = SITES.CHAOXING; let pageType = '未知'; if (currentUrl.includes('/exam-ans/mooc2/exam/')) { pageType = '考试'; currentSite.pageType = 'exam'; } else if (currentUrl.includes('/mooc-ans/mooc2/work/')) { pageType = '作业'; currentSite.pageType = 'homework'; } else if (currentUrl.includes('/mooc-ans/work/doHomeWorkNew/')) { pageType = '章节测验'; currentSite.pageType = 'chapter_test'; } else if (currentUrl.includes('/mooc-ans/api/work/')) { pageType = '章节测验'; currentSite.pageType = 'chapter_test'; } else if (currentUrl.includes('/ananas/modules/work/')) { pageType = '章节测验'; currentSite.pageType = 'chapter_test'; } else { const isHomeworkPage = document.querySelector('.questionLi[typename]') !== null; pageType = isHomeworkPage ? '作业' : '考试'; currentSite.pageType = isHomeworkPage ? 'homework' : 'exam'; } logMessage(`[站点检测] 通过特征识别: ${currentSite.name} - ${pageType}页面`, 'success'); return currentSite; } if (currentHost.includes('chinaedu') || currentUrl.includes('exam') || document.querySelector('.questionItem') || document.querySelector('.queStemC') || document.title.includes('学起') || document.title.includes('考试')) { currentSite = SITES.XUEQI; logMessage(`[站点检测] 通过特征识别: ${currentSite.name}`, 'success'); return currentSite; } const hasQuestionElements = document.querySelector('[class*="question"], [id*="question"], .exam, .test, .quiz'); if (hasQuestionElements) { currentSite = SITES.CHAOXING; logMessage(`[站点检测] 通用题目页面,使用: ${currentSite.name}`, 'warning'); return currentSite; } currentSite = SITES.XUEQI; logMessage(`[站点检测] 未识别当前站点, 使用默认解析器: ${currentSite.name}`, 'warning'); return currentSite; } function gmFetch(url, options = {}) { return new Promise((resolve, reject) => { const { method = 'GET', headers = {}, body = null, timeout = SERVER_CONFIG.timeout } = options; GM_xmlhttpRequest({ method: method, url: url, headers: headers, data: body, timeout: timeout, onload: function (response) { const result = { ok: response.status >= 200 && response.status < 300, status: response.status, statusText: response.statusText, json: () => { try { return Promise.resolve(JSON.parse(response.responseText)); } catch (error) { return Promise.reject(new Error(`Invalid JSON response: ${error.message}`)); } }, text: () => Promise.resolve(response.responseText) }; resolve(result); }, onerror: function (error) { reject(new Error(`Request failed: ${error.error || 'Network error'}`)); }, ontimeout: function () { reject(new Error('Request timeout')); } }); }); } const TokenManager = { TOKEN_KEY: 'user_token', _requestCache: new Map(), _lastRequestTime: 0, _minRequestInterval: 500, async _throttleRequest(key, requestFn, cacheTime = 30000) { const now = Date.now(); if (this._requestCache.has(key)) { const cached = this._requestCache.get(key); if (now - cached.timestamp < cacheTime) { return cached.result; } } let actualInterval = this._minRequestInterval; if (key.includes('validate') || key.includes('verify')) { actualInterval = 200; } else if (key.includes('check') || key.includes('status')) { actualInterval = 100; } const timeSinceLastRequest = now - this._lastRequestTime; if (timeSinceLastRequest < actualInterval) { const waitTime = actualInterval - timeSinceLastRequest; await new Promise(resolve => setTimeout(resolve, waitTime)); } this._lastRequestTime = Date.now(); try { const result = await requestFn(); this._requestCache.set(key, { result: result, timestamp: Date.now() }); return result; } catch (error) { if (!error.message.includes('429') && !error.message.includes('网络')) { this._requestCache.delete(key); } throw error; } }, getToken() { return localStorage.getItem(this.TOKEN_KEY); }, setToken(token) { localStorage.setItem(this.TOKEN_KEY, token); }, clearToken() { localStorage.removeItem(this.TOKEN_KEY); this._requestCache.clear(); }, async checkVisitorStatus() { const cacheKey = 'check_visitor_status'; return await this._throttleRequest(cacheKey, async () => { const response = await gmFetch(`${SERVER_CONFIG.apiUrl}/user/check-visitor`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, timeout: SERVER_CONFIG.timeout }); if (!response.ok) { throw new Error(`检测访问者状态失败: ${response.status} ${response.statusText}`); } const result = await response.json(); if (result.success) { if (result.data.isNewUser && result.data.userToken) { this.setToken(result.data.userToken); return { success: true, hasToken: true, token: result.data.userToken, userInfo: result.data.userInfo, message: result.data.message }; } if (!result.data.isNewUser && result.data.userToken) { this.setToken(result.data.userToken); return { success: true, hasToken: true, token: result.data.userToken, userInfo: result.data.userInfo, message: result.data.message }; } } if (result.data && result.data.needsToken) { return { success: false, needsToken: true, message: result.data.message || '请输入您的用户Token' }; } throw new Error(result.message || '检测访问者状态失败'); }, 120000); }, async verifyUserToken(userToken) { const cacheKey = `verify_${userToken.substring(0, 16)}`; return await this._throttleRequest(cacheKey, async () => { const response = await gmFetch(`${SERVER_CONFIG.apiUrl}/user/verify-token`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ userToken: userToken }), timeout: SERVER_CONFIG.timeout }); if (!response.ok) { let errorMessage = `Token验证失败: ${response.status} ${response.statusText}`; try { const result = await response.json(); errorMessage = result.message || errorMessage; } catch (parseError) { try { const errorText = await response.text(); errorMessage = errorText || errorMessage; } catch (textError) { } } throw new Error(errorMessage); } const result = await response.json(); if (!result.success) { const errorMessage = result.message || 'Token验证失败'; throw new Error(errorMessage); } this.setToken(result.data.userToken); return { success: true, token: result.data.userToken, userInfo: result.data.userInfo, message: result.data.message }; }, 300000); }, async promptUserToken() { try { const userToken = prompt( '🔐 请输入您的用户Token\n\n' + '如果您是首次使用,系统会在首次访问时自动为您创建Token。\n' + '如果您已有Token,请输入完整的64位Token字符串:' ); if (!userToken) { throw new Error('用户取消输入Token'); } if (userToken.length !== 64) { throw new Error('Token格式不正确,应为64位字符串'); } return await this.verifyUserToken(userToken); } catch (error) { throw error; } }, async _validateToken(token) { const cacheKey = `validate_${token.substring(0, 16)}`; return await this._throttleRequest(cacheKey, async () => { const response = await gmFetch(`${SERVER_CONFIG.apiUrl}/user/info`, { method: 'GET', headers: { 'Content-Type': 'application/json', 'X-User-Token': token }, timeout: SERVER_CONFIG.timeout }); if (!response.ok) { return false; } const result = await response.json(); return result.success; }, 60000); }, async getValidToken() { let token = this.getToken(); if (token) { const isValid = await this._validateToken(token); if (isValid) { return token; } this.clearToken(); } try { const result = await this.initialize(); if (result.success && result.hasToken) { return this.getToken(); } } catch (error) { } throw new Error('Token获取失败,请刷新页面重试'); }, async getUserInfo() { try { const token = await this.getValidToken(); const response = await gmFetch(`${SERVER_CONFIG.apiUrl}/user/info`, { method: 'GET', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, timeout: SERVER_CONFIG.timeout }); if (!response.ok) { throw new Error(`获取用户信息失败: ${response.status} ${response.statusText}`); } const result = await response.json(); if (!result.success) { throw new Error(result.message || '获取用户信息失败'); } return result.userInfo; } catch (error) { throw error; } }, async initialize() { try { const storedToken = this.getToken(); if (storedToken) { const isValid = await this._validateToken(storedToken); if (isValid) { return { success: true, hasToken: true, token: storedToken, message: '欢迎回来!Token已自动加载。' }; } else { this.clearToken(); } } return await this.showTokenSelectionDialog(); } catch (error) { return { success: false, error: error.message, message: '初始化失败,请刷新页面重试' }; } }, async showTokenSelectionDialog() { return new Promise((resolve) => { const dialogHTML = `

🎯 TK星球答题系统

请选择您的Token获取方式:

`; document.body.insertAdjacentHTML('beforeend', dialogHTML); const dialog = document.getElementById('token-dialog'); const createBtn = document.getElementById('create-new-token'); const inputBtn = document.getElementById('input-existing-token'); const inputArea = document.getElementById('token-input-area'); const tokenInput = document.getElementById('token-input'); const verifyBtn = document.getElementById('verify-token'); const cancelBtn = document.getElementById('cancel-input'); const messageDiv = document.getElementById('dialog-message'); const showMessage = (message, type = 'info') => { messageDiv.style.display = 'block'; messageDiv.textContent = message; if (type === 'error') { messageDiv.style.background = '#f8d7da'; messageDiv.style.color = '#721c24'; messageDiv.style.border = '1px solid #f5c6cb'; } else if (type === 'success') { messageDiv.style.background = '#d4edda'; messageDiv.style.color = '#155724'; messageDiv.style.border = '1px solid #c3e6cb'; } else { messageDiv.style.background = '#d1ecf1'; messageDiv.style.color = '#0c5460'; messageDiv.style.border = '1px solid #bee5eb'; } }; const closeDialog = () => { dialog.remove(); }; createBtn.addEventListener('click', async () => { try { createBtn.disabled = true; createBtn.textContent = '生成中...'; showMessage('正在生成新Token...', 'info'); const result = await this.checkVisitorStatus(); if (result.success && result.hasToken) { showMessage(`Token生成成功!`, 'success'); const tokenDisplay = document.createElement('div'); tokenDisplay.style.cssText = ` background: #f8f9fa; border: 2px solid #28a745; border-radius: 5px; padding: 10px; margin: 10px 0; font-family: monospace; font-size: 12px; word-break: break-all; cursor: pointer; text-align: center; `; tokenDisplay.textContent = result.token; tokenDisplay.title = '点击复制Token'; tokenDisplay.addEventListener('click', async () => { try { await navigator.clipboard.writeText(result.token); showMessage('Token已复制到剪贴板!', 'success'); } catch (err) { const textArea = document.createElement('textarea'); textArea.value = result.token; document.body.appendChild(textArea); textArea.select(); document.execCommand('copy'); document.body.removeChild(textArea); showMessage('Token已复制到剪贴板!', 'success'); } }); const rechargeNotice = document.createElement('div'); rechargeNotice.style.cssText = ` background: #fff3cd; border: 1px solid #ffeaa7; border-radius: 5px; padding: 10px; margin: 10px 0; font-size: 14px; color: #856404; text-align: center; `; rechargeNotice.innerHTML = ` ⚠️ 重要提示
新用户默认0次查询机会,请充值后使用答题功能。
请妥善保存您的Token,它是您的唯一凭证! `; messageDiv.parentNode.insertBefore(tokenDisplay, messageDiv.nextSibling); messageDiv.parentNode.insertBefore(rechargeNotice, tokenDisplay.nextSibling); createBtn.textContent = '开始答题'; createBtn.onclick = () => { closeDialog(); resolve({ success: true, hasToken: true, token: result.token, message: '新Token已生成,您可以开始答题了!' }); }; } else { throw new Error(result.message || 'Token生成失败'); } } catch (error) { showMessage('生成Token失败: ' + error.message, 'error'); createBtn.disabled = false; createBtn.textContent = '🆕 生成新Token'; } }); inputBtn.addEventListener('click', () => { inputArea.style.display = 'block'; tokenInput.focus(); }); verifyBtn.addEventListener('click', async () => { const token = tokenInput.value.trim(); if (!token) { showMessage('请输入Token', 'error'); return; } try { verifyBtn.disabled = true; verifyBtn.textContent = '验证中...'; showMessage('正在验证Token...', 'info'); const result = await this.verifyUserToken(token); if (result.success) { showMessage('Token验证成功!', 'success'); setTimeout(() => { closeDialog(); resolve({ success: true, hasToken: true, token: result.token, message: 'Token验证成功,您可以开始答题了!' }); }, 1500); } else { throw new Error(result.message || 'Token验证失败'); } } catch (error) { showMessage('Token验证失败: ' + error.message, 'error'); verifyBtn.disabled = false; verifyBtn.textContent = '验证Token'; } }); cancelBtn.addEventListener('click', () => { inputArea.style.display = 'none'; tokenInput.value = ''; messageDiv.style.display = 'none'; }); tokenInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { verifyBtn.click(); } }); }); } }; function handleSubmitConfirmDialog() { const documents = [ document, window.parent?.document, window.top?.document ].filter(doc => doc); let confirmDialog = null; let foundInDocument = null; for (const doc of documents) { try { confirmDialog = doc.querySelector('.popBottom'); if (confirmDialog) { foundInDocument = doc; break; } } catch (error) { } } if (!confirmDialog) { return false; } const targetDoc = foundInDocument || document; const popContent = targetDoc.querySelector('#popcontent'); if (!popContent || !popContent.textContent.includes('确认提交')) { return false; } const submitBtn = targetDoc.querySelector('#popok'); if (!submitBtn) { return false; } try { submitBtn.click(); console.log('✅ [提交确认] 已点击提交按钮'); GLOBAL_STATE.lastAnswerTime = Date.now(); GLOBAL_STATE.isAnswering = false; GLOBAL_STATE.isChapterTesting = false; console.log('📝 [提交确认] 章节测验已完成,状态已重置'); return true; } catch (error) { console.warn('❌ [提交确认] 点击提交按钮失败:', error); return false; } } function monitorSubmitDialog() { let checkCount = 0; const maxChecks = 10; const checkInterval = setInterval(() => { checkCount++; const dialogHandled = handleSubmitConfirmDialog(); if (dialogHandled || checkCount >= maxChecks) { clearInterval(checkInterval); } }, 1000); } function getXueqiQuestions() { try { const questions = []; const examTypeElement = document.querySelector('.test-part .f18.c_2d4.fb'); const examType = examTypeElement ? examTypeElement.textContent.trim() : '未知题型'; const examInfoElement = document.querySelector('.test-part .c_dan'); const examInfo = examInfoElement ? examInfoElement.textContent.trim() : ''; const questionElements = document.querySelectorAll('.questionItem'); questionElements.forEach((questionEl, index) => { const questionData = { type: examType, questionType: '', number: index + 1, stem: '', options: [], score: '' }; if (questionEl.classList.contains('singItem') || questionEl.querySelector('.singItem')) { questionData.questionType = '单选题'; } else if (questionEl.classList.contains('judge') || questionEl.querySelector('.judge')) { questionData.questionType = '判断题'; } else if (questionEl.classList.contains('Mutli') || questionEl.querySelector('.Mutli')) { questionData.questionType = '多选题'; } else { questionData.questionType = '未知题型'; } const stemElement = questionEl.querySelector('.queStemC'); if (stemElement) { const numberEl = stemElement.querySelector('.din.fb.mr10'); if (numberEl) { questionData.number = numberEl.textContent.trim(); } const contentEls = stemElement.querySelectorAll('.din'); if (contentEls.length > 1) { const contentEl = contentEls[1]; if (contentEl) { const tableSpan = contentEl.querySelector('table span'); if (tableSpan) { questionData.stem = tableSpan.textContent.trim(); } else { const pEl = contentEl.querySelector('p'); if (pEl) { questionData.stem = pEl.textContent.trim(); } else { const textContent = contentEl.textContent.trim(); questionData.stem = textContent.replace(/\s+/g, ' ').trim(); } } } } if (!questionData.stem) { const allText = stemElement.textContent.trim(); const cleanText = allText.replace(/^\d+\.\s*/, '').replace(/(\d+分)$/, '').trim(); if (cleanText) { questionData.stem = cleanText; } } const scoreEl = stemElement.querySelector('.f13.c_dan var'); if (scoreEl) { questionData.score = scoreEl.textContent.trim() + '分'; } } if (questionData.questionType === '判断题') { const judgeButtons = questionEl.querySelectorAll('.JudgeBtn input'); judgeButtons.forEach((btn, idx) => { const value = btn.value.trim(); questionData.options.push({ label: idx === 0 ? 'T' : 'F', content: value, element: btn }); }); } else if (questionData.questionType === '多选题') { const optionElements = questionEl.querySelectorAll('dd.clearfix'); optionElements.forEach(optionEl => { const optionLabel = optionEl.querySelector('.duplexCheck'); const optionContent = optionEl.querySelector('div'); if (optionLabel && optionContent) { questionData.options.push({ label: optionLabel.textContent.trim(), content: optionContent.textContent.trim(), element: optionEl }); } }); } else { const optionElements = questionEl.querySelectorAll('dd.clearfix'); optionElements.forEach(optionEl => { const optionLabel = optionEl.querySelector('.singleCheck'); const optionContent = optionEl.querySelector('div'); if (optionLabel && optionContent) { questionData.options.push({ label: optionLabel.textContent.trim(), content: optionContent.textContent.trim(), element: optionEl }); } }); } questions.push(questionData); }); return questions; } catch (error) { return []; } } function getChaoxingQuestions() { try { logMessage('🔍 [超星] 开始解析题目...', 'info'); const questions = []; const currentUrl = window.location.href; let pageType = '未知'; let isExamPage = false; let isHomeworkPage = false; let isChapterTestPage = false; if (currentUrl.includes('/exam-ans/mooc2/exam/')) { pageType = '考试'; isExamPage = true; } else if (currentUrl.includes('/mooc-ans/mooc2/work/')) { pageType = '作业'; isHomeworkPage = true; } else if (currentUrl.includes('/mooc-ans/work/doHomeWorkNew/') || currentUrl.includes('/mooc-ans/api/work/') || currentUrl.includes('/ananas/modules/work/')) { pageType = '章节测验'; isChapterTestPage = true; } else { const hasTypenameAttr = document.querySelector('.questionLi[typename]') !== null; if (hasTypenameAttr) { pageType = '作业'; isHomeworkPage = true; } else { pageType = '考试'; isExamPage = true; } } const questionElements = document.querySelectorAll('.questionLi'); if (questionElements.length === 0) { logMessage('⚠️ [超星] 未找到题目元素 (.questionLi),请确认页面结构。', 'warning'); return []; } logMessage(`[超星] 发现 ${questionElements.length} 个题目容器 (${pageType}页面)。`, 'info'); questionElements.forEach((questionEl, index) => { try { const questionData = { type: '超星学习通', questionType: '未知题型', number: index + 1, stem: '', options: [], score: '', questionId: '' }; let questionId = questionEl.getAttribute('data') || questionEl.id?.replace('sigleQuestionDiv_', '') || ''; if (!questionId) { const optionWithQid = questionEl.querySelector('[qid]'); if (optionWithQid) { questionId = optionWithQid.getAttribute('qid'); } } questionData.questionId = questionId; if (isHomeworkPage) { const typeNameAttr = questionEl.getAttribute('typename'); if (typeNameAttr) { questionData.questionType = typeNameAttr; console.log(`[作业页面] 从typename属性获取题型: ${typeNameAttr}`); } } else if (isExamPage) { const markNameEl = questionEl.querySelector('h3.mark_name'); if (markNameEl) { const typeScoreSpan = markNameEl.querySelector('span.colorShallow'); if (typeScoreSpan) { const typeScoreText = typeScoreSpan.textContent.trim(); const match = typeScoreText.match(/\(([^,)]+)(?:,\s*([^)]+))?\)/); if (match) { questionData.questionType = match[1].trim(); if (match[2]) { questionData.score = match[2].trim(); } console.log(`[考试页面] 从span.colorShallow获取题型: ${questionData.questionType}`); } } } } const markNameEl = questionEl.querySelector('h3.mark_name'); if (markNameEl) { const titleText = markNameEl.childNodes[0]?.textContent?.trim() || ''; const numberMatch = titleText.match(/^(\d+)\./); if (numberMatch) { questionData.number = numberMatch[1]; } let stemText = ''; if (isExamPage) { const stemDiv = markNameEl.querySelector('div[style*="overflow:hidden"]'); if (stemDiv) { stemText = stemDiv.textContent.trim(); } else { const fullText = markNameEl.textContent.trim(); stemText = fullText.replace(/^\d+\.\s*/, ''); const typePattern = /^\s*\((单选题|多选题|判断题|填空题)(?:,\s*[^)]+)?\)\s*/; stemText = stemText.replace(typePattern, '').trim(); } } else if (isHomeworkPage) { const fullText = markNameEl.textContent.trim(); stemText = fullText.replace(/^\d+\.\s*/, ''); const typePattern = /^\s*\((单选题|多选题|判断题|填空题)(?:,\s*[^)]+)?\)\s*/; stemText = stemText.replace(typePattern, '').trim(); } questionData.stem = stemText; if (!questionData.questionType || questionData.questionType === '未知题型') { if (questionData.stem && ( questionData.stem.includes('____') || questionData.stem.includes('()') || questionData.stem.includes('()') || questionData.stem.includes('_____') || questionData.stem.includes('填空') || questionData.stem.includes('空白') )) { questionData.questionType = '填空题'; } else { questionData.questionType = '单选题'; } } } const answerContainer = questionEl.querySelector('.stem_answer'); const hasInputElements = questionEl.querySelectorAll('input[type="text"], textarea').length > 0; const hasBlankItems = questionEl.querySelectorAll('.blankItemDiv').length > 0; const hasUEditor = questionEl.querySelectorAll('textarea[name*="answerEditor"]').length > 0; const hasTiankongSize = questionEl.querySelector('input[name*="tiankongsize"]'); const typeElement = questionEl.querySelector('.colorShallow'); const typeText = typeElement ? typeElement.textContent : ''; const isBlankQuestionByType = typeText.includes('填空题') || typeText.includes('【填空题】'); if ((hasInputElements || hasBlankItems || hasUEditor || hasTiankongSize || isBlankQuestionByType) && questionData.questionType !== '填空题') { questionData.questionType = '填空题'; } if (questionData.questionType !== '填空题') { if (answerContainer) { const optionElements = answerContainer.querySelectorAll('.clearfix.answerBg'); optionElements.forEach(optionEl => { const labelSpan = optionEl.querySelector('.num_option, .num_option_dx'); const contentDiv = optionEl.querySelector('.answer_p'); if (labelSpan && contentDiv) { let label = labelSpan.textContent.trim(); let content = ''; const pElement = contentDiv.querySelector('p'); if (pElement) { content = pElement.textContent.trim(); if (questionData.questionType === '判断题') { if (content === '对') { label = 'T'; content = '正确'; } else if (content === '错') { label = 'F'; content = '错误'; } } } else { content = contentDiv.textContent.trim(); } questionData.options.push({ label: label, content: content, element: optionEl, dataValue: labelSpan.getAttribute('data') || label, qid: labelSpan.getAttribute('qid') || questionData.questionId, isMultipleChoice: questionData.questionType === '多选题' }); } }); } } else { const stemAnswerEl = questionEl.querySelector('.stem_answer'); if (stemAnswerEl) { const answerContainers = stemAnswerEl.querySelectorAll('.Answer'); answerContainers.forEach((answerContainer, index) => { const textareaEl = answerContainer.querySelector('textarea[name*="answerEditor"]'); const iframe = answerContainer.querySelector('iframe'); const ueditorContainer = answerContainer.querySelector('.edui-editor'); if (textareaEl) { const editorId = textareaEl.id || textareaEl.name; let ueditorInstanceName = null; if (iframe && iframe.src) { const match = iframe.src.match(/ueditorInstant(\d+)/); if (match) { ueditorInstanceName = `ueditorInstant${match[1]}`; } } let iframeBody = null; try { if (iframe && iframe.contentDocument && iframe.contentDocument.body) { iframeBody = iframe.contentDocument.body; } } catch (e) { } questionData.options.push({ label: `填空${index + 1}`, content: '', element: textareaEl, dataValue: '', isFillInBlank: true, inputType: 'ueditor', editorId: editorId, ueditorContainer: ueditorContainer, iframe: iframe, iframeBody: iframeBody, ueditorInstanceName: ueditorInstanceName, answerContainer: answerContainer }); } else { const inputEl = answerContainer.querySelector('input[type="text"], textarea'); if (inputEl) { questionData.options.push({ label: `填空${index + 1}`, content: '', element: inputEl, dataValue: '', isFillInBlank: true, inputType: 'normal', answerContainer: answerContainer }); } } }); if (answerContainers.length === 0) { const inputElements = stemAnswerEl.querySelectorAll('input[type="text"], textarea'); inputElements.forEach((inputEl, index) => { questionData.options.push({ label: `填空${index + 1}`, content: '', element: inputEl, dataValue: '', isFillInBlank: true, inputType: 'normal' }); }); } } if (questionData.options.length === 0) { questionData.options.push({ label: '填空1', content: '', element: null, dataValue: '', isFillInBlank: true, isVirtual: true }); } } if (questionData.stem && (questionData.options.length > 0 || questionData.questionType === '填空题')) { questions.push(questionData); } else { logMessage(`⚠️ [超星] 第 ${index + 1} 题数据不完整,跳过`, 'warning'); } } catch (e) { logMessage(`❌ [超星] 解析第 ${index + 1} 题时出错: ${e.message}`, 'error'); } }); if (questions.length > 0) { logMessage(`✅ [超星] 成功解析 ${questions.length} 道题目`, 'success'); const typeCount = {}; questions.forEach(q => { typeCount[q.questionType] = (typeCount[q.questionType] || 0) + 1; }); const typeStats = Object.entries(typeCount) .map(([type, count]) => `${type}:${count}`) .join(' '); logMessage(`📊 [超星] 题型分布: ${typeStats}`, 'info'); } else { logMessage('❌ [超星] 未能解析到任何题目', 'error'); } return questions; } catch (error) { logMessage(`❌ [超星] 题目解析失败: ${error.message}`, 'error'); return []; } } function getExamQuestions() { try { if (!currentSite) { detectSite(); } const selectors = [ '.questionLi', '.questionItem', '.question-item', '.exam-question', '[class*="question"]' ]; let foundElements = []; let usedSelector = ''; for (const selector of selectors) { const elements = document.querySelectorAll(selector); if (elements.length > 0) { foundElements = elements; usedSelector = selector; break; } } if (foundElements.length === 0) { return []; } console.log(`✅ [调试] 使用选择器 "${usedSelector}" 找到 ${foundElements.length} 个题目元素`); if (currentSite && typeof currentSite.getQuestions === 'function') { console.log('🔍 [调试] 调用站点专用解析函数:', currentSite.name); const questions = currentSite.getQuestions(); console.log('🔍 [调试] 解析结果:', questions.length, '道题目'); return questions; } else { console.warn('⚠️ [调试] 站点解析函数不存在,尝试通用解析'); return tryGenericParsing(foundElements, usedSelector); } } catch (error) { console.error('❌ [调试] 获取题目时出错:', error); return []; } } function tryGenericParsing(elements, selector) { const questions = []; elements.forEach((el, index) => { try { const questionData = { type: '通用解析', questionType: '未知题型', number: index + 1, stem: '', options: [], score: '' }; const textContent = el.textContent.trim(); if (textContent.length > 10) { questionData.stem = textContent.substring(0, 200) + (textContent.length > 200 ? '...' : ''); if (textContent.includes('单选') || textContent.includes('Single')) { questionData.questionType = '单选题'; } else if (textContent.includes('多选') || textContent.includes('Multiple')) { questionData.questionType = '多选题'; } else if (textContent.includes('判断') || textContent.includes('True') || textContent.includes('False')) { questionData.questionType = '判断题'; } questions.push(questionData); console.log(`🔍 [调试] 通用解析题目 ${index + 1}:`, questionData.questionType, questionData.stem.substring(0, 50) + '...'); } } catch (error) { console.warn(`⚠️ [调试] 通用解析第 ${index + 1} 题失败:`, error.message); } }); console.log(`✅ [调试] 通用解析完成,共 ${questions.length} 道题目`); return questions; } async function callCloudAPI(questionData) { try { const token = await TokenManager.getValidToken(); const requestData = { question: questionData.stem, questionType: questionData.questionType, options: questionData.options.map(opt => ({ label: opt.label, content: opt.content })) }; if (questionData.questionType === '填空题') { requestData.options = []; requestData.fillInBlank = true; } const response = await gmFetch(`${SERVER_CONFIG.answerApiUrl}/answer`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-User-Token': token }, body: JSON.stringify(requestData), timeout: SERVER_CONFIG.timeout }); if (response.status === 429) { logMessage('⚠️ 请求过于频繁,等待10秒后继续...', 'warning'); await new Promise(resolve => { let remaining = 10; const countdownInterval = setInterval(() => { if (remaining <= 0) { clearInterval(countdownInterval); resolve(); return; } const statusElement = document.getElementById('question-count'); if (statusElement) { statusElement.textContent = `⏳ 限流等待中... ${remaining}s`; } remaining--; }, 1000); }); throw new Error('429_RATE_LIMIT_HANDLED'); } if (!response.ok) { if (response.status === 401) { TokenManager.clearToken(); throw new Error('Token无效或已过期,请刷新页面重新获取Token'); } let errorText = `API请求失败: ${response.status} ${response.statusText}`; try { const errorResponse = await response.json(); if (errorResponse.message) { errorText = `API请求失败: ${errorResponse.message}`; } } catch (jsonError) { try { const textResponse = await response.text(); if (textResponse) { errorText = `API请求失败: ${response.status} ${response.statusText} - ${textResponse.substring(0, 200)}`; } } catch (textError) { } } throw new Error(errorText); } const result = await response.json(); if (!result.success) { throw new Error(result.message || '云端分析失败'); } if (result.quota) { logMessage(result.quota.message, 'info'); } result.data.cached = result.cached || false; result.data.responseTime = result.data.responseTime || 0; return result.data; } catch (error) { console.error('❌ [客户端API] 调用失败:', error.message); if (error.message.includes('Token') || error.message.includes('token')) { logMessage('❌ Token验证失败,请刷新页面重新获取Token', 'error'); throw new Error('Token验证失败,请刷新页面重新获取Token'); } throw error; } } async function autoAnswerAllQuestions(delay = 1000) { try { if (GLOBAL_STATE.isAnswering || GLOBAL_STATE.isChapterTesting) { logMessage('⏸️ 已有答题任务在进行中,请稍后再试', 'warning'); return []; } GLOBAL_STATE.isAnswering = true; const questions = getExamQuestions(); if (!questions || !Array.isArray(questions) || questions.length === 0) { logMessage('❌ 批量答题失败: 未找到题目', 'error'); return []; } logMessage(`🚀 开始自动答题,共 ${questions.length} 道题目`, 'info'); const results = []; for (let i = 0; i < questions.length; i++) { const statusElement = document.getElementById('question-count'); if (statusElement) { statusElement.textContent = `📝 答题进度: ${i + 1}/${questions.length} (${Math.round((i + 1) / questions.length * 100)}%)`; } try { const result = await answerSingleQuestion(i); if (result) { results.push(result); } else { logMessage(`❌ 第 ${i + 1} 题答题失败`, 'error'); } } catch (error) { logMessage(`❌ 第 ${i + 1} 题出错: ${error.message}`, 'error'); } if (i < questions.length - 1) { await new Promise(resolve => { let remaining = Math.ceil(delay / 1000); const countdownInterval = setInterval(() => { if (remaining <= 0) { clearInterval(countdownInterval); resolve(); return; } if (statusElement) { statusElement.textContent = `⏳ 等待中... ${remaining}s (第${i + 2}题准备中)`; } remaining--; }, 1000); }); } } setTimeout(() => { updateQuestionCount(); }, 1000); if (results.length === questions.length) { logMessage(`🎉 自动答题完成!全部成功 (${results.length}/${questions.length})`, 'success'); } else { logMessage(`⚠️ 自动答题完成,成功: ${results.length}/${questions.length}`, 'warning'); } return results; } catch (error) { logMessage(`❌ 批量答题失败: ${error.message}`, 'error'); return []; } finally { GLOBAL_STATE.isAnswering = false; GLOBAL_STATE.lastAnswerTime = Date.now(); } } async function fillAnswers(questions, answers) { if (!questions || questions.length === 0) { logMessage('❌ 没有题目需要填写答案', 'error'); return { success: false, message: '没有题目需要填写答案' }; } if (!answers || answers.length === 0) { logMessage('❌ 没有获取到答案', 'error'); return { success: false, message: '没有获取到答案' }; } let successCount = 0; const results = []; for (let i = 0; i < Math.min(questions.length, answers.length); i++) { const question = questions[i]; const answer = answers[i]; try { logQuestionAnswer(question.stem, answer.answer, question.questionType); const result = await fillSingleAnswer(question, answer); results.push(result); if (result.success) { successCount++; logMessage(`✅ 第${i + 1}题填写成功`, 'success'); } else { logMessage(`❌ 第${i + 1}题填写失败: ${result.message}`, 'error'); } await new Promise(resolve => setTimeout(resolve, 500)); } catch (error) { logMessage(`❌ 第${i + 1}题处理异常: ${error.message}`, 'error'); results.push({ success: false, message: error.message }); } } const message = `答题完成:成功 ${successCount}/${questions.length} 题`; logMessage(message, successCount > 0 ? 'success' : 'error'); return { success: successCount > 0, message: message, successCount: successCount, totalCount: questions.length, results: results }; } function selectXueqiAnswer(questionIndex, answer) { try { const questionElements = document.querySelectorAll('.questionItem'); if (questionIndex >= questionElements.length) { console.log('题目索引超出范围'); return false; } const questionEl = questionElements[questionIndex]; if (questionEl.classList.contains('judge') || questionEl.querySelector('.judge')) { const judgeButtons = questionEl.querySelectorAll('.JudgeBtn input'); for (let btn of judgeButtons) { const btnValue = btn.value.trim(); if ((answer === 'T' && btnValue === '正确') || (answer === 'F' && btnValue === '错误')) { btn.click(); const judgeBtn = btn.parentElement; judgeBtn.style.backgroundColor = '#e8f5e8'; setTimeout(() => { judgeBtn.style.backgroundColor = ''; }, 1000); return true; } } } else if (questionEl.classList.contains('Mutli') || questionEl.querySelector('.Mutli')) { const options = questionEl.querySelectorAll('dd.clearfix'); if (!answer || typeof answer !== 'string' || answer.length === 0) { console.error('多选题答案无效:', answer); return false; } let answersToSelect; try { answersToSelect = [...answer]; } catch (err) { console.error('多选题答案转换失败:', err); return false; } let successCount = 0; for (let option of options) { const optionLabel = option.querySelector('.duplexCheck'); if (optionLabel) { const labelText = optionLabel.textContent.trim(); if (answersToSelect && answersToSelect.includes(labelText)) { option.click(); option.style.backgroundColor = '#e8f5e8'; setTimeout(() => { option.style.backgroundColor = ''; }, 1000); successCount++; } } } return answersToSelect && successCount === answersToSelect.length; } else { const options = questionEl.querySelectorAll('dd.clearfix'); for (let option of options) { const optionLabel = option.querySelector('.singleCheck'); if (optionLabel && optionLabel.textContent.trim() === answer) { option.click(); option.style.backgroundColor = '#e8f5e8'; setTimeout(() => { option.style.backgroundColor = ''; }, 1000); return true; } } } return false; } catch (error) { console.error('选择答案失败:', error); return false; } } function selectChaoxingAnswer(questionIndex, answer) { try { logMessage(`🎯 [超星] 选择第 ${questionIndex + 1} 题答案: ${answer}`, 'info'); const questions = getExamQuestions(); const questionData = questions[questionIndex]; if (!questionData) { logMessage(`❌ [超星] 第 ${questionIndex + 1} 题数据未找到`, 'error'); return false; } if (questionData.questionType === '填空题') { return selectFillInBlankAnswer(questionData, answer); } let answersToSelect = []; if (questionData.questionType === '判断题') { if (answer === 'T' || answer === 'true' || answer === '对') { answersToSelect = ['T']; } else if (answer === 'F' || answer === 'false' || answer === '错') { answersToSelect = ['F']; } else { const option = questionData.options.find(opt => opt.label === answer); if (option && option.dataValue) { answersToSelect = [option.dataValue === 'true' ? 'T' : 'F']; } else { answersToSelect = [answer]; } } } else { answersToSelect = [...answer.toUpperCase()]; } let successCount = 0; const questionId = questionData.questionId; if (questionData.questionType === '多选题') { return new Promise(async (resolve) => { for (let i = 0; i < answersToSelect.length; i++) { const ans = answersToSelect[i]; const targetOption = questionData.options.find(opt => opt.label === ans); if (targetOption && targetOption.element) { const isSelected = targetOption.element.classList.contains('hasBeenTo') || targetOption.element.classList.contains('selected') || targetOption.element.querySelector('.num_option, .num_option_dx')?.classList.contains('hasBeenTo'); if (isSelected) { successCount++; continue; } try { const isHomeworkPage = document.querySelector('.questionLi[typename]') !== null; if (isHomeworkPage) { if (typeof pageWindow.addMultipleChoice === 'function') { pageWindow.addMultipleChoice(targetOption.element); } else { targetOption.element.click(); logMessage(`✅ [超星] 通过click()选择选项 ${ans}`, 'success'); } } else { const optionQid = targetOption.qid || questionId; let success = false; if (typeof pageWindow.saveMultiSelect === 'function' && optionQid) { try { pageWindow.saveMultiSelect(targetOption.element, optionQid); success = true; } catch (e) { console.log(`[超星] saveMultiSelect失败:`, e.message); } } if (!success && typeof pageWindow.addMultipleChoice === 'function') { try { pageWindow.addMultipleChoice(targetOption.element); success = true; } catch (e) { console.log(`[超星] addMultipleChoice失败:`, e.message); } } if (!success) { try { targetOption.element.click(); logMessage(`✅ [超星] 通过click()选择选项 ${ans}`, 'success'); success = true; } catch (e) { console.log(`[超星] click失败:`, e.message); } } if (!success) { logMessage(`❌ [超星] 所有方法都失败,无法选择多选题选项 ${ans}`, 'error'); } } targetOption.element.style.backgroundColor = '#e8f5e8'; targetOption.element.style.border = '2px solid #4ade80'; setTimeout(() => { targetOption.element.style.backgroundColor = ''; targetOption.element.style.border = ''; }, 2000); successCount++; if (i < answersToSelect.length - 1) { await new Promise(resolve => setTimeout(resolve, 300)); } } catch (clickError) { } } else { } } const success = successCount > 0; if (success) { } else { } resolve(success); }); } else { answersToSelect.forEach(ans => { let targetOption = null; if (questionData.questionType === '判断题') { targetOption = questionData.options.find(opt => opt.label === ans || (ans === 'T' && (opt.dataValue === 'true' || opt.content === '正确' || opt.content === '对')) || (ans === 'F' && (opt.dataValue === 'false' || opt.content === '错误' || opt.content === '错')) ); } else { targetOption = questionData.options.find(opt => opt.label === ans); } if (targetOption && targetOption.element) { const isSelected = targetOption.element.classList.contains('hasBeenTo') || targetOption.element.classList.contains('selected') || targetOption.element.querySelector('.num_option, .num_option_dx')?.classList.contains('hasBeenTo'); if (isSelected) { successCount++; return; } try { const isHomeworkPage = document.querySelector('.questionLi[typename]') !== null; if (isHomeworkPage) { if (typeof pageWindow.addChoice === 'function') { pageWindow.addChoice(targetOption.element); logMessage(`✅ [超星] 通过addChoice()选择选项 ${ans}`, 'success'); } else { targetOption.element.click(); logMessage(`✅ [超星] 通过click()选择选项 ${ans}`, 'success'); } } else { const optionQid = targetOption.qid || questionId; if (typeof pageWindow.saveSingleSelect === 'function' && optionQid) { pageWindow.saveSingleSelect(targetOption.element, optionQid); logMessage(`✅ [超星] 通过saveSingleSelect()选择选项 ${ans}`, 'success'); } else { targetOption.element.click(); logMessage(`✅ [超星] 通过click()选择选项 ${ans}`, 'success'); } } targetOption.element.style.backgroundColor = '#e8f5e8'; targetOption.element.style.border = '2px solid #4ade80'; setTimeout(() => { targetOption.element.style.backgroundColor = ''; targetOption.element.style.border = ''; }, 2000); successCount++; } catch (clickError) { logMessage(`❌ [超星] 点击选项 ${ans} 失败: ${clickError.message}`, 'error'); console.error('[超星] 点击错误详情:', clickError); } } else { logMessage(`⚠️ [超星] 未找到答案选项 '${ans}'`, 'warning'); console.log('[超星] 可用选项:', questionData.options.map(opt => `${opt.label}(${opt.content})`)); } }); } const success = successCount > 0; if (success) { logMessage(`✅ [超星] 第 ${questionIndex + 1} 题答案选择完成 (${successCount}/${answersToSelect.length})`, 'success'); } else { logMessage(`❌ [超星] 第 ${questionIndex + 1} 题答案选择失败`, 'error'); } return success; } catch (error) { logMessage(`❌ [超星] 选择答案时出错: ${error.message}`, 'error'); console.error('[超星] 答案选择错误:', error); return false; } } function selectFillInBlankAnswer(questionData, answer) { try { logMessage(`📝 [填空题] 填入答案: ${answer}`, 'info'); const fillInBlankOptions = questionData.options.filter(opt => opt.isFillInBlank); if (fillInBlankOptions.length === 0) { logMessage(`❌ [填空题] 未找到输入框`, 'error'); return false; } let successCount = 0; let answers = []; if (answer.includes('|')) { answers = answer.split('|').map(a => a.trim()); logMessage(`📝 [填空题] 使用|分隔,解析出${answers.length}个答案: ${answers.join(', ')}`, 'info'); } else if (answer.includes(',')) { answers = answer.split(',').map(a => a.trim()); logMessage(`📝 [填空题] 使用,分隔,解析出${answers.length}个答案: ${answers.join(', ')}`, 'info'); } else if (answer.includes(',')) { answers = answer.split(',').map(a => a.trim()); logMessage(`📝 [填空题] 使用,分隔,解析出${answers.length}个答案: ${answers.join(', ')}`, 'info'); } else { answers = [answer.trim()]; logMessage(`📝 [填空题] 单个答案: ${answers[0]}`, 'info'); } fillInBlankOptions.forEach((option, index) => { if (index >= answers.length) { logMessage(`⚠️ [填空题] 填空${index + 1}没有对应答案,跳过`, 'warning'); return; } const answerText = answers[index]; logMessage(`📝 [填空题] 准备填入填空${index + 1}: "${answerText}"`, 'info'); if (option.element) { try { if (option.inputType === 'ueditor') { const editorId = option.editorId; logMessage(`🔍 [填空题] UEditor ID: ${editorId}`, 'info'); let ueditorSuccess = false; if (option.iframeBody) { try { option.iframeBody.innerHTML = `

${answerText}

`; const inputEvent = new Event('input', { bubbles: true }); option.iframeBody.dispatchEvent(inputEvent); logMessage(`✅ [填空题] 直接操作iframeBody填空${index + 1}已填入: ${answerText}`, 'success'); ueditorSuccess = true; successCount++; } catch (error) { logMessage(`⚠️ [填空题] 直接操作iframeBody失败: ${error.message}`, 'warning'); } } if (typeof window.UE !== 'undefined') { let editor = null; if (window.UE.getEditor) { editor = window.UE.getEditor(editorId); } if (!editor && window.UE.instants && option.ueditorInstanceName) { editor = window.UE.instants[option.ueditorInstanceName]; } logMessage(`🔍 [填空题] UEditor实例状态: ${editor ? '找到' : '未找到'} (ID: ${editorId})`, 'info'); if (editor && editor.setContent) { editor.setContent(answerText); logMessage(`✅ [填空题] UEditor setContent填空${index + 1}已填入: ${answerText}`, 'success'); ueditorSuccess = true; successCount++; } else if (editor && editor.execCommand) { editor.execCommand('inserthtml', answerText); logMessage(`✅ [填空题] UEditor execCommand填空${index + 1}已填入: ${answerText}`, 'success'); ueditorSuccess = true; successCount++; } else if (editor && editor.body) { editor.body.innerHTML = `

${answerText}

`; logMessage(`✅ [填空题] UEditor body操作填空${index + 1}已填入: ${answerText}`, 'success'); ueditorSuccess = true; successCount++; } else { logMessage(`⚠️ [填空题] UEditor实例方法不可用,尝试其他方法`, 'warning'); } } else { logMessage(`⚠️ [填空题] UE对象不存在,尝试其他方法`, 'warning'); } if (!ueditorSuccess && option.iframe) { try { const iframe = option.iframe; logMessage(`🔍 [填空题] 重新尝试获取iframe body: ${iframe.id}`, 'info'); const tryGetIframeBody = (attempts = 0) => { try { if (iframe.contentDocument && iframe.contentDocument.body) { const iframeBody = iframe.contentDocument.body; if (iframeBody.contentEditable === 'true' || iframeBody.classList.contains('view')) { iframeBody.innerHTML = `

${answerText}

`; const events = ['input', 'change', 'keyup', 'blur']; events.forEach(eventType => { try { const event = new iframe.contentWindow.Event(eventType, { bubbles: true }); iframeBody.dispatchEvent(event); } catch (e) { } }); logMessage(`✅ [填空题] iframe重新获取填空${index + 1}已填入: ${answerText}`, 'success'); ueditorSuccess = true; successCount++; return true; } else { logMessage(`⚠️ [填空题] iframe body不可编辑`, 'warning'); } } } catch (e) { logMessage(`⚠️ [填空题] iframe访问失败 (尝试${attempts + 1}): ${e.message}`, 'warning'); } if (attempts < 2) { setTimeout(() => tryGetIframeBody(attempts + 1), 200); } return false; }; tryGetIframeBody(); } catch (error) { logMessage(`⚠️ [填空题] iframe重新获取失败: ${error.message}`, 'warning'); } } if (!ueditorSuccess && option.iframe) { try { const iframe = option.iframe; if (iframe.contentDocument && iframe.contentWindow) { const iframeDoc = iframe.contentDocument; const iframeBody = iframeDoc.body; if (iframeBody) { iframeBody.innerHTML = ''; const p = iframeDoc.createElement('p'); p.textContent = answerText; p.appendChild(iframeDoc.createElement('br')); iframeBody.appendChild(p); const inputEvent = new Event('input', { bubbles: true }); iframeBody.dispatchEvent(inputEvent); logMessage(`✅ [填空题] 模拟操作填空${index + 1}已填入: ${answerText}`, 'success'); ueditorSuccess = true; successCount++; } } } catch (error) { logMessage(`⚠️ [填空题] 模拟操作失败: ${error.message}`, 'warning'); } } if (!ueditorSuccess && option.iframe) { try { const iframe = option.iframe; logMessage(`🔍 [填空题] 使用保存的iframe引用: ${iframe.id}`, 'info'); const setIframeContent = () => { try { if (iframe.contentDocument && iframe.contentDocument.body) { const body = iframe.contentDocument.body; body.innerHTML = `

${answerText}

`; if (iframe.contentWindow) { const inputEvent = new iframe.contentWindow.Event('input', { bubbles: true }); body.dispatchEvent(inputEvent); } logMessage(`✅ [填空题] 使用iframe引用填空${index + 1}已填入: ${answerText}`, 'success'); ueditorSuccess = true; successCount++; return true; } } catch (e) { logMessage(`⚠️ [填空题] iframe内容设置失败: ${e.message}`, 'warning'); } return false; }; if (!setIframeContent()) { setTimeout(setIframeContent, 200); } } catch (error) { logMessage(`⚠️ [填空题] iframe引用操作失败: ${error.message}`, 'warning'); } } } else { const element = option.element; logMessage(`🔍 [填空题] 元素类型: ${element.tagName}, name: ${element.name}, id: ${element.id}`, 'info'); element.value = answerText; if (element.value === answerText) { logMessage(`✅ [填空题] 值设置成功: ${element.value}`, 'info'); } else { logMessage(`❌ [填空题] 值设置失败,期望: ${answerText}, 实际: ${element.value}`, 'error'); } const events = ['input', 'change', 'blur', 'keyup']; events.forEach(eventType => { const event = new Event(eventType, { bubbles: true, cancelable: true }); element.dispatchEvent(event); }); element.focus(); setTimeout(() => { element.blur(); }, 100); logMessage(`✅ [填空题] 普通填空${index + 1}已填入: ${answerText}`, 'success'); successCount++; } if (option.element.style) { option.element.style.backgroundColor = '#e8f5e8'; option.element.style.border = '2px solid #4ade80'; setTimeout(() => { option.element.style.backgroundColor = ''; option.element.style.border = ''; }, 2000); } setTimeout(() => { const currentValue = option.element.value; if (currentValue === answerText) { logMessage(`✅ [填空题] 验证成功,填空${index + 1}当前值: ${currentValue}`, 'success'); } else { logMessage(`❌ [填空题] 验证失败,填空${index + 1}期望: ${answerText}, 实际: ${currentValue}`, 'error'); logMessage(`💡 [填空题] 请手动检查并填入答案: ${answerText}`, 'warning'); } }, 1000); } catch (error) { logMessage(`❌ [填空题] 填空${index + 1}填入失败: ${error.message}`, 'error'); console.error(`[填空题] 详细错误:`, error); } } else if (option.isVirtual) { logMessage(`📝 [填空题] 虚拟填空${index + 1}答案: ${answerText}`, 'info'); logMessage(`💡 [填空题] 请手动将答案"${answerText}"填入对应位置`, 'warning'); successCount++; } else { logMessage(`❌ [填空题] 填空${index + 1}没有找到输入框`, 'error'); } }); const success = successCount > 0; if (success) { logMessage(`✅ [填空题] 答案填入完成 (${successCount}/${fillInBlankOptions.length})`, 'success'); } else { logMessage(`❌ [填空题] 答案填入失败`, 'error'); } return success; } catch (error) { logMessage(`❌ [填空题] 填入答案时出错: ${error.message}`, 'error'); return false; } } async function selectAnswer(questionIndex, answer) { if (!currentSite) detectSite(); if (currentSite && typeof currentSite.selectAnswer === 'function') { const result = currentSite.selectAnswer(questionIndex, answer); if (result && typeof result.then === 'function') { return await result; } return result; } return false; } function createControlWindow() { const isPreviewPage = window.location.href.includes('/exam-ans/mooc2/exam/preview'); if (!isPreviewPage) { return null; } if (document.getElementById('exam-auto-control')) { return document.getElementById('exam-auto-control'); } const controlWindow = document.createElement('div'); controlWindow.id = 'exam-auto-control'; controlWindow.style.cssText = ` position: fixed; top: 20px; right: 20px; width: 450px; max-height: 400px; background: #ffffff; border: 1px solid #e1e5e9; border-radius: 12px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12); z-index: 999999; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; overflow: hidden; backdrop-filter: blur(10px); `; const titleBar = document.createElement('div'); titleBar.style.cssText = ` background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 12px 16px; cursor: move; font-size: 14px; font-weight: 600; display: flex; align-items: center; justify-content: space-between; `; const titleText = document.createElement('span'); titleText.textContent = '🤖 AI答题助手'; const minimizeBtn = document.createElement('button'); minimizeBtn.innerHTML = '−'; minimizeBtn.style.cssText = ` background: rgba(255, 255, 255, 0.2); border: none; color: white; width: 24px; height: 24px; border-radius: 4px; cursor: pointer; font-size: 16px; line-height: 1; `; titleBar.appendChild(titleText); titleBar.appendChild(minimizeBtn); const logArea = document.createElement('div'); logArea.id = 'log-display'; logArea.style.cssText = ` height: 320px; overflow-y: auto; padding: 16px; font-size: 13px; line-height: 1.5; background: #fafbfc; `; let isMinimized = false; minimizeBtn.addEventListener('click', () => { isMinimized = !isMinimized; if (isMinimized) { logArea.style.display = 'none'; controlWindow.style.height = 'auto'; minimizeBtn.innerHTML = '+'; } else { logArea.style.display = 'block'; minimizeBtn.innerHTML = '−'; } }); controlWindow.appendChild(titleBar); controlWindow.appendChild(logArea); document.body.appendChild(controlWindow); makeDraggable(controlWindow, titleBar); return controlWindow; } function makeDraggable(element, handle) { let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; handle.onmousedown = dragMouseDown; function dragMouseDown(e) { e = e || window.event; e.preventDefault(); pos3 = e.clientX; pos4 = e.clientY; document.onmouseup = closeDragElement; document.onmousemove = elementDrag; } function elementDrag(e) { e = e || window.event; e.preventDefault(); pos1 = pos3 - e.clientX; pos2 = pos4 - e.clientY; pos3 = e.clientX; pos4 = e.clientY; element.style.top = (element.offsetTop - pos2) + "px"; element.style.left = (element.offsetLeft - pos1) + "px"; } function closeDragElement() { document.onmouseup = null; document.onmousemove = null; } } function logMessage(message, type = 'info') { const logArea = document.getElementById('log-display'); if (!logArea) return; const timestamp = new Date().toLocaleTimeString('zh-CN', { hour12: false }); const colors = { 'info': '#6c757d', 'success': '#28a745', 'warning': '#ffc107', 'error': '#dc3545', 'question': '#007bff', 'answer': '#17a2b8' }; const logEntry = document.createElement('div'); logEntry.style.cssText = ` margin-bottom: 8px; padding: 8px 12px; border-radius: 6px; background: white; border-left: 3px solid ${colors[type] || colors.info}; font-size: 12px; line-height: 1.4; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); `; const timeSpan = document.createElement('span'); timeSpan.style.cssText = ` color: #6c757d; font-weight: 500; margin-right: 8px; `; timeSpan.textContent = `[${timestamp}]`; const messageSpan = document.createElement('span'); messageSpan.style.color = colors[type] || colors.info; messageSpan.textContent = message; logEntry.appendChild(timeSpan); logEntry.appendChild(messageSpan); logArea.appendChild(logEntry); logArea.scrollTop = logArea.scrollHeight; if (logArea.children.length > 50) { logArea.removeChild(logArea.firstChild); } } function logQuestionAnswer(question, answer, questionType = '') { const logArea = document.getElementById('log-display'); if (!logArea) return; const timestamp = new Date().toLocaleTimeString('zh-CN', { hour12: false }); const logEntry = document.createElement('div'); logEntry.style.cssText = ` margin-bottom: 12px; padding: 12px; border-radius: 8px; background: linear-gradient(135deg, #f8f9ff 0%, #fff 100%); border: 1px solid #e3f2fd; font-size: 12px; line-height: 1.5; `; const timeSpan = document.createElement('div'); timeSpan.style.cssText = ` color: #666; font-weight: 500; margin-bottom: 6px; font-size: 11px; `; timeSpan.textContent = `[${timestamp}] ${questionType}`; const questionDiv = document.createElement('div'); questionDiv.style.cssText = ` color: #333; margin-bottom: 6px; font-weight: 500; `; questionDiv.textContent = question.length > 80 ? question.substring(0, 80) + '...' : question; const answerDiv = document.createElement('div'); answerDiv.style.cssText = ` color: #28a745; font-weight: 600; padding: 4px 8px; background: rgba(40, 167, 69, 0.1); border-radius: 4px; display: inline-block; `; answerDiv.textContent = `答案:${answer}`; logEntry.appendChild(timeSpan); logEntry.appendChild(questionDiv); logEntry.appendChild(answerDiv); logArea.appendChild(logEntry); logArea.scrollTop = logArea.scrollHeight; if (logArea.children.length > 50) { logArea.removeChild(logArea.firstChild); } } async function answerSingleQuestion(questionIndex) { try { const questions = getExamQuestions(); if (!questions || questionIndex >= questions.length) { logMessage(`❌ 第 ${questionIndex + 1} 题: 题目不存在`, 'error'); return null; } const questionData = questions[questionIndex]; logMessage(`📝 正在答第 ${questionIndex + 1} 题: ${questionData.questionType}`, 'info'); const apiResponse = await callCloudAPI(questionData); if (!apiResponse || !apiResponse.answer) { logMessage(`❌ 第 ${questionIndex + 1} 题: 未获取到答案`, 'error'); return null; } const answer = apiResponse.answer.trim(); logMessage(`💡 第 ${questionIndex + 1} 题答案: ${answer}`, 'success'); const selectSuccess = await selectAnswer(questionIndex, answer); if (selectSuccess) { logMessage(`✅ 第 ${questionIndex + 1} 题答题成功`, 'success'); return { questionIndex: questionIndex + 1, answer: answer, success: true }; } else { logMessage(`❌ 第 ${questionIndex + 1} 题: 答案选择失败`, 'error'); return null; } } catch (error) { logMessage(`❌ 第 ${questionIndex + 1} 题答题异常: ${error.message}`, 'error'); return null; } } async function autoStartAnswering() { try { await new Promise(resolve => setTimeout(resolve, 1500)); const currentUrl = window.location.href; logMessage(`🔍 当前页面URL: ${currentUrl}`, 'info'); if (currentUrl.includes('exam-ans/exam/test/reVersionTestStartNew')) { logMessage('📄 检测到考试开始页面,查找整卷预览按钮...', 'info'); const previewButton = document.querySelector('.sub-button a.completeBtn'); if (previewButton && previewButton.textContent.includes('整卷预览')) { logMessage('📄 找到整卷预览按钮,将自动点击...', 'info'); setTimeout(() => { if (typeof pageWindow.topreview === 'function') { logMessage('⚡️ 直接调用页面函数 topreview()。', 'info'); pageWindow.topreview(); } else { logMessage('⚠️ 页面函数 topreview 不存在,回退到模拟点击。', 'warning'); previewButton.click(); } }, 1500); return; } else { logMessage('❌ 未找到整卷预览按钮', 'warning'); } } else if (currentUrl.includes('exam-ans/mooc2/exam/preview')) { logMessage('📝 检测到答题预览页面,开始自动答题...', 'info'); const questions = getExamQuestions(); if (questions && questions.length > 0) { logMessage(`发现 ${questions.length} 道题目,开始自动答题`, 'success'); const results = await autoAnswerAllQuestions(2000); if (results.length > 0) { logMessage(`自动答题完成,成功 ${results.length} 题`, 'success'); } else { logMessage('自动答题未成功,请检查页面', 'warning'); } } else { logMessage('未发现题目,可能页面还在加载中', 'info'); } } else { logMessage('🔍 其他页面,使用通用检测逻辑...', 'info'); if (currentSite && currentSite.name === '超星学习通') { const previewButton = document.querySelector('.sub-button a.completeBtn'); if (previewButton && previewButton.textContent.includes('整卷预览')) { logMessage('📄 检测到单题模式,将自动点击"整卷预览"...', 'info'); setTimeout(() => { if (typeof pageWindow.topreview === 'function') { logMessage('⚡️ 直接调用页面函数 topreview()。', 'info'); pageWindow.topreview(); } else { logMessage('⚠️ 页面函数 topreview 不存在,回退到模拟点击。', 'warning'); previewButton.click(); } }, 1500); return; } } const questions = getExamQuestions(); if (questions && questions.length > 0) { logMessage(`发现 ${questions.length} 道题目,开始自动答题`, 'success'); const results = await autoAnswerAllQuestions(2000); if (results.length > 0) { logMessage(`自动答题完成,成功 ${results.length} 题`, 'success'); } else { logMessage('自动答题未成功,请检查页面', 'warning'); } } else { logMessage('未发现题目,可能不是答题页面', 'info'); } } } catch (error) { logMessage(`自动答题启动失败: ${error.message}`, 'error'); } } async function initializeApp() { try { createControlWindow(); window.addEventListener('unhandledrejection', event => { if (event.reason && event.reason.message && event.reason.message.includes('429')) { logMessage('⚠️ 请求过于频繁,请稍后重试', 'warning'); } }); detectSite(); const tokenResult = await TokenManager.initialize(); if (!tokenResult.hasToken) { logMessage('❌ Token未设置,请先配置Token', 'error'); return; } if (currentSite && currentSite.autoStudy) { logMessage('🎓 学习页面检测成功,自动学习功能已启动', 'success'); logMessage('📹 视频自动播放功能已启用', 'info'); logMessage('📝 章节测验自动答题功能已启用', 'info'); logMessage('⏭️ 自动进入下一节功能已启用', 'info'); return; } logMessage('✅ 云端AI答题助手启动完成', 'success'); await autoStartAnswering(); } catch (error) { logMessage(`❌ 初始化失败: ${error.message}`, 'error'); } } if (pageWindow.AI_ASSISTANT_INITIALIZED) { return; } pageWindow.AI_ASSISTANT_INITIALIZED = true; if (pageDocument.readyState === 'loading') { pageDocument.addEventListener('DOMContentLoaded', () => { setTimeout(initializeApp, 800); }); } else { setTimeout(initializeApp, 800); } function createVideoSpeedPanel() { if (document.getElementById('videoSpeedPanel')) { return; } const speedPanel = document.createElement('div'); speedPanel.id = 'videoSpeedPanel'; speedPanel.innerHTML = `
倍速控制
当前: 1x
0/0 | 等待中
`; const style = document.createElement('style'); style.textContent = ` #videoSpeedPanel { position: fixed; top: 120px; right: 20px; width: 130px; background: white; border: 1px solid #ccc; border-radius: 4px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); z-index: 10000; font-family: Arial, sans-serif; display: none; font-size: 12px; } .speed-panel-header { background: #f5f5f5; padding: 6px 8px; border-bottom: 1px solid #ddd; display: flex; justify-content: space-between; align-items: center; cursor: move; font-size: 12px; } .speed-panel-close { background: none; border: none; font-size: 16px; cursor: pointer; padding: 0; width: 16px; height: 16px; } .speed-panel-content { padding: 8px; } .speed-buttons { display: grid; grid-template-columns: 1fr 1fr; gap: 4px; margin-bottom: 6px; } .speed-btn { background: #f9f9f9; border: 1px solid #ddd; border-radius: 3px; padding: 4px; font-size: 11px; cursor: pointer; } .speed-btn:hover { background: #e9e9e9; } .speed-btn.active { background: #007cba; color: white; border-color: #007cba; } .speed-info { text-align: center; font-size: 11px; margin: 4px 0; color: #666; } .task-status { text-align: center; font-size: 10px; color: #666; margin: 4px 0; padding-top: 4px; border-top: 1px solid #eee; } .panel-footer { text-align: center; font-size: 11px; color: #007cba; margin-top: 6px; padding-top: 4px; border-top: 1px solid #eee; cursor: pointer; } .panel-footer:hover { color: #0056b3; } `; document.head.appendChild(style); document.body.appendChild(speedPanel); bindVideoSpeedEvents(); } function bindVideoSpeedEvents() { const speedPanel = document.getElementById('videoSpeedPanel'); const closeBtn = speedPanel.querySelector('.speed-panel-close'); const speedBtns = speedPanel.querySelectorAll('.speed-btn'); const currentSpeedSpan = document.getElementById('currentSpeed'); const dragHandle = document.getElementById('speedPanelDragHandle'); let isDragging = false; let dragOffset = { x: 0, y: 0 }; dragHandle.addEventListener('mousedown', (e) => { isDragging = true; const rect = speedPanel.getBoundingClientRect(); dragOffset.x = e.clientX - rect.left; dragOffset.y = e.clientY - rect.top; dragHandle.style.cursor = 'grabbing'; e.preventDefault(); }); document.addEventListener('mousemove', (e) => { if (!isDragging) return; const x = e.clientX - dragOffset.x; const y = e.clientY - dragOffset.y; const maxX = window.innerWidth - speedPanel.offsetWidth; const maxY = window.innerHeight - speedPanel.offsetHeight; speedPanel.style.left = Math.max(0, Math.min(x, maxX)) + 'px'; speedPanel.style.top = Math.max(0, Math.min(y, maxY)) + 'px'; speedPanel.style.right = 'auto'; }); document.addEventListener('mouseup', () => { if (isDragging) { isDragging = false; dragHandle.style.cursor = 'move'; } }); closeBtn.addEventListener('click', () => { speedPanel.style.display = 'none'; if (window.taskStatusUpdateInterval) { clearInterval(window.taskStatusUpdateInterval); window.taskStatusUpdateInterval = null; } }); speedBtns.forEach(btn => { btn.addEventListener('click', () => { const speed = parseFloat(btn.dataset.speed); setVideoSpeed(speed); speedBtns.forEach(b => b.classList.remove('active')); btn.classList.add('active'); currentSpeedSpan.textContent = speed + 'x'; }); }); const savedSpeed = getSavedVideoSpeed(); speedBtns.forEach(btn => { const btnSpeed = parseFloat(btn.dataset.speed); if (btnSpeed === savedSpeed) { btn.classList.add('active'); currentSpeedSpan.textContent = `${savedSpeed}x`; } }); if (!speedPanel.querySelector('.speed-btn.active')) { speedBtns[0].classList.add('active'); } const panelFooter = speedPanel.querySelector('.panel-footer'); if (panelFooter) { panelFooter.addEventListener('click', () => { const groupNumber = '923349555'; if (navigator.clipboard) { navigator.clipboard.writeText(groupNumber).then(() => { panelFooter.textContent = '群号已复制!'; setTimeout(() => { panelFooter.textContent = '反馈群: 923349555'; }, 2000); }).catch(() => { panelFooter.textContent = '手动复制: 923349555'; setTimeout(() => { panelFooter.textContent = '反馈群: 923349555'; }, 3000); }); } else { panelFooter.textContent = '手动复制: 923349555'; setTimeout(() => { panelFooter.textContent = '反馈群: 923349555'; }, 3000); } }); } } function setVideoSpeed(speed) { try { localStorage.setItem('videoPlaybackRate', speed.toString()); console.log(`💾 [倍速设置] 已保存倍速: ${speed}x`); } catch (e) { console.warn('⚠️ [倍速设置] 保存失败:', e); } let videoCount = 0; function setSpeedInDocument(doc, context = 'main', depth = 0) { if (depth > 5) return; try { const videos = doc.querySelectorAll('video'); videos.forEach((video, index) => { if (video.readyState >= 1) { video.playbackRate = speed; videoCount++; console.log(`🎬 [倍速设置] ${context} 视频 #${index} 倍速已设置为 ${speed}x`); } }); const iframes = doc.querySelectorAll('iframe'); iframes.forEach((iframe, index) => { try { const iframeDoc = iframe.contentDocument || iframe.contentWindow?.document; if (iframeDoc) { const iframeContext = `${context}-iframe-${index}`; setSpeedInDocument(iframeDoc, iframeContext, depth + 1); } } catch (e) { console.warn(`⚠️ [倍速设置] ${context}-iframe-${index} 访问失败(跨域)`); } }); } catch (e) { console.warn(`⚠️ [倍速设置] ${context} 处理失败:`, e); } } setSpeedInDocument(document, 'main'); console.log(`🎬 [倍速设置] 总共设置了 ${videoCount} 个视频为 ${speed}x 倍速`); try { window.dispatchEvent(new CustomEvent('videoSpeedChanged', { detail: { speed: speed, videoCount: videoCount } })); } catch (e) { } } function getSavedVideoSpeed() { try { const savedSpeed = localStorage.getItem('videoPlaybackRate'); if (savedSpeed) { const speed = parseFloat(savedSpeed); if (speed > 0 && speed <= 16) { console.log(`📖 [倍速设置] 读取保存的倍速: ${speed}x`); return speed; } } } catch (e) { console.warn('⚠️ [倍速设置] 读取失败:', e); } console.log(`📖 [倍速设置] 使用默认倍速: 1x`); return 1; } function applySavedSpeedToVideo(video) { if (!video) return; const savedSpeed = getSavedVideoSpeed(); const applySpeed = () => { if (video.readyState >= 1) { try { video.playbackRate = savedSpeed; console.log(`🎬 [倍速恢复] 视频倍速已设置为: ${savedSpeed}x (readyState: ${video.readyState})`); } catch (e) { console.warn('⚠️ [倍速恢复] 设置失败:', e); } } else { const onLoadedData = () => { try { video.playbackRate = savedSpeed; console.log(`🎬 [倍速恢复] 视频加载完成后倍速已设置为: ${savedSpeed}x`); } catch (e) { console.warn('⚠️ [倍速恢复] 延迟设置失败:', e); } video.removeEventListener('loadeddata', onLoadedData); }; video.addEventListener('loadeddata', onLoadedData); setTimeout(() => { video.removeEventListener('loadeddata', onLoadedData); }, 5000); } }; applySpeed(); } function updateTaskStatus() { const taskProgressEl = document.getElementById('taskProgress'); const currentTaskEl = document.getElementById('currentTask'); if (!taskProgressEl || !currentTaskEl) return; const taskStatus = typeof checkTaskCompletion === 'function' ? checkTaskCompletion() : { completed: 0, total: 0, isAllCompleted: false }; taskProgressEl.textContent = `${taskStatus.completed}/${taskStatus.total}`; let currentTask = '空闲'; if (GLOBAL_STATE.isAnswering) { currentTask = '答题中'; } else if (GLOBAL_STATE.isChapterTesting) { currentTask = '章节测试'; } else if (taskStatus.total > 0 && !taskStatus.isAllCompleted) { const videoIframes = document.querySelectorAll('iframe.ans-attach-online'); const hasVideo = videoIframes.length > 0; if (hasVideo) { currentTask = '视频学习'; } else { currentTask = '文档学习'; } } else if (taskStatus.isAllCompleted) { currentTask = '已完成'; } currentTaskEl.textContent = currentTask; } function showVideoSpeedPanel() { createVideoSpeedPanel(); const speedPanel = document.getElementById('videoSpeedPanel'); speedPanel.style.display = 'block'; updateTaskStatus(); if (!window.taskStatusUpdateInterval) { window.taskStatusUpdateInterval = setInterval(updateTaskStatus, 2000); } } function startVideoSpeedMonitoring() { console.log(`[${new Date().toLocaleTimeString()}] [倍速监控] 启动视频倍速监控系统`); let monitorInterval = setInterval(() => { const savedSpeed = getSavedVideoSpeed(); if (savedSpeed === 1) return; let appliedCount = 0; function checkVideosInDocument(doc, context = 'main', depth = 0) { if (depth > 5) return; try { const videos = doc.querySelectorAll('video'); videos.forEach((video, index) => { if (video.readyState >= 1 && video.playbackRate !== savedSpeed) { video.playbackRate = savedSpeed; appliedCount++; console.log(`🔄 [倍速监控] ${context} 视频 #${index} 倍速已调整为 ${savedSpeed}x`); } }); const iframes = doc.querySelectorAll('iframe'); iframes.forEach((iframe, index) => { try { const iframeDoc = iframe.contentDocument || iframe.contentWindow?.document; if (iframeDoc) { checkVideosInDocument(iframeDoc, `${context}-iframe-${index}`, depth + 1); } } catch (e) { } }); } catch (e) { } } checkVideosInDocument(document, 'main'); if (appliedCount > 0) { console.log(`🔄 [倍速监控] 本次检查调整了 ${appliedCount} 个视频的倍速`); } }, 5000); window.videoSpeedMonitorId = monitorInterval; } if (!window.videoSpeedMonitorId) { startVideoSpeedMonitoring(); } function controlIframeVideo() { console.log(`[${new Date().toLocaleTimeString()}] [iframe视频] 开始检测iframe视频`); const videoIframes = document.querySelectorAll('iframe'); let controlledCount = 0; videoIframes.forEach((iframe, index) => { try { const iframeDoc = iframe.contentDocument || iframe.contentWindow?.document; if (!iframeDoc) { console.log(`[${new Date().toLocaleTimeString()}] [iframe视频] iframe #${index} 无法访问(可能跨域)`); return; } const videos = iframeDoc.querySelectorAll('video'); const playButton = iframeDoc.querySelector('.vjs-big-play-button'); if (videos.length === 0 && !playButton) { return; } console.log(`[${new Date().toLocaleTimeString()}] [iframe视频] iframe #${index} 发现 ${videos.length} 个视频元素`); videos.forEach((video, videoIndex) => { // 检查视频是否已经被处理过,避免重复设置 if (!video._iframeVideoProcessed) { // 不设置 muted = true,因为防切屏设置会阻止静音 // video.muted = true; // 注释掉,与防切屏设置保持一致 video.volume = 0.01; // 设置极低音量 video.autoplay = true; // 应用保存的播放速度 applySavedSpeedToVideo(video); // 标记视频已被处理 video._iframeVideoProcessed = true; console.log(`[${new Date().toLocaleTimeString()}] [iframe视频] iframe #${index} 视频 #${videoIndex} 初始化完成`); } if (!video._iframeControllerAdded) { video.addEventListener('play', () => { console.log(`[${new Date().toLocaleTimeString()}] [iframe视频] iframe #${index} 视频 #${videoIndex} 开始播放`); applySavedSpeedToVideo(video); // 清除等待标记 video._waitingLogged = false; video._pauseWaitingLogged = false; }); video.addEventListener('pause', () => { if (!video.ended && video.readyState >= 2) { // 禁用旧的视频控制逻辑,改为使用handleVideoFrames系统 const shouldPlay = false; // checkIfVideoShouldPlay(video, `iframe-${index}-video-${videoIndex}`); if (shouldPlay) { // 避免频繁恢复播放,添加时间间隔检查 const now = Date.now(); const lastResumeAttempt = video._lastResumeAttempt || 0; if (now - lastResumeAttempt > 1000) { // 1秒内不重复恢复 video._lastResumeAttempt = now; console.log(`[${new Date().toLocaleTimeString()}] [iframe视频] iframe #${index} 视频 #${videoIndex} 被暂停,正在恢复播放`); setTimeout(() => { video.play().catch(e => { console.warn(`[${new Date().toLocaleTimeString()}] [iframe视频] 恢复播放失败:`, e); }); }, 100); } } else { // 只在状态变化时记录日志 if (!video._pauseWaitingLogged) { console.log(`[${new Date().toLocaleTimeString()}] [iframe视频] iframe #${index} 视频 #${videoIndex} 被暂停,等待轮到播放`); video._pauseWaitingLogged = true; } } } }); video.addEventListener('loadeddata', () => { console.log(`[${new Date().toLocaleTimeString()}] [iframe视频] iframe #${index} 视频 #${videoIndex} 加载完成`); applySavedSpeedToVideo(video); }); video._iframeControllerAdded = true; } // 只在视频暂停且准备就绪时才尝试播放,避免重复操作 if (video.paused && !video.ended && video.readyState >= 2) { // 禁用旧的视频控制逻辑,改为使用handleVideoFrames系统 const shouldPlay = false; // checkIfVideoShouldPlay(video, `iframe-${index}-video-${videoIndex}`); if (shouldPlay) { // 检查是否最近已经尝试过播放,避免频繁重试 const now = Date.now(); const lastPlayAttempt = video._lastPlayAttempt || 0; if (now - lastPlayAttempt > 3000) { // 3秒内不重复尝试播放 video._lastPlayAttempt = now; if (playButton && playButton.offsetParent !== null) { console.log(`[${new Date().toLocaleTimeString()}] [iframe视频] iframe #${index} 视频 #${videoIndex} 点击播放按钮`); playButton.click(); } else { console.log(`[${new Date().toLocaleTimeString()}] [iframe视频] iframe #${index} 视频 #${videoIndex} 直接播放视频`); video.play().then(() => { applySavedSpeedToVideo(video); }).catch(e => { console.warn(`[${new Date().toLocaleTimeString()}] [iframe视频] 播放失败:`, e); }); } } } else { // 只在状态变化时记录日志,避免重复日志 if (!video._waitingLogged) { console.log(`[${new Date().toLocaleTimeString()}] [iframe视频] iframe #${index} 视频 #${videoIndex} 等待轮到播放`); video._waitingLogged = true; } } } else if (!video.paused) { // 如果视频正在播放,清除等待日志标记 video._waitingLogged = false; } controlledCount++; }); } catch (error) { console.warn(`[${new Date().toLocaleTimeString()}] [iframe视频] iframe #${index} 处理失败:`, error.message); } }); if (controlledCount > 0) { showVideoSpeedPanel(); console.log(`[${new Date().toLocaleTimeString()}] [iframe视频] 成功控制 ${controlledCount} 个视频元素`); } return controlledCount > 0; } /* iframe视频控制功能改进: 1. ✅ 增强iframe检测 - 检测所有iframe,不仅限于特定类名 2. ✅ 递归倍速设置 - 支持多层嵌套iframe的倍速控制 3. ✅ 智能视频监控 - 为每个视频添加事件监听器,自动处理暂停恢复 4. ✅ 倍速持久化 - 保存用户设置并自动应用到新视频 5. ✅ 定期监控系统 - 定期检查并调整新加载视频的倍速 6. ✅ 详细调试日志 - 完整的日志系统便于问题排查 7. ✅ 错误处理机制 - 优雅处理跨域和其他异常情况 新增特性: - 支持多层嵌套iframe(最多5层) - 自动事件监听器绑定 - 视频状态智能检测 - 倍速变更事件通知 - 定期监控和自动调整 */ function base64ToUint8Array(base64) { var data = window.atob(base64); var buffer = new Uint8Array(data.length); for (var i = 0; i < data.length; ++i) { buffer[i] = data.charCodeAt(i); } return buffer; } function chaoxingFontDecrypt(doc = document) { var $tip = Array.from(doc.querySelectorAll('style')).find(style => (style.textContent || style.innerHTML || '').includes('font-cxsecret') ); if (!$tip) return false; var fontText = $tip.textContent || $tip.innerHTML; var fontMatch = fontText.match(/base64,([\w\W]+?)'/); if (!fontMatch || !fontMatch[1]) return false; var font = Typr.parse(base64ToUint8Array(fontMatch[1]))[0]; var table = JSON.parse(GM_getResourceText('Table')); var match = {}; for (var i = 19968; i < 40870; i++) { var glyph = Typr.U.codeToGlyph(font, i); if (!glyph) continue; var path = Typr.U.glyphToPath(font, glyph); var pathMD5 = md5(JSON.stringify(path)).slice(24); if (table[pathMD5]) { match[i] = table[pathMD5]; } } var elements = doc.querySelectorAll('.font-cxsecret'); elements.forEach(function (element) { var html = element.innerHTML; for (var key in match) { if (match[key]) { var keyChar = String.fromCharCode(key); var valueChar = String.fromCharCode(match[key]); var regex = new RegExp(keyChar, 'g'); html = html.replace(regex, valueChar); } } element.innerHTML = html; element.classList.remove('font-cxsecret'); }); return elements.length > 0; } window.restartMainInterval = function () { if (window.mainIntervalId) { clearInterval(window.mainIntervalId); } }; window.restorePage = function () { let restoredCount = 0; const decryptedElements = document.querySelectorAll('[data-decrypted="true"]'); decryptedElements.forEach(element => { element.classList.add('font-cxsecret'); element.removeAttribute('data-decrypted'); restoredCount++; }); const originalDecryptedElements = document.querySelectorAll('[data-decrypted-original="true"]'); originalDecryptedElements.forEach(element => { element.classList.add('font-cxsecret'); element.removeAttribute('data-decrypted-original'); element.style.background = ''; element.style.borderColor = ''; restoredCount++; }); const inlineDecryptedElements = document.querySelectorAll('[data-decrypted-inline="true"]'); inlineDecryptedElements.forEach(element => { element.removeAttribute('data-decrypted-inline'); restoredCount++; }); return { restoredCount: restoredCount, success: restoredCount > 0 }; }; window.applyChaoxingFontDecryptOriginal = function () { try { const allStyles = document.querySelectorAll('style'); let $tip = null; for (const style of allStyles) { const content = style.textContent || style.innerHTML || ''; if (content.includes('font-cxsecret')) { $tip = style; break; } } if (!$tip) { console.log('ℹ️ [原版解密] 未找到font-cxsecret样式'); return false; } console.log('✅ [原版解密] 找到font-cxsecret样式'); const fontSecretElements = document.querySelectorAll('.font-cxsecret'); if (fontSecretElements.length === 0) { console.log('ℹ️ [原版解密] 未找到.font-cxsecret元素'); return false; } console.log(`✅ [原版解密] 找到 ${fontSecretElements.length} 个加密元素`); let processedCount = 0; fontSecretElements.forEach((element, index) => { try { const originalText = element.textContent || ''; element.classList.remove('font-cxsecret'); element.setAttribute('data-decrypted-original', 'true'); const newText = element.textContent || ''; console.log(` 元素 ${index + 1}: "${originalText.substring(0, 30)}..." → "${newText.substring(0, 30)}..."`); processedCount++; } catch (error) { console.log(`⚠️ [原版解密] 处理元素 ${index + 1} 失败:`, error.message); } }); return processedCount > 0; } catch (error) { console.log('❌ [原版解密] 执行失败:', error.message); return false; } }; window.decodePageTexts = async function () { console.log('🔄 [批量解码] 开始解码页面中的所有乱码文本...'); try { const mapping = await buildAIDecodingMapping(); if (Object.keys(mapping).length === 0) { console.log('⚠️ 未获取到有效的字符映射表'); return false; } const elements = document.querySelectorAll('.fontLabel, .after, .CeYan *'); let decodedCount = 0; for (const element of elements) { const originalText = element.textContent || ''; if (originalText && /[\u5600-\u56FF]/.test(originalText)) { let decodedText = originalText; for (const [garbled, decoded] of Object.entries(mapping)) { decodedText = decodedText.replace(new RegExp(garbled, 'g'), decoded); } if (decodedText !== originalText) { decodedCount++; } } } console.log(`✅ 批量解码完成,共处理 ${decodedCount} 个文本`); return true; } catch (error) { console.log(`❌ 批量解码失败: ${error.message}`); return false; } }; const IFRAME_TYPES = { VIDEO: 'video', CHAPTER_TEST: 'test', DOCUMENT: 'document', DISCUSSION: 'discussion', UNKNOWN: 'unknown' }; function findDeepestIframes(doc = document, depth = 0) { const iframes = doc.querySelectorAll('iframe'); let deepestFrames = []; for (const iframe of iframes) { const src = iframe.src || ''; if (src.startsWith('javascript:void') || src.includes('UE.instants') || src.includes('ueditorInstant')) { continue; } try { const subDoc = iframe.contentDocument; if (subDoc) { if (subDoc.body && subDoc.body.className === 'view') { continue; } const video = subDoc.querySelector('video'); if (video) { // 不设置 muted = true,因为防切屏设置会阻止静音 // video.muted = true; // 注释掉,与防切屏设置保持一致 video.volume = 0.01; // 设置极低音量 video.autoplay = true; if (video.paused && !video.ended) { const bigPlayBtn = subDoc.querySelector('.vjs-big-play-button'); const playControlBtn = subDoc.querySelector('.vjs-play-control'); if (bigPlayBtn && bigPlayBtn.offsetParent !== null) { bigPlayBtn.click(); } else if (playControlBtn && playControlBtn.offsetParent !== null) { playControlBtn.click(); } else { video.play().catch(() => {}); } } } const childFrames = findDeepestIframes(subDoc, depth + 1); if (childFrames.length > 0) { deepestFrames.push(...childFrames); } else { const frameType = classifyIframe(src, subDoc); if (frameType !== IFRAME_TYPES.UNKNOWN) { deepestFrames.push({ iframe, doc: subDoc, src: src, type: frameType, accessible: true, depth: depth }); } } } else { const frameType = classifyIframe(src, null); if (frameType !== IFRAME_TYPES.UNKNOWN) { deepestFrames.push({ iframe, doc: null, src: src, type: frameType, accessible: false, depth: depth }); } } } catch (e) { const frameType = classifyIframe(src, null); if (frameType !== IFRAME_TYPES.UNKNOWN) { deepestFrames.push({ iframe, doc: null, src: src, type: frameType, accessible: false, depth: depth, error: e.message }); } } } return deepestFrames; } function classifyIframe(src, doc) { if (src.startsWith('javascript:void') || src.includes('UE.instants') || src.includes('ueditorInstant') || (doc && doc.body && doc.body.className === 'view')) { return IFRAME_TYPES.UNKNOWN; } if (src.includes('mooc1.chaoxing.com/ananas/modules/video') || src.includes('ans-attach-online') || src.includes('insertvideo')) { return IFRAME_TYPES.VIDEO; } if (src.includes('mooc1.chaoxing.com/mooc-ans/api/work') || src.includes('/mooc-ans/work/doHomeWorkNew/') || src.includes('/ananas/modules/work/')) { return IFRAME_TYPES.CHAPTER_TEST; } if (src.includes('pan-yz.chaoxing.com/screen/file') || src.includes('/screen/file')) { return IFRAME_TYPES.DOCUMENT; } if (src.includes('mooc1.chaoxing.com/mooc-ans/bbscircle/chapter') || src.includes('/bbscircle/chapter') || src.includes('insertbbs/index.html') || src.includes('/modules/insertbbs/') || src.includes('discussion') || src.includes('comment')) { return IFRAME_TYPES.DISCUSSION; } if (doc) { try { if (doc.querySelector('video') || doc.querySelector('.vjs-big-play-button')) { console.log('📹 [分类] 通过DOM识别为视频iframe'); return IFRAME_TYPES.VIDEO; } if (doc.querySelector('.singleQuesId') || doc.querySelector('.CeYan h3') || doc.querySelector('.questionLi')) { console.log('📝 [分类] 通过DOM识别为章节测验iframe'); return IFRAME_TYPES.CHAPTER_TEST; } if (doc.querySelector('.ppt-container') || doc.querySelector('.document-viewer') || doc.querySelector('canvas')) { console.log('📄 [分类] 通过DOM识别为文档iframe'); return IFRAME_TYPES.DOCUMENT; } if (doc.querySelector('.bbs-content') || doc.querySelector('.discussion-area')) { console.log('💬 [分类] 通过DOM识别为讨论iframe'); return IFRAME_TYPES.DISCUSSION; } } catch (e) { console.log(`⚠️ [分类] DOM检查时出错: ${e.message}`); } } console.log('❓ [分类] 未知类型iframe'); return IFRAME_TYPES.UNKNOWN; } // === 新的清晰的内容处理系统 === // === 基于原版.js优化的内容处理系统 === async function processIframeContent() { // 检查是否有任务正在进行 if (GLOBAL_STATE.isAnswering || GLOBAL_STATE.isChapterTesting) { console.log('⏸️ [iframe处理] 已有任务在进行中,跳过'); return { processed: false, reason: 'busy' }; } // 查找最深层iframe const deepestFrames = findDeepestIframes(); if (deepestFrames.length === 0) { console.log('ℹ️ [iframe处理] 未找到任何iframe,尝试直接进入下一节'); const nextSectionResult = goToNextSection(); if (nextSectionResult) { console.log('✅ [iframe处理] 已点击下一节按钮'); return { processed: true, reason: 'no_frames_next_section', action: 'next_section' }; } else { console.log('❌ [iframe处理] 未找到下一节按钮'); return { processed: false, reason: 'no_frames_no_next' }; } } console.log(`🔍 找到 ${deepestFrames.length} 个最深层iframe`); deepestFrames.forEach(frame => { console.log(`📋 深度${frame.depth}: ${frame.type} - ${frame.src}`); }); // 按页面顺序逐个处理iframe内容 console.log('📋 [顺序处理] 按页面顺序逐个处理iframe内容'); for (let i = 0; i < deepestFrames.length; i++) { const frame = deepestFrames[i]; console.log(`📋 [顺序处理] 处理第${i + 1}个iframe: ${frame.type}`); switch (frame.type) { case IFRAME_TYPES.CHAPTER_TEST: const testResult = await handleChapterTest([frame]); if (testResult) { console.log(`✅ [顺序处理] 第${i + 1}个iframe(章节测验)处理完成,本轮结束`); return { processed: true, type: 'chapter_test' }; } break; case IFRAME_TYPES.VIDEO: const videoResult = handleVideoFrames([frame]); if (videoResult) { console.log(`✅ [顺序处理] 第${i + 1}个iframe(视频)处理完成,本轮结束`); return { processed: true, type: 'video' }; } break; case IFRAME_TYPES.DOCUMENT: const docResult = handleDocumentFrames([frame]); if (docResult) { console.log(`✅ [顺序处理] 第${i + 1}个iframe(文档)处理完成,本轮结束`); return { processed: true, type: 'document' }; } break; case IFRAME_TYPES.DISCUSSION: // 讨论区域直接标记为完成 const isCommentPage = frame.src.includes('insertbbs/index.html') || frame.src.includes('/modules/insertbbs/') || frame.src.includes('bbscircle/chapter') || frame.src.includes('discussion') || frame.src.includes('comment'); if (isCommentPage) { console.log(`💬 [顺序处理] 第${i + 1}个iframe确认为评论页面,标记为完成`); } else { console.log(`💬 [顺序处理] 第${i + 1}个iframe为普通讨论区域,标记为完成`); } console.log(`✅ [顺序处理] 第${i + 1}个iframe(讨论)处理完成,本轮结束`); return { processed: true, type: 'discussion' }; default: console.log(`❓ [顺序处理] 第${i + 1}个iframe类型未知(${frame.type}),跳过`); continue; } } // 无需处理的内容 console.log('ℹ️ [顺序处理] 无需处理的内容'); return { processed: false, type: 'none' }; } // === 基于多视频播放逻辑.js的顺序播放系统 === // 视频播放状态管理 const VIDEO_CONTROLLER = { isPlaying: false, // 当前是否有视频在播放 videoQueue: [], // 视频队列 currentVideoInfo: null, // 当前播放的视频信息 lastScanTime: 0 // 上次扫描时间 }; // 查找所有iframe中的视频(递归扫描) function findAllVideos() { console.log(`📹 [顺序播放] 开始扫描所有iframe中的视频`); VIDEO_CONTROLLER.videoQueue = []; function scanWindow(win, depth = 0, path = 'main') { try { // 先快速检查是否有视频元素,没有就直接返回 const videos = win.document.querySelectorAll('video'); const iframes = win.document.querySelectorAll('iframe'); if (videos.length === 0 && iframes.length === 0) { console.log(`📹 [扫描] ${path} 没有视频和iframe,跳过`); return; } // 应用防切屏设置(只在有视频或iframe时应用) if (videos.length > 0 || iframes.length > 0) { injectHooksToDocument(win.document, `video-scan-${path}`); } // 处理当前窗口中的视频 if (videos.length > 0) { console.log(`📹 [扫描] ${path} 发现 ${videos.length} 个视频元素`); videos.forEach((video, index) => { const videoInfo = { video: video, window: win, depth: depth, path: `${path}-video-${index}`, iframe: depth > 0 ? win.frameElement : null }; // 检查视频是否在视频容器中(任务点视频) const container = video.closest('.ans-attach-ct.videoContainer') || video.closest('.ans-attach-ct') || video.closest('[class*="video"]'); if (container) { videoInfo.isTaskVideo = true; videoInfo.container = container; // 检查是否已完成 const completedIcon = container.querySelector('.ans-job-icon.ans-job-finished'); videoInfo.isCompleted = !!completedIcon; } VIDEO_CONTROLLER.videoQueue.push(videoInfo); console.log(`📹 [扫描] 发现视频: ${videoInfo.path} (深度${depth}) ${videoInfo.isCompleted ? '✅已完成' : '⏳未完成'}`); }); } // 递归扫描iframe(只在有iframe时进行) if (iframes.length > 0) { console.log(`📹 [扫描] ${path} 发现 ${iframes.length} 个iframe,开始递归扫描`); iframes.forEach((iframe, index) => { try { const childWin = iframe.contentWindow; if (childWin && childWin.document) { scanWindow(childWin, depth + 1, `${path}-iframe-${index}`); } } catch (e) { console.log(`📹 [扫描] 无法访问iframe ${path}-iframe-${index},可能是跨域`); } }); } } catch (error) { console.warn(`📹 [扫描] 扫描窗口失败 ${path}:`, error.message); } } // 开始扫描主窗口 scanWindow(window); // 如果没有找到任何视频,直接返回 if (VIDEO_CONTROLLER.videoQueue.length === 0) { console.log(`📹 [扫描完成] 未发现任何视频元素`); return false; } // 过滤掉已完成的视频,按任务点视频优先排序 const uncompletedVideos = VIDEO_CONTROLLER.videoQueue.filter(info => !info.isCompleted); const taskVideos = uncompletedVideos.filter(info => info.isTaskVideo); const otherVideos = uncompletedVideos.filter(info => !info.isTaskVideo); VIDEO_CONTROLLER.videoQueue = [...taskVideos, ...otherVideos]; console.log(`📹 [扫描完成] 总计发现 ${VIDEO_CONTROLLER.videoQueue.length} 个未完成视频 (任务点视频: ${taskVideos.length}, 其他视频: ${otherVideos.length})`); return VIDEO_CONTROLLER.videoQueue.length > 0; } // 播放队列中的下一个视频 function playNextVideo() { if (VIDEO_CONTROLLER.isPlaying) { console.log(`📹 [顺序播放] 当前有视频正在播放,等待完成`); return false; } if (VIDEO_CONTROLLER.videoQueue.length === 0) { console.log(`📹 [顺序播放] 视频队列为空,重新扫描`); findAllVideos(); if (VIDEO_CONTROLLER.videoQueue.length === 0) { console.log(`📹 [顺序播放] 没有找到需要播放的视频`); return false; } } const videoInfo = VIDEO_CONTROLLER.videoQueue.shift(); const { video, path } = videoInfo; if (!video || video.ended) { console.log(`📹 [顺序播放] 视频 ${path} 无效或已结束,跳过`); return playNextVideo(); // 递归播放下一个 } try { VIDEO_CONTROLLER.isPlaying = true; VIDEO_CONTROLLER.currentVideoInfo = videoInfo; console.log(`▶️ [顺序播放] 开始播放视频: ${path}`); // 设置视频属性 video.volume = 0.01; // 极低音量 video.autoplay = true; // 应用倍速设置 applySavedSpeedToVideo(video); // 添加结束事件监听器(避免重复添加) if (!video._sequentialPlayListener) { video.addEventListener('ended', () => { console.log(`✅ [顺序播放] 视频 ${path} 播放完成`); VIDEO_CONTROLLER.isPlaying = false; VIDEO_CONTROLLER.currentVideoInfo = null; // 延迟播放下一个视频 setTimeout(() => { playNextVideo(); }, 1000); }); video.addEventListener('error', () => { console.log(`❌ [顺序播放] 视频 ${path} 播放出错`); VIDEO_CONTROLLER.isPlaying = false; VIDEO_CONTROLLER.currentVideoInfo = null; playNextVideo(); }); video._sequentialPlayListener = true; } // 尝试播放视频 const playPromise = video.play(); if (playPromise) { playPromise.then(() => { console.log(`✅ [顺序播放] 视频 ${path} 开始播放成功`); applySavedSpeedToVideo(video); showVideoSpeedPanel(); }).catch(error => { console.warn(`❌ [顺序播放] 视频 ${path} 播放失败:`, error.message); VIDEO_CONTROLLER.isPlaying = false; VIDEO_CONTROLLER.currentVideoInfo = null; playNextVideo(); }); } return true; } catch (error) { console.warn(`❌ [顺序播放] 播放视频 ${path} 时发生错误:`, error.message); VIDEO_CONTROLLER.isPlaying = false; VIDEO_CONTROLLER.currentVideoInfo = null; return playNextVideo(); } } // 主要的视频控制函数 function controlIframeVideo() { const now = Date.now(); // 快速检查页面是否有视频相关元素 const hasVideoElements = document.querySelector('video') || document.querySelector('iframe') || document.querySelector('.ans-attach-ct.videoContainer'); if (!hasVideoElements) { console.log(`📹 [视频控制] 页面没有视频相关元素,跳过扫描`); return false; } // 每30秒重新扫描一次视频 if (now - VIDEO_CONTROLLER.lastScanTime > 30000) { console.log(`📹 [视频控制] 定期重新扫描视频`); const foundVideos = findAllVideos(); VIDEO_CONTROLLER.lastScanTime = now; if (!foundVideos) { console.log(`📹 [视频控制] 重新扫描未发现视频`); return false; } } // 如果没有视频在播放,尝试播放下一个 if (!VIDEO_CONTROLLER.isPlaying) { return playNextVideo(); } else if (VIDEO_CONTROLLER.currentVideoInfo) { // 检查当前播放的视频状态 const { video, path } = VIDEO_CONTROLLER.currentVideoInfo; if (video.paused && !video.ended) { console.log(`⏸️ [视频控制] 检测到视频 ${path} 被暂停,尝试恢复播放`); video.play().catch(e => { console.warn(`❌ [视频控制] 恢复播放失败:`, e.message); }); } } return VIDEO_CONTROLLER.videoQueue.length > 0 || VIDEO_CONTROLLER.isPlaying; } // 删除复杂的视频容器控制函数,使用原版.js的简单方式 // 删除复杂的独立处理函数,在processIframeContent中直接调用原有的处理函数 async function handleChapterTest(testFrames) { for (const frame of testFrames) { if (!frame.accessible || !frame.doc) { console.log('❌ [章节测验] iframe不可访问,跳过'); continue; } const doc = frame.doc; const iframeWindow = frame.iframe.contentWindow; const completedStatus = doc.querySelector('.testTit_status_complete'); if (completedStatus && completedStatus.textContent.includes('已完成')) { console.log('✅ [章节测验] 检测到已完成状态,跳过答题'); return true; } const completedDiv = doc.querySelector('.fr.testTit_status.testTit_status_complete'); if (completedDiv && completedDiv.textContent.includes('已完成')) { console.log('✅ [章节测验] 检测到已完成状态(方式2),跳过答题'); return true; } chaoxingFontDecrypt(doc); const questions = doc.querySelectorAll('.singleQuesId'); console.log(`📄 共 ${questions.length} 道题目`); if (questions.length === 0) { continue; } GLOBAL_STATE.isChapterTesting = true; GLOBAL_STATE.isAnswering = true; console.log('🚀 [章节测验] 开始答题流程'); for (let i = 0; i < questions.length; i++) { const qEl = questions[i]; const typeText = qEl.querySelector('.newZy_TItle')?.innerText || '未知类型'; let content = qEl.querySelector('.fontLabel')?.innerText?.trim() || ''; content = content.replace(/【[^】]*题】/g, '').trim(); console.log(`📝 [${i + 1}/${questions.length}] ${typeText}`); const options = qEl.querySelectorAll('li'); const optionsData = []; const cleanQuestionType = typeText.replace(/【|】/g, ''); options.forEach(opt => { let spanElement = null; let label = ''; if (cleanQuestionType.includes('多选题')) { spanElement = opt.querySelector('span.num_option_dx'); } else { spanElement = opt.querySelector('span.num_option'); } label = spanElement?.innerText || ''; const aElement = opt.querySelector('a.after'); const text = aElement?.innerText || ''; const dataValue = spanElement?.getAttribute('data') || ''; if (label && text) { optionsData.push({ label: label, content: text, value: dataValue, element: opt, questionType: cleanQuestionType }); } }); try { const cleanQuestionType = typeText.replace(/【|】/g, ''); const questionData = { stem: content, questionType: cleanQuestionType, options: optionsData }; const apiResponse = await callCloudAPI(questionData); if (apiResponse && apiResponse.answer) { const answer = apiResponse.answer.trim(); console.log(` ✅ 答案: ${answer}`); console.log(`🎯 [答题] 选择答案: ${answer}`); if (cleanQuestionType.includes('填空题')) { console.log(`📝 [填空题] 开始处理填空题`); const blankItems = qEl.querySelectorAll('.blankItemDiv'); console.log(`📝 [填空题] 找到 ${blankItems.length} 个填空项`); let answerParts = []; if (typeof answer === 'string') { if (answer.includes('|')) { answerParts = answer.split('|'); } else if (answer.includes(';')) { answerParts = answer.split(';'); } else if (answer.includes(';')) { answerParts = answer.split(';'); } else if (answer.includes(',')) { answerParts = answer.split(','); } else if (answer.includes(',')) { answerParts = answer.split(','); } else { answerParts = [answer]; } } else { answerParts = [answer]; } console.log(`📝 [填空题] 答案分割结果:`, answerParts); blankItems.forEach((blankItem, blankIndex) => { if (blankIndex < answerParts.length) { const answerText = answerParts[blankIndex].trim(); console.log(`📝 [填空题] 第${blankIndex + 1}空填入: ${answerText}`); const editorTextarea = blankItem.querySelector('textarea[name*="answerEditor"]'); if (editorTextarea) { const editorId = editorTextarea.id; try { let fillSuccess = false; if (iframeWindow && iframeWindow.UE && iframeWindow.UE.getEditor) { try { const editor = iframeWindow.UE.getEditor(editorId); if (editor && editor.setContent) { editor.setContent(answerText); fillSuccess = true; } } catch (ueError) { } } if (!fillSuccess) { editorTextarea.value = answerText; const events = ['input', 'change', 'blur', 'keyup']; events.forEach(eventType => { try { const event = new (iframeWindow || window).Event(eventType, { bubbles: true }); editorTextarea.dispatchEvent(event); } catch (eventError) { const event = doc.createEvent('Event'); event.initEvent(eventType, true, true); editorTextarea.dispatchEvent(event); } }); fillSuccess = true; } if (!fillSuccess) { const inpDiv = blankItem.querySelector('.InpDIV'); if (inpDiv) { inpDiv.innerHTML = answerText; inpDiv.textContent = answerText; fillSuccess = true; } } } catch (error) { } } } }); } else if (cleanQuestionType.includes('判断题')) { for (const opt of options) { const text = opt.querySelector('a')?.innerText || ''; if ((answer === 'T' && (text === '对' || text === '正确' || text === '是')) || (answer === 'F' && (text === '错' || text === '错误' || text === '否'))) { opt.click(); console.log(` 🎯 已选择: ${text}`); break; } } } else if (cleanQuestionType.includes('多选题')) { for (const opt of options) { const spanElement = opt.querySelector('span.num_option_dx'); const label = spanElement?.innerText || ''; if (answer.includes(label)) { if (typeof iframeWindow.addMultipleChoice === 'function') { try { iframeWindow.addMultipleChoice(opt); console.log(` 🎯 已选择多选项: ${label}`); } catch (error) { opt.click(); console.log(` 🎯 已选择多选项: ${label} (备用)`); } } else { opt.click(); console.log(` 🎯 已选择多选项: ${label}`); } } } } else { for (const opt of options) { const spanElement = opt.querySelector('span.num_option'); const label = spanElement?.innerText || ''; if (answer.includes(label)) { opt.click(); console.log(` 🎯 已选择: ${label}`); break; } } } } } catch (error) { console.log(` ❌ 答题异常: ${error.message}`); } await new Promise(resolve => setTimeout(resolve, 1000)); } console.log('✅ [章节测验] 答题完成,准备提交'); GLOBAL_STATE.isAnswering = false; setTimeout(() => { if (iframeWindow.btnBlueSubmit) { iframeWindow.btnBlueSubmit(); console.log('✅ [自动提交] 测验提交成功'); setTimeout(() => { monitorSubmitDialog(); }, 500); } }, 2000); return true; } return false; } function handleVideoFrames(specificFrames = null) { console.log(`🎯 [统一视频控制] handleVideoFrames系统启动 - ${new Date().toLocaleTimeString()}`); let videoContainers; if (specificFrames && specificFrames.length > 0) { videoContainers = []; specificFrames.forEach(frame => { if (frame.iframe && frame.type === IFRAME_TYPES.VIDEO) { const container = frame.iframe.closest('.ans-attach-ct.videoContainer'); if (container) { videoContainers.push(container); } } }); console.log(`📹 [视频处理] 处理指定的 ${videoContainers.length} 个视频容器`); } else { const wrapContainer = document.querySelector('.wrap'); if (wrapContainer) { videoContainers = wrapContainer.querySelectorAll('.ans-attach-ct.videoContainer'); } else { videoContainers = document.querySelectorAll('.ans-attach-ct.videoContainer'); } console.log(`📹 [视频处理] 找到 ${videoContainers.length} 个视频容器,当前播放: ${GLOBAL_STATE.currentPlayingVideoIndex || '无'} (统一控制系统)`); } if (videoContainers.length === 0) { return false; } let processedAny = false; let playingVideoFound = false; const videoStates = []; videoContainers.forEach((container, index) => { const videoState = { index: index + 1, container: container, isCompleted: false, isPlaying: false, needsPlay: false }; try { if (container.classList.contains('ans-job-finished')) { videoState.isCompleted = true; const videoIframe = container.querySelector('iframe.ans-insertvideo-online'); if (videoIframe) { try { const videoDoc = videoIframe.contentDocument; if (videoDoc) { const video = videoDoc.querySelector('video'); if (video) { const alreadyProcessed = video.getAttribute('data-task-completed') === 'true'; if (!alreadyProcessed) { video.setAttribute('data-task-completed', 'true'); video.taskCompleted = true; if (!video.paused) { video.pause(); console.log(`⏸️ [视频${index + 1}] 已暂停播放 (任务点已完成)`); } // 不设置 muted = true,因为防切屏设置会阻止静音 // video.muted = true; // 注释掉,与防切屏设置保持一致 video.volume = 0.01; // 设置极低音量 video.autoplay = true; } } } } catch (e) { } } } else { const taskIcon = container.querySelector('.ans-job-icon'); if (taskIcon) { const ariaLabel = taskIcon.getAttribute('aria-label'); if (ariaLabel && ariaLabel.includes('已完成')) { videoState.isCompleted = true; const videoIframe = container.querySelector('iframe.ans-insertvideo-online'); if (videoIframe) { try { const videoDoc = videoIframe.contentDocument; if (videoDoc) { const video = videoDoc.querySelector('video'); if (video) { const alreadyProcessed = video.getAttribute('data-task-completed') === 'true'; if (!alreadyProcessed) { video.setAttribute('data-task-completed', 'true'); video.taskCompleted = true; if (!video.paused) { video.pause(); console.log(`⏸️ [视频${index + 1}] 已暂停播放 (任务点已完成)`); } // 不设置 muted = true,因为防切屏设置会阻止静音 // video.muted = true; // 注释掉,与防切屏设置保持一致 video.volume = 0.01; // 设置极低音量 video.autoplay = true; } } } } catch (e) { } } } else { const videoIframe = container.querySelector('iframe.ans-insertvideo-online'); if (videoIframe) { try { const videoDoc = videoIframe.contentDocument; if (videoDoc) { const video = videoDoc.querySelector('video'); if (video) { if (!video.paused && !video.ended) { videoState.isPlaying = true; playingVideoFound = true; console.log(`▶️ [视频${index + 1}] 正在播放中`); } else if (video.paused && !video.ended && video.readyState >= 2) { videoState.needsPlay = true; console.log(`⏸️ [视频${index + 1}] 已暂停,需要播放`); } else if (video.readyState < 2) { console.log(`⏳ [视频${index + 1}] 正在加载中`); } } } } catch (e) { console.log(`❌ [视频${index + 1}] 跨域无法访问`); } } } } else { console.log(`❓ [视频${index + 1}] 未找到任务点图标,标记为需要跳过`); videoState.noTaskIcon = true; } } } catch (error) { console.log(`❌ [视频${index + 1}] 检查失败:`, error.message); } videoStates.push(videoState); }); const videosWithoutTaskIcon = videoStates.filter(state => state.noTaskIcon); if (videosWithoutTaskIcon.length > 0) { console.log(`❓ [视频处理] 没有任务点图标,标记为完成,让主循环控制跳转`); return true; } if (playingVideoFound) { return true; } // 实现顺序播放:只播放第一个未完成的视频 const firstUncompletedVideo = videoStates.find(state => state.needsPlay); if (firstUncompletedVideo) { // 检查是否已有视频在播放 if (GLOBAL_STATE.currentPlayingVideoIndex !== null && GLOBAL_STATE.currentPlayingVideoIndex !== firstUncompletedVideo.index) { console.log(`⏸️ [视频控制] 视频${GLOBAL_STATE.currentPlayingVideoIndex}正在播放,等待完成`); return true; // 等待当前视频完成 } // 暂停所有其他正在播放的视频 videoStates.forEach(state => { if (state.isPlaying && state.index !== firstUncompletedVideo.index) { pauseVideo(state.container, state.index); } }); console.log(`▶️ [视频处理] 开始播放视频${firstUncompletedVideo.index} (顺序播放)`); const success = playVideo(firstUncompletedVideo.container, firstUncompletedVideo.index); if (success) { GLOBAL_STATE.currentPlayingVideoIndex = firstUncompletedVideo.index; processedAny = true; } } // 显示视频队列状态 if (videoStates.length > 0) { const completed = videoStates.filter(s => s.isCompleted).length; const playing = videoStates.filter(s => s.isPlaying).length; const needsPlay = videoStates.filter(s => s.needsPlay).length; console.log(`📊 [视频状态] 总计:${videoStates.length} | 已完成:${completed} | 播放中:${playing} | 待播放:${needsPlay}`); // 删除调试信息调用,保持简洁 } return processedAny; } function playVideo(container, videoIndex) { try { const videoIframe = container.querySelector('iframe.ans-insertvideo-online'); if (!videoIframe) { console.log(`❌ [视频${videoIndex}] 未找到视频iframe`); return false; } showVideoSpeedPanel(); let videoDoc = null; try { videoDoc = videoIframe.contentDocument; } catch (e) { console.log(`❌ [视频${videoIndex}] 无法访问iframe内容,跨域限制`); return false; } if (!videoDoc) { console.log(`❌ [视频${videoIndex}] iframe文档为空`); return false; } const video = videoDoc.querySelector('video'); const playButton = videoDoc.querySelector('.vjs-big-play-button'); if (!video && !playButton) { console.log(`❌ [视频${videoIndex}] 未找到视频元素`); return false; } if (video) { // 不设置 muted = true,因为防切屏设置会阻止静音 // video.muted = true; // 注释掉,与防切屏设置保持一致 video.volume = 0.01; // 设置极低音量 video.autoplay = true; // 添加视频结束事件监听,自动播放下一个视频 if (!video.hasSequentialListener) { video.addEventListener('ended', () => { console.log(`✅ [视频${videoIndex}] 播放完成,准备播放下一个视频`); // 清除当前播放状态 if (GLOBAL_STATE.currentPlayingVideoIndex === videoIndex) { GLOBAL_STATE.currentPlayingVideoIndex = null; } setTimeout(() => { // 触发下一轮视频检查 handleVideoFrames(); }, 1000); }); video.hasSequentialListener = true; } if (video.paused && !video.ended) { const playButtons = [ videoDoc.querySelector('.vjs-big-play-button'), videoDoc.querySelector('.vjs-play-control'), playButton ].filter(btn => btn && btn.offsetParent !== null); if (playButtons.length > 0) { playButtons[0].click(); console.log(`✅ [视频${videoIndex}] 通过点击播放按钮开始播放(非静音,1%音量)`); console.log(`🔍 [视频${videoIndex}] 使用的按钮选择器:`, playButtons[0].className); // 确保其他视频被暂停 pauseAllOtherVideos(videoIndex); setTimeout(() => { applySavedSpeedToVideo(video); }, 500); } else { video.play().then(() => { console.log(`✅ [视频${videoIndex}] 直接播放成功(非静音,1%音量)`); // 确保其他视频被暂停 pauseAllOtherVideos(videoIndex); applySavedSpeedToVideo(video); }).catch((error) => { console.log(`❌ [视频${videoIndex}] 自动播放被阻止,需要用户交互: ${error.message}`); console.log(`💡 [视频${videoIndex}] 建议:在控制台执行 document.querySelector(".vjs-big-play-button").click()`); }); } return true; } } return false; } catch (error) { console.log(`❌ [视频${videoIndex}] 播放失败:`, error.message); return false; } } // 暂停指定视频 function pauseVideo(container, videoIndex) { try { const videoIframe = container.querySelector('iframe.ans-insertvideo-online'); if (!videoIframe) { return false; } let videoDoc = null; try { videoDoc = videoIframe.contentDocument; } catch (e) { return false; } if (!videoDoc) { return false; } const video = videoDoc.querySelector('video'); if (video && !video.paused) { video.pause(); console.log(`⏸️ [视频${videoIndex}] 已暂停播放 (顺序控制)`); // 清除全局播放状态 if (GLOBAL_STATE.currentPlayingVideoIndex === videoIndex) { GLOBAL_STATE.currentPlayingVideoIndex = null; } return true; } return false; } catch (error) { console.log(`❌ [视频${videoIndex}] 暂停失败:`, error.message); return false; } } // 删除旧的复杂视频控制函数 // 删除复杂的视频暂停控制函数 // === 基于多视频播放逻辑.js的顺序播放系统 === /* 优化总结: 1. 🎯 核心设计理念: - 严格的视频顺序播放:一次只播放一个视频 - 使用队列管理:videoQueue 确保播放顺序 - 状态控制:isPlaying 标志防止同时播放 2. 📹 视频处理流程: - findAllVideos(): 递归扫描所有iframe中的视频 - playNextVideo(): 从队列中取出下一个视频播放 - 视频结束后自动播放下一个 - 任务点视频优先播放 3. 🛡️ 防切屏增强: - 主页面防切屏设置 - 扫描时自动应用iframe防切屏设置 - 事件监听器拦截 4. 🔄 智能管理: - 定期重新扫描视频(30秒) - 自动跳过已完成的视频 - 暂停恢复机制 - 错误处理和重试 5. 📊 解决的问题: - ✅ 多视频同时播放冲突 - ✅ 视频播放顺序混乱 - ✅ 任务点视频优先级 - ✅ 播放状态管理 */ function handleDocumentFrames(docFrames) { if (!docFrames || docFrames.length === 0) { return false; } console.log(`📄 [文档处理] 开始处理 ${docFrames.length} 个文档iframe`); let processedAny = false; docFrames.forEach((frame, index) => { if (!frame.accessible || !frame.doc) { console.log(`❌ [文档${index + 1}] iframe不可访问,跳过`); return; } try { const doc = frame.doc; const iframeWindow = frame.iframe.contentWindow; const isPPT = frame.src.includes('pan-yz.chaoxing.com/screen/file') || frame.src.includes('/screen/file') || doc.querySelector('.ppt-container') || doc.querySelector('.document-viewer') || doc.querySelector('canvas'); if (isPPT) { console.log(`📄 [PPT${index + 1}] 开始自动滚动`); startPPTAutoScroll(doc, iframeWindow, index + 1); processedAny = true; } else { processedAny = true; } } catch (error) { console.log(`❌ [文档${index + 1}] 处理失败:`, error.message); } }); return processedAny; } function startPPTAutoScroll(doc, iframeWindow, docIndex) { if (!doc || !iframeWindow) { return; } const fileBox = doc.querySelector('.fileBox'); const pageItems = doc.querySelectorAll('.fileBox ul li'); if (!fileBox || pageItems.length === 0) { console.log(`❌ PPT${docIndex} 未找到PPT页面结构`); return; } console.log(`📄 PPT${docIndex} 找到${pageItems.length}页内容`); const autoScrollToBottom = () => { try { const docHeight = Math.max( doc.body.scrollHeight, doc.body.offsetHeight, doc.documentElement.clientHeight, doc.documentElement.scrollHeight, doc.documentElement.offsetHeight ); console.log(`📏 PPT${docIndex} 文档高度: ${docHeight}px`); iframeWindow.scrollTo({ top: docHeight, behavior: 'smooth' }); setTimeout(() => { doc.documentElement.scrollTop = docHeight; doc.body.scrollTop = docHeight; const scrollEvent = new iframeWindow.Event('scroll', { bubbles: true }); iframeWindow.dispatchEvent(scrollEvent); console.log(`📜 PPT${docIndex} 滚动到底部完成`); }, 1000); setTimeout(() => { const currentScrollTop = Math.max( doc.documentElement.scrollTop || 0, doc.body.scrollTop || 0 ); const windowHeight = iframeWindow.innerHeight || doc.documentElement.clientHeight; const isAtBottom = (currentScrollTop + windowHeight) >= (docHeight - 10); console.log(`✅ PPT${docIndex} 滚动检查: ${isAtBottom ? '已到底部' : '未到底部'}`); if (isAtBottom) { try { if (iframeWindow.parent && iframeWindow.parent !== iframeWindow) { iframeWindow.parent.postMessage({ isFinished: 1 }, '*'); console.log(`📤 PPT${docIndex} 发送完成消息`); } } catch (e) { console.log(`⚠️ PPT${docIndex} 发送完成消息失败:`, e.message); } console.log(`🎉 PPT${docIndex} 浏览完成,等待主循环检测任务完成状态`); } }, 2000); } catch (error) { console.log(`❌ PPT${docIndex} 滚动失败:`, error.message); } }; setTimeout(() => { autoScrollToBottom(); }, 500); } function checkTaskCompletion() { const taskIcons = document.querySelectorAll('.ans-job-icon'); let totalTasks = 0; let completedTasks = 0; taskIcons.forEach((icon) => { totalTasks++; const ariaLabel = icon.getAttribute('aria-label') || ''; if (ariaLabel.includes('已完成')) { completedTasks++; } }); if (totalTasks === 0) { const iframes = document.querySelectorAll('iframe'); const hasContent = iframes.length > 0; if (!hasContent) { console.log('ℹ️ [任务检查] 页面无任务点图标且无iframe内容,认为已完成'); return { total: 0, completed: 0, isAllCompleted: true, details: [] }; } else { console.log('ℹ️ [任务检查] 页面无任务点图标但有iframe内容,检查处理状态'); if (!GLOBAL_STATE.isAnswering && !GLOBAL_STATE.isChapterTesting) { console.log('ℹ️ [任务检查] 非答题状态,可能内容已完成,认为页面已完成'); return { total: 1, completed: 1, isAllCompleted: true, details: [] }; } } } const isAllCompleted = totalTasks > 0 && completedTasks === totalTasks; return { total: totalTasks, completed: completedTasks, isAllCompleted: isAllCompleted, details: [] }; } function goToNextSection() { console.log('🔍 [下一节] 开始查找下一节按钮...'); const isInIframe = window !== window.top; const targetDoc = isInIframe ? window.top.document : document; try { const nextButton = targetDoc.querySelector('#prevNextFocusNext'); console.log('🔍 [下一节] 在', isInIframe ? '主页面' : '当前页面', '查找按钮:', !!nextButton); if (nextButton) { console.log('🔍 [下一节] 找到按钮,详情:', { id: nextButton.id, className: nextButton.className, display: nextButton.style.display, visible: nextButton.offsetParent !== null, onclick: nextButton.getAttribute('onclick'), textContent: nextButton.textContent?.trim() }); const isVisible = nextButton.offsetParent !== null; const isDisplayed = nextButton.style.display !== 'none'; if (isVisible && isDisplayed) { console.log('✅ [下一节] 找到可用的下一节按钮,准备点击'); setTimeout(() => { try { nextButton.click(); console.log('✅ [下一节] 已点击下一节按钮'); } catch (error) { console.log('❌ [下一节] 点击失败:', error); const onclickAttr = nextButton.getAttribute('onclick'); if (onclickAttr) { console.log('🔄 [下一节] 尝试执行onclick事件:', onclickAttr); try { const targetWindow = isInIframe ? window.top : window; targetWindow.eval(onclickAttr); console.log('✅ [下一节] onclick事件执行成功'); } catch (evalError) { console.log('❌ [下一节] onclick事件执行失败:', evalError); } } } }, 2000); return true; } else { console.log('❌ [下一节] 按钮不可用:', { visible: isVisible, displayed: isDisplayed }); } } else { console.log('❌ [下一节] 未找到 #prevNextFocusNext 按钮'); } } catch (error) { console.log('❌ [下一节] 访问主页面失败(跨域限制):', error); if (isInIframe) { console.log('🔄 [下一节] 尝试通过postMessage通信'); window.top.postMessage({ type: 'CLICK_NEXT_SECTION', selector: '#prevNextFocusNext' }, '*'); } } return false; } function isStudyPage() { const url = window.location.href; return url.includes('/knowledge/cards') && url.includes('mooc1.chaoxing.com') && url.includes('clazzid') && url.includes('courseid'); } if (isStudyPage()) { console.log(`🎯 [系统启动] 基于多视频播放逻辑.js的顺序播放系统`); console.log(`📹 [视频系统] controlIframeVideo() - 严格的视频顺序播放,一次只播放一个`); console.log(`📋 [内容系统] processIframeContent() - 顺序处理测验和文档`); console.log(`🛡️ [防切屏] 主页面防切屏设置已应用: document.hidden=${document.hidden}, document.visibilityState=${document.visibilityState}`); console.log(`✅ [顺序播放] 多视频顺序播放系统已启用,避免同时播放冲突`); console.log(`⚡ [性能优化] 添加快速检查机制,无视频元素时跳过扫描`); // 初始化视频扫描 setTimeout(() => { console.log(`🔍 [初始化] 开始首次视频扫描`); // 快速检查是否有视频相关元素 const hasVideoElements = document.querySelector('video') || document.querySelector('iframe') || document.querySelector('.ans-attach-ct.videoContainer'); if (!hasVideoElements) { console.log(`🔍 [初始化] 页面没有视频相关元素,跳过初始化扫描`); return; } const foundVideos = findAllVideos(); if (foundVideos && VIDEO_CONTROLLER.videoQueue.length > 0) { console.log(`🎬 [初始化] 发现 ${VIDEO_CONTROLLER.videoQueue.length} 个视频,开始顺序播放`); playNextVideo(); } else { console.log(`🔍 [初始化] 未发现需要播放的视频`); } }, 3000); let nextSectionTriggered = false; let lastTaskStatus = null; // 缓存上次的任务状态 let videoControlCount = 0; // 视频控制计数器 window.mainIntervalId = setInterval(async () => { // 不再调用controlIframeVideo,改为使用handleVideoFrames系统 // if (videoControlCount % 3 === 0) { // console.log(`🔄 [主循环] 执行视频控制 (第${videoControlCount + 1}次循环)`); // controlIframeVideo(); // } videoControlCount++; const taskStatus = checkTaskCompletion(); if (taskStatus.isAllCompleted && !nextSectionTriggered) { console.log('✅ [自动跳转] 所有任务已完成,跳转下一节'); nextSectionTriggered = true; goToNextSection(); return; } // 只在任务状态变化或首次运行时处理iframe内容 const taskStatusChanged = !lastTaskStatus || lastTaskStatus.isAllCompleted !== taskStatus.isAllCompleted || lastTaskStatus.completedCount !== taskStatus.completedCount; if (!taskStatus.isAllCompleted && !GLOBAL_STATE.isAnswering && !GLOBAL_STATE.isChapterTesting) { // 学习原版.js:每次循环都执行视频控制 controlIframeVideo(); // 只在状态变化时或每30秒处理一次iframe内容,避免频繁处理 if (taskStatusChanged || videoControlCount % 6 === 0) { const iframeResult = await processIframeContent(); if (iframeResult.reason === 'no_frames_next_section') { console.log('🔄 [自动跳转] 无内容需要处理,跳转下一节'); nextSectionTriggered = true; return; } if (iframeResult.processed) { console.log(`📝 [内容处理] 已处理: ${iframeResult.type},等待所有任务完成`); } } } // 更新任务状态缓存 lastTaskStatus = { ...taskStatus }; if (!taskStatus.isAllCompleted) { nextSectionTriggered = false; } }, 10000); // 调整为10秒间隔,进一步减少频繁调用 } if (window === window.top) { window.addEventListener('message', function (event) { if (event.data && event.data.type === 'CLICK_NEXT_SECTION') { console.log('📨 [postMessage] 收到iframe的下一节请求'); const nextButton = document.querySelector(event.data.selector); if (nextButton && nextButton.offsetParent !== null) { setTimeout(() => { nextButton.click(); console.log('✅ [postMessage] 已点击下一节按钮'); }, 1000); } } }); } })();