// ==UserScript== // @name ✨学习通AI助手极速版|不限次数永久使用 // @namespace http://tampermonkey.net/ // @version 2.1.2 // @description 无需配置,采用DeepSeek-Flash强力驱动,识题准、答题快、正确率高。支持章节/作业/考试页面,界面简洁稳定,持续适配平台更新。 // @author kail // @match *://*.chaoxing.com/* // @grant GM_xmlhttpRequest // @grant GM_addStyle // @grant GM_getResourceText // @grant GM_setValue // @grant GM_getValue // @connect api.116611.xyz // @resource typrMd5Lib https://116611.xyz/typr-md5.js // @resource fontTableData https://116611.xyz/table.json // @tag AI答题 // @tag 网课助手 // @tag 免费试用 // @tag 工具类 // @tag 在线教育 // @tag 超星学习通 // @tag 长期维护 // @antifeature payment 脚本存在第三方答题接口付费功能 // @icon data:image/png;base64,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 // @license All Rights Reserved // ==/UserScript== const systemConfig = { buildTime: new Date().toISOString(), environment: 'production' }; class Logger { constructor(prefix) { this.prefix = prefix || 'System'; this.levels = ['DEBUG', 'INFO', 'WARN', 'ERROR']; } log(level, message) { if (this.levels.includes(level)) { const timestamp = new Date().toISOString(); } } debug(msg) { this.log('DEBUG', msg); } info(msg) { this.log('INFO', msg); } warn(msg) { this.log('WARN', msg); } error(msg) { this.log('ERROR', msg); } } const logger = new Logger('CXHelper'); const browserCheck = { isChrome: () => /Chrome/.test(navigator.userAgent), isFirefox: () => /Firefox/.test(navigator.userAgent), isEdge: () => /Edge/.test(navigator.userAgent), isSafari: () => /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent) }; class PerformanceMonitor { constructor() { this.metrics = new Map(); this.startTimes = new Map(); } start(name) { this.startTimes.set(name, performance.now()); } end(name) { const startTime = this.startTimes.get(name); if (startTime) { const duration = performance.now() - startTime; this.metrics.set(name, duration); this.startTimes.delete(name); return duration; } return 0; } getMetric(name) { return this.metrics.get(name) || 0; } } const perfMonitor = new PerformanceMonitor(); const utils = { generateId: (length = 8) => { const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; let result = ''; for (let i = 0; i < length; i++) { result += chars.charAt(Math.floor(Math.random() * chars.length)); } return result; }, deepClone: (obj) => { if (obj === null || typeof obj !== 'object') return obj; if (obj instanceof Date) return new Date(obj.getTime()); if (obj instanceof Array) return obj.map(item => utils.deepClone(item)); if (typeof obj === 'object') { const cloned = {}; Object.keys(obj).forEach(key => { cloned[key] = utils.deepClone(obj[key]); }); return cloned; } }, debounce: (func, wait) => { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; }, throttle: (func, limit) => { let inThrottle; return function() { const args = arguments; const context = this; if (!inThrottle) { func.apply(context, args); inThrottle = true; setTimeout(() => inThrottle = false, limit); } } } }; class CacheManager { constructor(maxSize = 100) { this.cache = new Map(); this.maxSize = maxSize; } set(key, value, ttl = 300000) { // 默认5分钟过期 if (this.cache.size >= this.maxSize) { const firstKey = this.cache.keys().next().value; this.cache.delete(firstKey); } const item = { value: value, expiry: Date.now() + ttl }; this.cache.set(key, item); } get(key) { const item = this.cache.get(key); if (!item) return null; if (Date.now() > item.expiry) { this.cache.delete(key); return null; } return item.value; } clear() { this.cache.clear(); } } const cacheManager = new CacheManager(); logger.info('System initialization completed'); (function() { 'use strict'; (function(){ function _b64ToBytes(b64){ const bin = atob(b64); const out = new Uint8Array(bin.length); for(let i=0;ic-1); const __SALT = String.fromCharCode.apply(null, __S); const __ENC = "idiM9YBNCEBSu80fAcWJtglB3UgCOQSNSUCG2EFbmlWW7AyIJkSb/kgNWgCHsoEA"; function __getDSK(){ try { const step1 = __ENC.split('').reverse().join(''); const xored = _b64ToBytes(step1); const plainB64Bytes = _xor(xored, _strToBytes(__SALT)); const plainB64 = _bytesToStr(plainB64Bytes); return atob(plainB64); } catch (e) { return ''; } } window.__getDeepseekKey = __getDSK; })(); const DEEPSEEK_PROXY_BASE = 'https://api.116611.xyz'; const DEEPSEEK_MODEL = 'deepseek-chat'; const DEEPSEEK_API_URL = `${DEEPSEEK_PROXY_BASE}/v1/chat/completions`; function deepseekChat(messages, options = {}) { return new Promise((resolve, reject) => { try { GM_xmlhttpRequest({ method: 'POST', url: DEEPSEEK_API_URL, headers: { 'Content-Type': 'application/json', 'X-App-Token': __getDSAuth() }, data: JSON.stringify({ model: DEEPSEEK_MODEL, messages, ...options }), timeout: 60000, onload: (r) => { if (r.status >= 200 && r.status < 300) { try { resolve(JSON.parse(r.responseText)); } catch (e) { reject(e); } } else { reject(new Error(`HTTP ${r.status}: ${String(r.responseText || '').slice(0,200)}`)); } }, onerror: () => reject(new Error('Network error')), ontimeout: () => reject(new Error('Timeout')) }); } catch (err) { reject(err); } }); } let isAnswering = false; let isStudyingChapters = false; let studyIntervalId = null; const PANEL_REG_KEY = 'cx_helper_active_panel_v2'; const PANEL_INSTANCE_ID = `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`; const FRAME_DEPTH = (() => { let d = 0; try { let w = window; while (w !== w.top) { d++; w = w.parent; } } catch { d = 999; } return d; })(); let isActiveOwner = false; let createdPanelEl = null; const HEARTBEAT_INTERVAL_MS = 2000; const STALE_MS = 7000; let heartbeatTimerId = null; let lastAutoSkipTs = 0; let emptyChecksCount = 0; let lastEmptySectionKey = ''; let currentStudySectionKey = ''; let chapterQuizHandledSectionKey = ''; let chapterQuizEnteringSectionKey = ''; let chapterQuizEnterRetryAt = 0; function isChapterStudyFrameHostUrl(url) { return /^https:\/\/mooc1\.chaoxing\.com\/mycourse\/studentstudy\?chapterId(?:=|%3D)/.test(String(url || '')); } function isChapterStudyPageContext() { try { if (isChapterStudyFrameHostUrl(window.top.location.href)) return true; } catch {} try { if (isChapterStudyFrameHostUrl(location.href)) return true; } catch {} return false; } function shouldPreferOuterFramePanel() { try { return isChapterStudyFrameHostUrl(window.top.location.href); } catch { return isChapterStudyFrameHostUrl(location.href); } } function isPanelBlockedPage(url) { try { const u = new URL(String(url || location.href), location.origin); if (u.hostname === 'passport2.chaoxing.com') return true; if (u.hostname === 'www.chaoxing.com' && (u.pathname === '/' || u.pathname === '')) return true; } catch {} return false; } const API_BASE = 'https://116611.xyz'; const MONEY_YUAN = (() => { try { const b64 = 'OC4wMA=='; return atob(b64); } catch { return '8.00'; } })(); const PAY_NAME = 'xHelper 解锁/赞助'; const DEFAULT_PAY = 'alipay'; const LS_KEY_DEV_ID = 'cxhelper_device_id'; const LS_KEY_FREE = 'cxhelper_free_used'; const LS_KEY_LICENSED = 'cxhelper_licensed'; const POLL_MS_PAY = 3000; const POLL_MAX_PAY = 100; const getFreeLimit = (() => { let cached = null; return function() { if (cached != null) return cached; try { const b64 = 'JDEw'; const decoded = atob(b64); const n = parseInt(decoded.replace(/\D/g, ''), 10); cached = Number.isFinite(n) ? n : 10; } catch { cached = 10; } return cached; }; })(); function getDeviceIdPaid() { try { let id = ''; // Prefer cookie so all chaoxing subdomains share one deviceId. id = getCookie(LS_KEY_DEV_ID); if (!id) id = localStorage.getItem(LS_KEY_DEV_ID) || ''; if (!id) { id = ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c => (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)); } try { localStorage.setItem(LS_KEY_DEV_ID, id); } catch {} try { setCookie(LS_KEY_DEV_ID, id, 3650, '.chaoxing.com'); } catch {} return id; } catch { return 'dev_' + Math.random().toString(36).slice(2); } } const DEVICE_ID_PAID = getDeviceIdPaid(); function getCookie(key) { try { const name = key + '='; const parts = document.cookie.split(';'); for (let part of parts) { const s = part.trim(); if (s.indexOf(name) === 0) return decodeURIComponent(s.substring(name.length)); } return ''; } catch { return ''; } } function setCookie(key, value, days, domain) { try { const d = new Date(); d.setTime(d.getTime() + ((days || 365) * 24 * 60 * 60 * 1000)); const expires = 'expires=' + d.toUTCString(); const dm = domain ? ';domain=' + domain : ''; document.cookie = key + '=' + encodeURIComponent(value) + ';path=/' + ';' + expires + dm; } catch {} } function isLocallyLicensed() { try { const c = getCookie(LS_KEY_LICENSED); if (c === '1') return true; return localStorage.getItem(LS_KEY_LICENSED) === '1'; } catch { return false; } } function setLocallyLicensed() { try { localStorage.setItem(LS_KEY_LICENSED, '1'); } catch {} try { setCookie(LS_KEY_LICENSED, '1', 365, '.chaoxing.com'); } catch {} } function getFreeUsedCount() { try { return parseInt(localStorage.getItem(LS_KEY_FREE) || '0', 10) || 0; } catch { return 0; } } function incFreeUsedCount() { try { const n = getFreeUsedCount() + 1; localStorage.setItem(LS_KEY_FREE, String(n)); return n; } catch { return 0; } } async function checkLicensePaid(forceCloud = false) { if (isLocallyLicensed()) return true; if (!forceCloud) return false; try { const r = await fetch(`${API_BASE}/api/license/status?deviceId=${encodeURIComponent(DEVICE_ID_PAID)}`, { credentials: 'omit' }); const ct = (r.headers.get('content-type') || '').toLowerCase(); const data = ct.includes('application/json') ? await r.json() : await r.text(); const ok = !!(data && data.licensed); if (ok) setLocallyLicensed(); return ok; } catch { return false; } } async function startPaymentPaid(payType) { const win = window.open('', '_blank'); try { const resp = await fetch(`${API_BASE}/api/create`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: PAY_NAME, money: MONEY_YUAN, type: payType || DEFAULT_PAY, param: DEVICE_ID_PAID }) }); const html = await resp.text(); win.document.open(); win.document.write(html); win.document.close(); return true; } catch (e) { if (win) win.close(); alert('发起支付失败:' + e.message); return false; } } async function pollUntilLicensedPaid(onProgress) { for (let i = 0; i < POLL_MAX_PAY; i++) { if (onProgress) { try { onProgress(i); } catch {} } const ok = await checkLicensePaid(true); if (ok) { setLocallyLicensed(); return true; } await new Promise(r => setTimeout(r, POLL_MS_PAY)); } return false; } function showPayModalPaid(messageText) { return new Promise(resolve => { const mask = document.createElement('div'); mask.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.45);z-index:999998;backdrop-filter:blur(4px);'; const box = document.createElement('div'); box.style.cssText = 'position:fixed;left:50%;top:120px;transform:translateX(-50%);width:420px;background:#fff;border-radius:16px;box-shadow:0 20px 25px -5px rgba(0,0,0,0.1), 0 10px 10px -5px rgba(0,0,0,0.04);overflow:hidden;z-index:999999;font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Arial;'; const msg = messageText || '试用已用完,打赏8元永久解锁哦'; box.innerHTML = ( '
解锁全部功能(永久)
' + '
' + '
' + msg + '
' + '
' + '
' + '' + '' + '
' + '
' + '
点击"去支付"将打开收银台,完成后此处会自动检测。
' + '
' + '
' + '' + '' + '
' ); document.body.appendChild(mask); document.body.appendChild(box); const tip = box.querySelector('#cx_tip_paid'); const btnPay = box.querySelector('#cx_go_pay'); const btnCancel = box.querySelector('#cx_cancel_paid'); function close() { try { box.remove(); mask.remove(); } catch {} } btnCancel.onclick = () => { close(); resolve(false); }; btnPay.onclick = async () => { btnPay.disabled = true; btnPay.textContent = '打开收银台...'; const payType = (box.querySelector('input[name="cx_pay"]:checked') || {}).value || DEFAULT_PAY; const ok = await startPaymentPaid(payType); if (!ok) { btnPay.disabled = false; btnPay.textContent = '去支付'; return; } btnPay.textContent = '检测支付中...'; if (tip) tip.innerHTML = '已打开收银台,请完成支付,完成后此处会自动解锁...
💡 若无法打开支付页面请尝试连接手机热点网络'; const done = await pollUntilLicensedPaid(); if (done) { if (tip) tip.textContent = '支付成功,正在解锁...'; setLocallyLicensed(); try { updateTrialBadge(); } catch {} setTimeout(() => { close(); resolve(true); }, 500); } else { btnPay.disabled = false; btnPay.textContent = '去支付'; if (tip) tip.textContent = '未检测到支付完成,可重试或稍后再次打开本面板。'; } }; }); } function showFeedbackModal() { const mask = document.createElement('div'); mask.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.45);z-index:999998;backdrop-filter:blur(4px);'; const box = document.createElement('div'); box.style.cssText = 'position:fixed;left:50%;top:120px;transform:translateX(-50%);width:400px;background:#fff;border-radius:16px;box-shadow:0 20px 25px -5px rgba(0,0,0,0.1), 0 10px 10px -5px rgba(0,0,0,0.04);overflow:hidden;z-index:999999;font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Arial;'; box.innerHTML = ( '
意见反馈
' + '
' + '
' + '如果您在使用过程中遇到问题或有任何建议,欢迎通过以下方式联系我们:' + '
' + '
' + '
联系邮箱
' + '
3893504084@qq.com
' + '
点击邮箱地址即可复制
' + '
' + '
我们将会认真对待每一条反馈,并且尽快回复您的问题。您的建议是我们改进产品的重要动力!
' + '
' + '
' + '' + '
' ); document.body.appendChild(mask); document.body.appendChild(box); const closeBtn = box.querySelector('#feedback-close'); function close() { try { box.remove(); mask.remove(); } catch {} } closeBtn.onclick = close; mask.onclick = close; const emailDiv = box.querySelector('[onclick*="clipboard"]'); if (emailDiv) { emailDiv.addEventListener('click', function() { const originalText = this.innerHTML; this.innerHTML = '✅ 已复制到剪贴板'; this.style.color = '#2563eb'; setTimeout(() => { this.innerHTML = originalText; this.style.color = '#333'; }, 2000); }); } } let paymentCancelled = false; async function ensureAccessAllowed() { if (await checkLicensePaid()) return true; const used = getFreeUsedCount(); if (used < getFreeLimit()) { incFreeUsedCount(); try { updateTrialBadge(); } catch {} return true; } if (paymentCancelled) throw new Error('试用已用完,请解锁后继续使用'); const ok = await showPayModalPaid(); if (ok) { setLocallyLicensed(); try { updateTrialBadge(); } catch {} return true; } paymentCancelled = true; throw new Error('试用已用完,请解锁后继续使用'); } function getActivePanelRecord() { try { const raw = localStorage.getItem(PANEL_REG_KEY); return raw ? JSON.parse(raw) : null; } catch { return null; } } function setActivePanelRecord(rec) { try { localStorage.setItem(PANEL_REG_KEY, JSON.stringify(rec)); } catch {} } function clearActivePanelRecordIfOwner() { try { const cur = getActivePanelRecord(); if (cur && cur.id === PANEL_INSTANCE_ID) { localStorage.removeItem(PANEL_REG_KEY); } } catch {} } function shouldWeOwn(current) { const nowTs = Date.now(); if (!current) return { own: true, ts: nowTs }; if (!current.aliveTs || nowTs - current.aliveTs > STALE_MS) return { own: true, ts: nowTs }; const currentDepth = Number.isFinite(current.depth) ? current.depth : 0; if (shouldPreferOuterFramePanel()) { if (FRAME_DEPTH < currentDepth) return { own: true, ts: nowTs }; if (FRAME_DEPTH > currentDepth) return { own: false, ts: nowTs }; } else { if (FRAME_DEPTH > currentDepth) return { own: true, ts: nowTs }; if (FRAME_DEPTH < currentDepth) return { own: false, ts: nowTs }; } try { if (current.url && current.url !== location.href) return { own: true, ts: nowTs }; } catch {} if (FRAME_DEPTH === currentDepth && nowTs > (current.ts || 0)) return { own: true, ts: nowTs }; return { own: false, ts: nowTs }; } function claimOwnership() { const cur = getActivePanelRecord(); const decision = shouldWeOwn(cur); if (decision.own) { setActivePanelRecord({ id: PANEL_INSTANCE_ID, depth: FRAME_DEPTH, ts: decision.ts, aliveTs: Date.now(), url: location.href }); isActiveOwner = true; } else { isActiveOwner = false; } return isActiveOwner; } function startHeartbeat() { if (heartbeatTimerId) return; heartbeatTimerId = setInterval(() => { if (!isActiveOwner) return; const cur = getActivePanelRecord(); if (!cur || cur.id !== PANEL_INSTANCE_ID) { stopHeartbeat(); return; } cur.aliveTs = Date.now(); try { cur.url = location.href; } catch {} setActivePanelRecord(cur); }, HEARTBEAT_INTERVAL_MS); } function stopHeartbeat() { if (heartbeatTimerId) { clearInterval(heartbeatTimerId); heartbeatTimerId = null; } } const cleanupOwnership = () => { stopHeartbeat(); clearActivePanelRecordIfOwner(); }; window.addEventListener('beforeunload', cleanupOwnership); window.addEventListener('pagehide', cleanupOwnership); function destroyPanelAndStop() { try { if (studyIntervalId) { clearInterval(studyIntervalId); studyIntervalId = null; } isStudyingChapters = false; isAnswering = false; stopHeartbeat(); const panel = document.getElementById('answer-helper-panel'); if (panel && panel.parentNode) panel.parentNode.removeChild(panel); createdPanelEl = null; } catch {} } window.addEventListener('storage', (e) => { if (e.key !== PANEL_REG_KEY) return; if (isPanelBlockedPage(location.href)) return; const rec = getActivePanelRecord(); if (!rec) return; if (rec.id === PANEL_INSTANCE_ID) { if (!createdPanelEl) { try { createdPanelEl = createPanel(); bindPanelEvents(); } catch {} } isActiveOwner = true; startHeartbeat(); } else { isActiveOwner = false; destroyPanelAndStop(); } }); GM_addStyle(` /* Panel: Modern Light Theme */ #answer-helper-panel { --ah-panel-height: 340px; position: fixed; top: 24px; left: 24px; width: 320px; min-width: 320px; max-width: 320px; height: var(--ah-panel-height) !important; min-height: var(--ah-panel-height) !important; max-height: var(--ah-panel-height) !important; background: #ffffff; border: 1px solid rgba(0, 0, 0, 0.08); border-radius: 16px; padding: 0; z-index: 9999; box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.05); font-family: 'Inter', 'SF Pro Display', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; color: #1f2937; user-select: none; overflow: hidden; transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s ease, height 0.3s ease; } /* Header */ #answer-helper-header { cursor: move; height: 52px; padding: 0 18px; display: flex; align-items: center; justify-content: space-between; background: #ffffff; border-bottom: 1px solid #f3f4f6; font-size: 15px; color: #111827; } #answer-helper-header .title { display: inline-flex; align-items: center; gap: 10px; font-weight: 700; color: #111827; } #answer-helper-header .title .accent { display: inline-block; width: 10px; height: 10px; border-radius: 50%; background: #6366f1; box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.15); } #answer-helper-header .title .version { font-size: 11px; font-weight: 600; line-height: 16px; color: #6366f1; background: rgba(99, 102, 241, 0.1); border-radius: 6px; padding: 1px 6px; transform: translateY(-1px); } #answer-helper-header .cx-header-actions { display: inline-flex; align-items: center; gap: 10px; } #answer-helper-header .cx-collapse-btn { width: 32px; height: 32px; border-radius: 8px; background: #f9fafb; border: 1px solid #f3f4f6; cursor: pointer; color: #6b7280; display: flex; align-items: center; justify-content: center; transition: all 0.2s ease; } #answer-helper-header .cx-collapse-btn:hover { background: #f3f4f6; color: #111827; border-color: #e5e7eb; transform: scale(1.05); } .cx-collapse-btn-inner { width: 16px; height: 16px; position: relative; } .cx-collapse-icon-bar.horizontal { width: 14px; height: 2px; background: currentColor; border-radius: 1px; position: absolute; left: 1px; top: 7px; } .cx-collapse-icon-bar.vertical { width: 2px; height: 14px; background: currentColor; border-radius: 1px; position: absolute; left: 7px; top: 1px; } /* Content */ #answer-helper-content { padding: 12px 16px; overflow-y: auto; height: calc(var(--ah-panel-height) - 52px); max-height: calc(var(--ah-panel-height) - 52px); } #answer-helper-panel.collapsed #answer-helper-content { display: none; } #answer-helper-panel.collapsed { width: 240px; min-width: 240px; max-width: 240px; height: 52px !important; min-height: 52px !important; border-radius: 12px; } /* Body: main on top, actions at bottom */ #answer-helper-content .panel-body { display: grid; grid-template-columns: 1fr; grid-template-rows: auto auto; grid-template-areas: "main" "actions"; gap: 12px; align-items: start; } .panel-main { min-width: 0; grid-area: main; } .panel-actions { grid-area: actions; } .panel-actions.panel-row { display: flex; flex-direction: column; gap: 10px; } .panel-actions.panel-row .primary-pair { display: flex; gap: 10px; width: 100%; } .panel-actions.panel-row .primary-pair .pair-slot { flex: 1; display: flex; } .panel-actions.panel-row .primary-pair .pair-slot .ah-btn { flex: 1; min-height: 44px; border-radius: 12px; } .panel-actions.panel-row .ah-btn { min-height: 40px; padding: 8px 12px; border-radius: 10px; } /* Toast */ #no-task-toast { position: fixed; top: 24px; right: 24px; background: #ffffff; border: 1px solid rgba(0,0,0,0.05); color: #111827; padding: 12px 20px; border-radius: 12px; font-size: 14px; font-weight: 500; z-index: 10000; box-shadow: 0 10px 15px -3px rgba(0,0,0,0.1); animation: fadeInOut 3s cubic-bezier(0.4, 0, 0.2, 1) forwards; } @keyframes fadeInOut { 0% { opacity: 0; transform: translateY(-20px) scale(0.95); } 10% { opacity: 1; transform: translateY(0) scale(1); } 90% { opacity: 1; transform: translateY(0) scale(1); } 100% { opacity: 0; transform: translateY(-20px) scale(0.95); } } /* Terminal Log */ #answer-log { height: 140px; overflow-y: auto; border: 1px solid #f3f4f6; border-radius: 12px; padding: 10px; margin-bottom: 8px; font-size: 12px; line-height: 1.6; font-family: 'JetBrains Mono', 'Fira Code', 'Menlo', monospace; background: #f9fafb; color: #4b5563; } #answer-log::-webkit-scrollbar { width: 6px; } #answer-log::-webkit-scrollbar-track { background: transparent; } #answer-log::-webkit-scrollbar-thumb { background: #e5e7eb; border-radius: 10px; } #answer-log::-webkit-scrollbar-thumb:hover { background: #d1d5db; } .log-item { margin: 4px 0; padding: 6px 10px; border-radius: 8px; background: #ffffff; border: 1px solid #f3f4f6; box-shadow: 0 1px 2px rgba(0,0,0,0.02); } .success { color: #059669; border-left: 3px solid #10b981; } .error { color: #dc2626; border-left: 3px solid #ef4444; } .info { color: #374151; border-left: 3px solid #6b7280; } .debug { color: #6b7280; border-left: 3px solid #9ca3af; } /* Buttons */ .ah-btn { flex: 1; padding: 8px 14px; border: 1px solid #e5e7eb; border-radius: 10px; font-size: 13px; font-weight: 600; cursor: pointer; transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); display: flex; align-items: center; justify-content: center; gap: 6px; color: #374151; background: #ffffff; } .ah-btn:hover { background: #f9fafb; border-color: #d1d5db; transform: translateY(-1px); box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05); } .ah-btn:active { transform: translateY(0); box-shadow: none; } .ah-primary { background: #6366f1; color: #ffffff; border-color: #4f46e5; } .ah-primary:hover { background: #4f46e5; border-color: #4338ca; box-shadow: 0 4px 12px rgba(99, 102, 241, 0.3); } .ah-danger { background: #ffffff; color: #ef4444; border-color: #fee2e2; } .ah-danger:hover { background: #fef2f2; border-color: #fca5a5; } .ah-secondary { background: #f9fafb; color: #4b5563; border-color: #f3f4f6; } .ah-secondary:hover { background: #f3f4f6; border-color: #e5e7eb; } .ah-success { background: #10b981; color: #ffffff; border-color: #059669; } .ah-success:hover { background: #059669; box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3); } /* Speed buttons */ .speed-button { transition: all 0.2s ease; border-radius: 8px; } .speed-active { background: #6366f1 !important; color: #ffffff !important; border-color: #4f46e5 !important; } #playback-speed-controls.segmented { display: flex; gap: 6px; justify-content: flex-end; margin-top: 6px; } #playback-speed-controls.segmented .ah-btn { min-width: 60px; padding: 6px 8px; font-size: 12px; } /* Trial Badge */ #answer-helper-panel .cx-trial-badge { position: absolute; left: 16px; bottom: 12px; color: #9ca3af; font-size: 11px; } #answer-helper-panel .cx-trial-badge .trial-buy-btn { display: inline-block; margin-left: 8px; padding: 2px 8px; font-size: 11px; border: 1px solid #e0e7ff; background: #eef2ff; color: #4f46e5; border-radius: 6px; cursor: pointer; font-weight: 600; } #answer-helper-panel .cx-trial-badge .trial-buy-btn:hover { background: #e0e7ff; border-color: #c7d2fe; } `); function getScriptVersion() { try { if (typeof GM_info !== 'undefined' && GM_info) { if (GM_info.script && GM_info.script.version) return String(GM_info.script.version); if (GM_info.scriptMetaStr) { const m = /\/\/\s*@version\s+(\S+)/.exec(GM_info.scriptMetaStr); if (m) return m[1]; } } } catch (e) {} return ''; } function createPanel() { let isChapterPage = false; try { isChapterPage = window.top.location.href.startsWith('https://mooc1.chaoxing.com/mycourse/studentstudy?chapterId'); } catch (e) { // Fallback in case of cross-origin frame access error isChapterPage = location.href.startsWith('https://mooc1.chaoxing.com/mycourse/studentstudy?chapterId'); } const panel = document.createElement('div'); panel.id = 'answer-helper-panel'; panel.innerHTML = `
学习通AI助手
检测中...
`; document.body.appendChild(panel); try { panel.style.setProperty('--ah-panel-height', '340px'); } catch {} try { const versionEl = panel.querySelector('#cx-title-version'); const scriptVersion = getScriptVersion(); if (versionEl) { if (scriptVersion) { versionEl.textContent = 'v' + scriptVersion; } else { versionEl.style.display = 'none'; } } } catch {} try { const rect = panel.getBoundingClientRect(); const winW = window.innerWidth || document.documentElement.clientWidth; const winH = window.innerHeight || document.documentElement.clientHeight; const left = Math.max(12, Math.round((winW - rect.width) / 2)); let isQuizPage = false; try { forEachAllSameOriginDocs((doc) => { if (isQuizPage) return; if (isQuizPageDoc(doc)) isQuizPage = true; }); } catch {} const centerTop = Math.round((winH - rect.height) / 2); const offset = Math.round(winH * 0.1); const desiredTop = isQuizPage ? Math.round(winH * 0.08) : (centerTop - offset); const top = Math.max(12, Math.min(desiredTop, Math.max(12, winH - rect.height - 12))); panel.style.left = left + 'px'; panel.style.top = top + 'px'; panel.style.right = 'auto'; } catch {} let isDragging = false, offsetX = 0, offsetY = 0; const header = panel.querySelector('#answer-helper-header'); header.addEventListener('mousedown', function(e) { if (e.target.closest('.cx-collapse-btn')) return; isDragging = true; const rect = panel.getBoundingClientRect(); offsetX = e.clientX - rect.left; offsetY = e.clientY - rect.top; document.body.style.userSelect = 'none'; }); document.addEventListener('mousemove', function(e) { if (!isDragging) return; panel.style.left = (e.clientX - offsetX) + 'px'; panel.style.top = (e.clientY - offsetY) + 'px'; panel.style.right = 'auto'; }); document.addEventListener('mouseup', function() { isDragging = false; document.body.style.userSelect = ''; }); const collapseBtn = panel.querySelector('.cx-collapse-btn'); const collapseBtnInner = collapseBtn.querySelector('.cx-collapse-btn-inner'); collapseBtn.addEventListener('click', function() { panel.classList.toggle('collapsed'); collapseBtnInner.innerHTML = ''; if (panel.classList.contains('collapsed')) { const h = document.createElement('span'); h.className = 'cx-collapse-icon-bar horizontal'; const v = document.createElement('span'); v.className = 'cx-collapse-icon-bar vertical'; collapseBtnInner.appendChild(h); collapseBtnInner.appendChild(v); } else { const h = document.createElement('span'); h.className = 'cx-collapse-icon-bar horizontal'; collapseBtnInner.appendChild(h); } }); return panel; } function setPanelFixedHeight(px) { const panel = document.getElementById('answer-helper-panel'); if (!panel) return; const val = Math.max(200, Math.round(px || 0)) + 'px'; try { panel.style.setProperty('--ah-panel-height', val); } catch {} try { const rect = panel.getBoundingClientRect(); const winH = window.innerHeight || document.documentElement.clientHeight; const maxTop = Math.max(12, winH - rect.height - 12); if (rect.top > maxTop) panel.style.top = maxTop + 'px'; } catch {} } function bindPanelEvents() { document.getElementById('start-answer')?.addEventListener('click', () => { addLog('本助手仅供学习研究,请遵守课程与平台规则。', 'info'); addLog('开始自动答题...'); autoAnswer(); }); document.getElementById('pause-answer')?.addEventListener('click', () => { isAnswering = false; addLog('正在暂停自动答题...', 'info'); }); const startStudyBtn = document.getElementById('start-study'); const pauseStudyBtn = document.getElementById('pause-study'); if (startStudyBtn && pauseStudyBtn) { startStudyBtn.addEventListener('click', () => { addLog('本助手仅供学习研究,请遵守课程与平台规则。', 'info'); studySessionInitiator = true; publishStudySession(true); startStudyChapters(); }); pauseStudyBtn.addEventListener('click', () => { stopStudyChapters(); }); } document.getElementById('speed-1x')?.addEventListener('click', () => { setVideoPlaybackSpeed(1.0); }); document.getElementById('speed-1.5x')?.addEventListener('click', () => { setVideoPlaybackSpeed(1.5); if (isChapterStudyPageContext()) { addLog('⚠️ 章节视频请勿倍速观看,倍速观看可能导致账号异常哦', 'warning'); } }); document.getElementById('speed-2x')?.addEventListener('click', () => { setVideoPlaybackSpeed(2.0); if (isChapterStudyPageContext()) { addLog('⚠️ 章节视频请勿倍速观看,倍速观看可能导致账号异常哦', 'warning'); } }); updateSpeedButtonsState(); const buyBtn = document.getElementById('buy-license'); if (buyBtn) { buyBtn.addEventListener('click', async () => { try { await showPayModalPaid('免费试用,打赏8元永久解锁哦'); } catch (e) { addLog('打开支付弹窗失败: ' + (e && e.message ? e.message : e), 'error'); } }); } document.getElementById('cx-feedback-btn')?.addEventListener('click', () => { showFeedbackModal(); }); setTimeout(updateTrialBadge, 0); } function safeClick(el) { try { if (!el) return false; el.click(); el.dispatchEvent(new MouseEvent('click', { bubbles: true })); return true; } catch { return false; } } function forEachSameOriginFrame(callback) { const visit = (win) => { for (let i = 0; i < win.frames.length; i++) { const f = win.frames[i]; try { const doc = f.document || f.contentDocument; if (doc && doc.location && doc.location.href.includes('.chaoxing.com')) { callback(doc); visit(f); } } catch { } } }; try { callback(document); } catch {} try { visit(window); } catch {} } function forEachAllSameOriginDocs(callback) { const seen = new Set(); const visit = (win) => { if (!win || seen.has(win)) return; seen.add(win); try { const doc = win.document || win.contentDocument; if (doc) callback(doc); } catch {} try { const len = win.frames ? win.frames.length : 0; for (let i = 0; i < len; i++) { try { visit(win.frames[i]); } catch {} } } catch {} }; try { visit(window.top); } catch { visit(window); } } const QUESTION_CONTAINER_SELECTOR = '.question, .questionLi, .subject_item, .examPaper_subject, .questionContainer, .q-item, .subject_node, .ti-item, .exam-item, .TiMu, .singleQuesId'; function isNullPageDoc(doc) { try { const nullEl = doc.querySelector('.nullpage'); if (!nullEl) return false; const txt = (nullEl.textContent || '').trim(); return txt.includes('暂无内容'); } catch {} return false; } function isLikelyQuestionContainer(el) { try { if (!el) return false; if (el.closest('.translationBox, .func-pop, .colorPop, .nullpage')) return false; if (el.id === 'fanyaMarking') return false; if (el.querySelector('.singleQuesId, .questionLi') && !el.classList.contains('singleQuesId') && !el.classList.contains('questionLi')) return false; if (el.querySelector('.stem_answer, .subject_describe, .questionContent, .subject_stem, .type_title, .subject_type, .stem_type, [id^="answertype"], .Zy_TItle, ul.Zy_ulTop')) return true; if (el.querySelector('input[type="radio"], input[type="checkbox"], textarea, [id^="answerEditor"], iframe[id^="ueditor_"], div[contenteditable="true"]')) return true; const t = (el.textContent || '').trim(); if (/单选题|多选题|判断题|填空题|简答题|名词解释|论述题|计算题|完形填空|写作题|连线题|分录题/.test(t)) return true; } catch {} return false; } function isUtilityElement(el) { try { if (!el || !el.closest) return false; return !!el.closest('.translationBox, .func-pop, .colorPop, #markDiv, .code-tool-wrap'); } catch {} return false; } function isVisibleElement(el) { try { if (!el) return false; const win = (el.ownerDocument && el.ownerDocument.defaultView) || window; const st = win.getComputedStyle ? win.getComputedStyle(el) : null; if (st && (st.display === 'none' || st.visibility === 'hidden')) return false; const r = el.getBoundingClientRect ? el.getBoundingClientRect() : null; return !!(r && r.width > 0 && r.height > 0); } catch {} return true; } function getInteractiveInputs(doc) { try { const list = Array.from(doc.querySelectorAll('input[type="radio"], input[type="checkbox"], textarea, select, input[type="text"], div[contenteditable="true"], [id^="answerEditor"], iframe[id^="ueditor_"]')); return list.filter(el => !isUtilityElement(el) && isVisibleElement(el)); } catch {} return []; } function getQuestionContainers(doc) { try { if (!doc || isNullPageDoc(doc)) return []; const all = Array.from(doc.querySelectorAll(QUESTION_CONTAINER_SELECTOR)); return all.filter(isLikelyQuestionContainer); } catch {} return []; } function isQuizReviewDoc(doc) { try { if (!doc) return false; const hasQuizShell = !!doc.querySelector('.newTestCon, #RightCon .newTestTitle, form[name="form1"]'); if (!hasQuizShell) return false; const hasAnswerView = !!doc.querySelector('.newAnswerBx .myAnswer, .answerFont, .answerScore, .myAllAnswerBx'); if (!hasAnswerView) return false; const hasEditable = !!doc.querySelector('input[type="radio"], input[type="checkbox"], textarea:not([readonly]), input[type="text"]:not([readonly]), [id^="answerEditor"], iframe[id^="ueditor_"], div[contenteditable="true"]'); const hasSubmit = !!doc.querySelector('button[type="submit"], input[type="submit"], .submit-btn, [onclick*="submit"], .btnBlueSubmit'); return !hasEditable && !hasSubmit; } catch {} return false; } function isQuizEffectivelyCompletedDoc(doc) { try { if (!doc) return false; if (isQuizReviewDoc(doc)) return true; const pageText = String((doc.body && (doc.body.innerText || doc.body.textContent)) || '').replace(/\s+/g, ' ').trim(); if (/待批阅|已完成|已交|提交成功|成绩|得分|查看结果|答题结果|作答记录/.test(pageText)) { const hasSubmit = !!doc.querySelector('button[type="submit"], input[type="submit"], .submit-btn, [onclick*="submit"], .btnBlueSubmit'); if (!hasSubmit) return true; } if (isQuizPageDoc(doc) && !hasUnansweredQuestions(doc)) { const hasSubmit = !!doc.querySelector('button[type="submit"], input[type="submit"], .submit-btn, [onclick*="submit"], .btnBlueSubmit'); const hasEditable = !!doc.querySelector('input[type="radio"], input[type="checkbox"], textarea:not([readonly]), input[type="text"]:not([readonly]), [id^="answerEditor"], iframe[id^="ueditor_"], div[contenteditable="true"]'); if (!hasSubmit || !hasEditable) return true; } } catch {} return false; } async function waitForQuestionsRenderAny(timeoutMs = 8000) { const end = Date.now() + timeoutMs; while (Date.now() < end) { let hit = null; forEachAllSameOriginDocs((doc) => { if (hit) return; try { const qs = getQuestionContainers(doc); if (qs && qs.length > 0) hit = doc; } catch {} }); if (hit) return hit; await new Promise(r => setTimeout(r, 300)); } return null; } function scheduleSkipChapter(delay) { if (pendingSkipTimeoutId) { clearTimeout(pendingSkipTimeoutId); } pendingSkipTimeoutId = setTimeout(function() { pendingSkipTimeoutId = null; if (isStudyingChapters) skipChapter(); }, delay); } function skipChapter() { if (!isStudyingChapters) return; if (isSkippingChapter) return; if (Date.now() - lastChapterNavTs < 8000) { return; } addLog("跳过章节", 'info'); isSkippingChapter = true; lastChapterNavTs = Date.now(); processingBatch = false; arrayEchelon = []; autoSkipDone = false; stopProgressMonitor(); blankSectionChecks = 0; lastBlankSectionUrl = ''; var chapterNext = window.top.document.querySelector("#prevNextFocusNext"); if (chapterNext) { chapterNext.click(); } else { addLog("未找到 #prevNextFocusNext 元素", 'warning'); } setTimeout(function() { try { var tip = document.querySelector(".maskDiv.jobFinishTip.maskFadeOut"); if (tip) { var tipNextChapter = document.querySelector(".jb_btn.jb_btn_92.fr.fs14.nextChapter"); if (tipNextChapter) { tipNextChapter.click(); } } } catch(e) { addLog("未找到完成提示或下一章节按钮", 'warning'); } isSkippingChapter = false; }, 6000); } function videoHandler(entry) { var idocument = entry.document; addLog("处理视频任务中...", 'info'); var retries = 0; var maxRetries = 10; function tryFindVideo() { try { var video = idocument.querySelector("video"); var videoPlayButton = idocument.querySelector(".vjs-big-play-button"); var modalDialog = idocument.querySelector(".vjs-modal-dialog-content"); var closeButton = idocument.querySelector(".vjs-done-button"); if (!video || !videoPlayButton) { var iframes = idocument.querySelectorAll("iframe"); for (var i = 0; i < iframes.length; i++) { try { var innerDoc = iframes[i].contentDocument || iframes[i].contentWindow.document; video = innerDoc.querySelector("video"); videoPlayButton = innerDoc.querySelector(".vjs-big-play-button"); modalDialog = innerDoc.querySelector(".vjs-modal-dialog-content"); closeButton = innerDoc.querySelector(".vjs-done-button"); if (video && videoPlayButton) break; } catch(e) {} } } if (!video || !videoPlayButton) { retries++; if (retries < maxRetries) { setTimeout(tryFindVideo, 2000); } else { addLog("没有找到视频或播放按钮", 'error'); setTimeout(function() { document.dispatchEvent(dealEvent); }, 3000); } return; } video.muted = true; if (!Number.isNaN(video.playbackRate)) video.playbackRate = currentPlaybackSpeed; var playVideo = function() { if (video && typeof video.play === 'function') { video.muted = true; video.play().catch(function(error) { videoPlayButton.click(); }); } else { videoPlayButton.click(); } }; var clickPlayVideo = function() { if (videoPlayButton && typeof videoPlayButton.click === 'function') { videoPlayButton.click(); } else { video.muted = true; video.play().catch(function() { videoPlayButton.click(); }); } }; var closeModalDialog = function() { if (modalDialog && closeButton && modalDialog.getAttribute("aria-hidden") !== "true") { setTimeout(function() { closeButton.click(); }, 2000); } }; var handlePause = function() { if (video.ended) return; clickPlayVideo(); closeModalDialog(); }; video.addEventListener("pause", handlePause); video.addEventListener("ended", function() { addLog("视频任务已完成", 'success'); video.removeEventListener("pause", handlePause); setTimeout(function() { document.dispatchEvent(dealEvent); }, 500); }, { once: true }); setTimeout(function() { if (video.paused && !video.ended) { playVideo(); } }, 10000); playVideo(); } catch(e) { retries++; if (retries < maxRetries) { setTimeout(tryFindVideo, 2000); } else { addLog('视频处理出错: ' + e.message, 'error'); setTimeout(function() { document.dispatchEvent(dealEvent); }, 2000); } } } tryFindVideo(); } function handlePPTInDocument(doc) { try { const nextSelectors = ['.next', '.vc-next', '.reader-next', 'a[title="下一页"]', '.btn-next', '#next']; for (const sel of nextSelectors) { const btn = doc.querySelector(sel); if (btn && !btn.className.includes('disabled') && !btn.getAttribute('disabled')) { if (safeClick(btn)) { return true; } } } return false; } catch { return false; } } function findChapterQuizTab(doc) { try { const byTitle = doc.querySelector('li[title*="章节测验"], li[title*="测验"], a[title*="章节测验"], a[title*="测验"]'); if (byTitle) return byTitle; const byOnClick = Array.from(doc.querySelectorAll('li[onclick], a[onclick], button[onclick]')).find(el => { const oc = (el.getAttribute('onclick') || '').toString(); return oc.includes('changeDisplayContent') && (oc.includes('(2,2') || oc.includes(',2)')); }); if (byOnClick) return byOnClick; const candidates = Array.from(doc.querySelectorAll('li, a, button, [role="tab"], [role="option"]')); const textEl = candidates.find(el => /章节测验|测验/.test(((el.textContent || el.getAttribute('title') || '') + '').trim())); if (textEl) return textEl; } catch {} return null; } async function waitForQuestionsRender(doc, timeoutMs = 6000) { const end = Date.now() + timeoutMs; while (Date.now() < end) { try { const qs = getQuestionContainers(doc); if (qs.length > 0) return true; } catch {} await new Promise(r => setTimeout(r, 300)); } return false; } let TYPR_MD5_LIB, FONT_TABLE_DATA; try { TYPR_MD5_LIB = GM_getResourceText('typrMd5Lib'); FONT_TABLE_DATA = GM_getResourceText('fontTableData'); if (TYPR_MD5_LIB) { window.TYPR_MD5_LIB = TYPR_MD5_LIB; } if (FONT_TABLE_DATA) { window.FONT_TABLE_DATA = FONT_TABLE_DATA; } } catch (e) { console.error('加载外部资源失败:', e); TYPR_MD5_LIB = ''; FONT_TABLE_DATA = '{}'; } function injectConsoleDecryptCode(doc, timeoutMs = 3000) { return new Promise((resolve) => { try { const consoleCode = ` if (!window.Typr || !window.md5) { ${TYPR_MD5_LIB || window.TYPR_MD5_LIB || ''} } if (!window.chaoXingFontTable) { window.chaoXingFontTable = ${FONT_TABLE_DATA || window.FONT_TABLE_DATA || '{}'}; } const decryptChaoXingFont = async () => { const { Typr, md5, chaoXingFontTable: table } = window; const base64ToUint8Array = (base64) => { const data = atob(base64); const buffer = new Uint8Array(data.length); for (let i = 0; i < data.length; i++) { buffer[i] = data.charCodeAt(i); } return buffer; }; const styleElements = [...document.querySelectorAll('style')]; const cxStyle = styleElements.find(el => el.textContent.includes('font-cxsecret') ); if (!cxStyle) { return; } const fontData = cxStyle.textContent.match(/base64,([\\w\\W]+?)'/)[1]; const parsedFont = Typr.parse(base64ToUint8Array(fontData))[0]; const charMap = {}; for (let charCode = 19968; charCode < 40870; charCode++) { const glyph = Typr.U.codeToGlyph(parsedFont, charCode); if (!glyph) continue; const path = Typr.U.glyphToPath(parsedFont, glyph); const pathHash = md5(JSON.stringify(path)).slice(24); charMap[String.fromCharCode(charCode)] = String.fromCharCode(table[pathHash]); } document.querySelectorAll('.font-cxsecret').forEach(element => { let htmlContent = element.innerHTML; Object.entries(charMap).forEach(([encryptedChar, decryptedChar]) => { const regex = new RegExp(encryptedChar, 'g'); htmlContent = htmlContent.replace(regex, decryptedChar); }); element.innerHTML = htmlContent; element.classList.remove('font-cxsecret'); }); }; decryptChaoXingFont().catch(console.error); `; const beforeCnt = (() => { try { return doc.querySelectorAll('.font-cxsecret').length; } catch { return -1; } })(); let script = doc.createElement('script'); script.type = 'text/javascript'; let blobUrl = ''; try { const blob = new Blob([consoleCode], { type: 'text/javascript' }); blobUrl = (doc.defaultView || window).URL.createObjectURL(blob); script.src = blobUrl; } catch { script.textContent = consoleCode; } (doc.head || doc.documentElement).appendChild(script); script.onload = () => { try { if (blobUrl) (doc.defaultView || window).URL.revokeObjectURL(blobUrl); } catch {} }; const start = Date.now(); const timer = setInterval(() => { try { const cnt = doc.querySelectorAll('.font-cxsecret').length; if (cnt === 0 || (beforeCnt >= 0 && cnt < beforeCnt)) { clearInterval(timer); resolve(); return; } } catch {} if (Date.now() - start > timeoutMs) { clearInterval(timer); resolve(); } }, 200); } catch { resolve(); } }); } async function tryEnterQuizAndAnswer(contextDoc) { try { if (!isStudyingChapters) return false; if (isChapterQuizEnteringForCurrentSection()) return true; let targetDoc = null; let tabEl = null; forEachAllSameOriginDocs((doc) => { if (tabEl) return; const el = findChapterQuizTab(doc); if (el) { tabEl = el; targetDoc = doc; } }); if (!tabEl || !targetDoc) return false; addLog('正在进入章节测验...', 'info'); markChapterQuizEnteringForCurrentSection(12000); await new Promise((r) => { let pending = 0; let done = false; forEachAllSameOriginDocs((doc) => { pending++; injectConsoleDecryptCode(doc).finally(() => { if (--pending === 0 && !done) { done = true; r(); } }); }); if (pending === 0) r(); }); safeClick(tabEl); const qDoc = await waitForQuestionsRenderAny(4000); if (!isStudyingChapters) return false; if (!qDoc) { clearChapterQuizEnteringForCurrentSection(); markChapterQuizHandledForCurrentSection(); addLog('章节测验未检测到题目', 'warning'); return false; } clearChapterQuizEnteringForCurrentSection(); await injectConsoleDecryptCode(qDoc); if (!isStudyingChapters) return false; await autoAnswerInDocument(qDoc); addLog('章节测验已完成', 'success'); markChapterQuizHandledForCurrentSection(); return true; } catch (e) { clearChapterQuizEnteringForCurrentSection(); addLog(`进入章节测验失败: ${e.message}`, 'error'); return false; } } function hasActionableStudyContent(doc) { try { if (isNullPageDoc(doc)) return false; if (isQuizReviewDoc(doc)) return false; if (isQuizPageDoc(doc)) return true; if (doc.querySelector('#form1, .newTestCon, .TiMu, .singleQuesId, [id^="answertype"], .Zy_TItle, ul.Zy_ulTop')) return true; if (doc.querySelector('video, .video-js video')) return true; const docSelectors = [ '.ans-attach-ct', '.reader', '.ppt', '.ppt-play', '.vjs-control', '.vjs-big-play-button' ]; if (docSelectors.some(sel => !!doc.querySelector(sel))) return true; if (getQuestionContainers(doc).length > 0) return true; if (getInteractiveInputs(doc).length > 0) return true; const iframes = Array.from(doc.querySelectorAll('iframe')); if (iframes.some(f => { const src = (f.getAttribute('src') || '').toLowerCase(); return src.includes('mooc-ans') || src.includes('document') || src.includes('ppt') || src.includes('video') || src.includes('knowledgeid'); })) return true; } catch {} return false; } function hasUnansweredQuestions(doc) { try { if (isNullPageDoc(doc)) return false; if (isQuizReviewDoc(doc)) return false; const containers = getQuestionContainers(doc); for (const q of containers) { try { if (!isQuestionAnswered(q)) return true; } catch {} } const radios = getInteractiveInputs(doc).filter(el => el.tagName && el.tagName.toLowerCase() === 'input' && el.type === 'radio'); if (radios.length > 0) { const groups = new Map(); radios.forEach(r => { const k = r.name || `__radio_${Math.random()}`; if (!groups.has(k)) groups.set(k, []); groups.get(k).push(r); }); for (const [, list] of groups) { if (!list.some(r => r.checked)) return true; } } const texts = getInteractiveInputs(doc).filter(el => { const tag = (el.tagName || '').toLowerCase(); return tag === 'textarea' || (tag === 'input' && el.type === 'text') || (tag === 'div' && el.getAttribute('contenteditable') === 'true'); }); if (texts.length > 0) { if (texts.some(el => { if (el.tagName && el.tagName.toLowerCase() === 'div') return ((el.innerText || el.textContent || '').trim().length === 0); return ((el.value || '').trim().length === 0); })) return true; } if (doc.querySelector('[id^="answertype"], .TiMu, .singleQuesId, .Zy_TItle, ul.Zy_ulTop')) return !isQuizReviewDoc(doc); } catch {} return false; } function getDocPathname(doc) { try { return new URL(doc.location.href).pathname || ''; } catch {} return ''; } function getQueryParamFromUrl(url, key) { try { return new URL(String(url || ''), location.origin).searchParams.get(key) || ''; } catch {} return ''; } function buildSectionKeyFromUrl(url) { const chapterId = getQueryParamFromUrl(url, 'chapterId'); const knowledgeId = getQueryParamFromUrl(url, 'knowledgeId') || getQueryParamFromUrl(url, 'knowledgeid'); if (!chapterId && !knowledgeId) return ''; return `chapter:${chapterId || ''}|knowledge:${knowledgeId || ''}`; } function getCurrentStudySectionKey() { const urls = []; try { urls.push(window.top.location.href); } catch {} try { urls.push(window.location.href); } catch {} forEachAllSameOriginDocs((doc) => { try { urls.push(doc.location.href); } catch {} }); for (const url of urls) { const key = buildSectionKeyFromUrl(url); if (key) return key; } return ''; } function markChapterQuizHandledForCurrentSection() { const key = currentStudySectionKey || getCurrentStudySectionKey(); if (!key) return; chapterQuizHandledSectionKey = key; chapterQuizEnteringSectionKey = ''; chapterQuizEnterRetryAt = 0; } function isChapterQuizHandledForCurrentSection() { const key = currentStudySectionKey || getCurrentStudySectionKey(); if (!key) return false; return chapterQuizHandledSectionKey === key; } function markChapterQuizEnteringForCurrentSection(waitMs = 12000) { const key = currentStudySectionKey || getCurrentStudySectionKey(); if (!key) return; chapterQuizEnteringSectionKey = key; chapterQuizEnterRetryAt = Date.now() + waitMs; } function clearChapterQuizEnteringForCurrentSection() { chapterQuizEnteringSectionKey = ''; chapterQuizEnterRetryAt = 0; } function isChapterQuizEnteringForCurrentSection() { const key = currentStudySectionKey || getCurrentStudySectionKey(); if (!key) return false; if (chapterQuizEnteringSectionKey !== key) return false; if (Date.now() >= chapterQuizEnterRetryAt) { clearChapterQuizEnteringForCurrentSection(); return false; } return true; } function findNestedDoc(root, predicate, depth = 0) { if (!root || depth > 4) return null; try { if (predicate(root)) return root; } catch {} try { const iframes = Array.from(root.querySelectorAll('iframe')); for (const iframe of iframes) { try { const nextDoc = iframe.contentDocument || iframe.contentWindow?.document; const result = findNestedDoc(nextDoc, predicate, depth + 1); if (result) return result; } catch {} } } catch {} return null; } function handlePPTInDocument(doc) { try { const nextSelectors = ['.next', '.vc-next', '.reader-next', 'a[title="下一页"]', '.btn-next', '#next']; for (const sel of nextSelectors) { const btn = doc.querySelector(sel); if (btn && !btn.className.includes('disabled') && !btn.getAttribute('disabled')) { if (safeClick(btn)) { return true; } } } return false; } catch { return false; } } var arrayEchelon = []; var completedJobCache = new Set(); var studyPageDefaults = null; var studyPageTasks = null; var dealEvent = new Event("redeal", { bubbles: false, cancelable: false }); var testDom = null; var ifTest = 'take'; var testType = 'submit'; var taskInterval = 6000; var isMainInstance = false; var autoSkipDone = false; var progressMonitor = null; function getReaderMissionApiPaths(mission) { const type = String((mission && mission.type) || '').toLowerCase(); if (type === 'ppt' || type === 'pdf' || type === 'reader') { return ['/ananas/job/document', '/ananas/job/readv2', '/ananas/job']; } return ['/ananas/job/document']; } async function handleQuizTest(doc) { try { if (!doc) return; await injectConsoleDecryptCode(doc); if (isQuizEffectivelyCompletedDoc(doc)) { addLog('章节测验已处于完成状态', 'success'); return; } const handled = await autoAnswerQuizInDocument(doc); if (handled) { addLog('章节测验已完成', 'success'); } } catch (e) { addLog(`章节答题出错: ${e.message}`, 'error'); } } async function autoAnswerInDocument(rootDoc) { try { if (!isStudyingChapters) return false; if (isQuizPageDoc(rootDoc)) { if (!isStudyingChapters) return false; const ok = await autoAnswerQuizInDocument(rootDoc); if (ok) return true; } const questions = getQuestionContainers(rootDoc); if (questions.length === 0) return false; addLog(`章节内发现 ${questions.length} 个题目,自动作答...`, 'info'); for (let q of questions) { if (!isStudyingChapters) return false; const info = getQuestionInfo(q); if (!info || !info.question) continue; const ans = await getAnswer(info); if (ans) { fillAnswer(ans, q, info.type); await new Promise(r => setTimeout(r, 1000)); } } return true; } catch (e) { addLog(`章节答题出错: ${e.message}`, 'error'); return false; } } function isStudyPage() { return /mycourse\/studentstudy|mooc2-ans|knowledgeId|chapterId/.test(location.href); } function isStudyContentDoc(doc) { try { if (!doc) return false; var path = String((doc.location && doc.location.pathname) || ''); if (path.indexOf('/knowledge/cards') !== -1 || path.indexOf('/knowledge/start') !== -1) return true; if (doc.querySelector && doc.querySelector('.ans-attach-ct, .ans-job-ct, .ans-job-icon')) return true; } catch (e) {} return false; } function docHasTaskFrames(doc) { try { if (!doc) return false; var iframes = doc.querySelectorAll('iframe'); for (var i = 0; i < iframes.length; i++) { var f = iframes[i]; if (f.getAttribute('data')) return true; var src = f.getAttribute('src') || ''; if (!src || src.indexOf('javascript:') === 0) continue; if (/api\/work|modules\/(video|audio|ppt|doc|pptx|docx|pdf|innerbook)|video|audio|\.mp4/.test(src)) return true; } } catch (e) {} return false; } // ================= 兜底方案:新版章节页 iframe 识别 ================= // 参考同类脚本做法:不依赖 /knowledge/cards,直接扫描同源嵌套 iframe, // 用 iframe 的 src / data 判断任务类型,再交给现有处理器执行。 function isTaskIframeFinished(iframe, cdoc, src) { try { var el = iframe.parentElement; while (el) { if (el.classList && el.classList.contains('ans-job-finished')) return true; el = el.parentElement; } } catch(e) {} try { var icon = iframe.closest('.ans-job-icon') || (iframe.parentElement && iframe.parentElement.querySelector('.ans-job-icon')); if (icon && String(icon.getAttribute('aria-label') || '').indexOf('已完成') !== -1) return true; } catch(e) {} try { if (src && src.indexOf('api/work') !== -1) { var txt = (cdoc.documentElement && cdoc.documentElement.innerText) || ''; if (txt.indexOf('已完成') !== -1 || txt.indexOf('待批阅') !== -1) return true; } } catch(e) {} return false; } function classifyTaskIframe(iframe, src) { if (!src || src.indexOf('javascript:') === 0) return null; if (src.indexOf('api/work') !== -1 || /\/(work|mooc2\/work)\/(doTest|index|calcAnswer)/.test(src)) return 'workA'; if (src.indexOf('modules/video') !== -1 || /\/video\//.test(src) || /\.mp4(\?|$)/.test(src)) return '.mp4'; if (src.indexOf('modules/audio') !== -1 || /\.(mp3|wav)(\?|$)/.test(src)) return '.mp3'; if (/modules\/(ppt|pptx|doc|docx|pdf|innerbook)/.test(src) || /\.(pdf|ppt|pptx|doc|docx)(\?|$)/.test(src)) return '.pdf'; var dataStr = iframe.getAttribute('data'); if (dataStr) { try { var d = JSON.parse(dataStr); var t = String(d.type || d.worktype || d.resourceType || ''); if (t) { if (/mp4|video/i.test(t)) return '.mp4'; if (/mp3|audio/i.test(t)) return '.mp3'; if (/pdf|ppt|doc|book/i.test(t)) return '.pdf'; if (/work|exam|test|quiz|homework/i.test(t)) return 'workA'; } } catch(e) {} } return null; } // 深度扫描同源嵌套 iframe,返回 { pending: [...], total: 任务点总数 } function collectTasksFromIframes(rootDoc) { var pending = []; var total = 0; var seen = new Set(); var visit = function(d) { if (!d || seen.has(d)) return; seen.add(d); var iframes = []; try { iframes = d.querySelectorAll('iframe'); } catch(e) { return; } for (var i = 0; i < iframes.length; i++) { var f = iframes[i]; var cdoc = null; try { cdoc = f.contentDocument; } catch(e) { cdoc = null; } if (!cdoc) continue; var src = f.getAttribute('src') || f.src || ''; var type = classifyTaskIframe(f, src); if (!type) { visit(cdoc); continue; } total++; if (isTaskIframeFinished(f, cdoc, src)) continue; var dataStr = f.getAttribute('data'); if (dataStr) { try { var dj = JSON.parse(dataStr); var jobid = dj.jobid || (dj.property && dj.property.jobid) || ''; if (jobid && completedJobCache.has(jobid)) continue; } catch(e) {} } pending.push({ doc: cdoc, dataStr: dataStr, type: type }); } }; visit(rootDoc); return { pending: pending, total: total }; } // ============ 跨域刷章节接力(GM 存储,跨 frame / 跨子域共享) ============ const STUDY_SESSION_KEY = 'cx_study_session'; const STUDY_ADOPT_KEY = 'cx_study_adopted_ts'; const STUDY_SESSION_TTL = 15000; var studySessionInitiator = false; var lastAdoptNotifiedTs = 0; function gmStoreGet(key, def) { try { if (typeof GM_getValue === 'function') return GM_getValue(key, def); } catch(e) {} try { var raw = localStorage.getItem('cx_gm_' + key); return raw == null ? def : JSON.parse(raw); } catch(e) {} return def; } function gmStoreSet(key, val) { try { if (typeof GM_setValue === 'function') { GM_setValue(key, val); return; } } catch(e) {} try { localStorage.setItem('cx_gm_' + key, JSON.stringify(val)); } catch(e) {} } function publishStudySession(running) { gmStoreSet(STUDY_SESSION_KEY, { running: !!running, ts: Date.now() }); } function readStudySession() { var s = gmStoreGet(STUDY_SESSION_KEY, null); if (!s) return null; if (typeof s === 'string') { try { s = JSON.parse(s); } catch(e) { return null; } } return s; } function isStudySessionActive() { var s = readStudySession(); if (!s || !s.running) return false; return (Date.now() - (Number(s.ts) || 0)) <= STUDY_SESSION_TTL; } // 本帧能否访问顶层文档:能=同源(交给外层处理),不能=跨域(需要本帧接管) function isFrameSameOriginWithTop() { try { return !!(window.top && window.top.document); } catch(e) { return false; } } // 跨域接力:某个 frame 自己就是任务内容帧时,由它接管刷章节 function tryAdoptStudySession() { if (isStudyingChapters) return; if (studySessionInitiator) return; if (!isStudySessionActive()) return; if (isFrameSameOriginWithTop()) return; var scanned = collectTasksFromIframes(document); if (scanned.total === 0) return; gmStoreSet(STUDY_ADOPT_KEY, Date.now()); addLog('检测到内容帧跨域,由本帧接管刷章节', 'info'); try { startStudyChapters(); } catch(e) {} } // ==================================================================== function hasStudyContentDeep() { let found = false; const tryDoc = (doc) => { try { if (doc.querySelector('video, .video-js, .ans-attach-ct, .reader, .ppt, .ppt-play, .catalog, .vjs-play-control')) { found = true; return; } if (getQuestionContainers(doc).length > 0) { found = true; return; } } catch {} }; forEachSameOriginFrame(tryDoc); return found; } let currentPlaybackSpeed = 1.0; const PLAYBACK_SPEED_KEY = 'cx_playback_speed'; const LS_KEY_ANSWERING_STATE = 'cx_answering_state'; function saveAnsweringState() { try { const state = { active: true, startTime: Date.now() }; localStorage.setItem(LS_KEY_ANSWERING_STATE, JSON.stringify(state)); setTimeout(() => { clearAnsweringState(); }, 15 * 1000); } catch {} } function clearAnsweringState() { try { localStorage.removeItem(LS_KEY_ANSWERING_STATE); } catch {} } function getAnsweringState() { try { const raw = localStorage.getItem(LS_KEY_ANSWERING_STATE); if (!raw) return null; const state = JSON.parse(raw); if (!state.active) return null; const elapsed = Date.now() - state.startTime; if (elapsed > 15 * 1000) { clearAnsweringState(); return null; } return state; } catch { return null; } } try { const savedSpeed = localStorage.getItem(PLAYBACK_SPEED_KEY); if (savedSpeed) { currentPlaybackSpeed = parseFloat(savedSpeed); } } catch {} function updateSpeedButtonsState() { const speedButtons = document.querySelectorAll('.speed-button'); speedButtons.forEach(btn => { btn.classList.remove('speed-active'); }); const activeButton = document.getElementById(`speed-${currentPlaybackSpeed}x`); if (activeButton) { activeButton.classList.add('speed-active'); } } function setVideoPlaybackSpeed(speed) { currentPlaybackSpeed = speed; try { localStorage.setItem(PLAYBACK_SPEED_KEY, speed.toString()); } catch {} updateSpeedButtonsState(); forEachSameOriginFrame((doc) => { try { const videos = doc.querySelectorAll('video, .video-js video'); videos.forEach(v => { if (!Number.isNaN(v.playbackRate)) v.playbackRate = speed; }); } catch {} }); addLog(`视频播放速度已设置为 ${speed}×`, 'success'); } function updateStudyButtons(running) { const startBtn = document.getElementById('start-study'); const pauseBtn = document.getElementById('pause-study'); const speedControls = document.getElementById('playback-speed-controls'); if (!startBtn || !pauseBtn) return; if (running) { startBtn.style.display = 'none'; pauseBtn.style.display = 'flex'; if (speedControls) speedControls.style.display = 'flex'; } else { startBtn.style.display = 'flex'; pauseBtn.style.display = 'none'; if (speedControls) speedControls.style.display = 'none'; } } function initAllInDoc(targetDoc) { if (!isStudyingChapters) return; autoSkipDone = false; isMainInstance = true; addLog('正在加载当前页面的任务...', 'info'); var rawPayload = extractStudyPayloadFromPage(targetDoc); if (rawPayload && rawPayload.defaults) { studyPageDefaults = rawPayload.defaults; } if (rawPayload && Array.isArray(rawPayload.attachments)) { studyPageTasks = rawPayload.attachments; } var arrayAns = targetDoc.querySelectorAll(".ans-attach-ct"); if (arrayAns.length === 0) { arrayAns = targetDoc.querySelectorAll(".ans-job-ct"); } if (arrayAns.length === 0) { arrayAns = targetDoc.querySelectorAll(".chapter-item"); } if (arrayAns.length === 0) { arrayAns = targetDoc.querySelectorAll('[class*="ans"], [class*="task"], [class*="chapter"]'); } addLog(`当前页面任务数量为: ${arrayAns.length}`, 'info'); var filteredArrayAns = targetDoc.querySelectorAll('.ans-attach-ct:not(.ans-job-finished), .ans-job-ct:not(.ans-job-finished)'); var taskCount = filteredArrayAns.length; if (taskCount > 0) { try { var arrayType = getIframesType(filteredArrayAns); var arrayDocument = getAllIframesDocument(filteredArrayAns); arrayEchelon = distributeAns(arrayType, arrayDocument); if (completedJobCache.size > 0) { var before = arrayEchelon.length; arrayEchelon = arrayEchelon.filter(function(item) { if (!item.dataStr) return true; try { var d = JSON.parse(item.dataStr); var jobid = d.jobid || (d.property && d.property.jobid) || ''; return !completedJobCache.has(jobid); } catch(e) { return true; } }); if (arrayEchelon.length < before) { addLog('已过滤 ' + (before - arrayEchelon.length) + ' 个缓存中已完成的任务', 'info'); } } startProgressMonitor(); document.dispatchEvent(dealEvent); } catch (error) { addLog('初始化过程中发生错误: ' + error, 'error'); } } else { addLog('当前页面没有可处理的任务,等待确认', 'info'); var retryKey = 'cx_retry_' + (targetDoc.location ? targetDoc.location.href : ''); if (!targetDoc[retryKey]) { targetDoc[retryKey] = true; setTimeout(function() { targetDoc[retryKey] = false; var retryTasks = targetDoc.querySelectorAll('.ans-attach-ct:not(.ans-job-finished), .ans-job-ct:not(.ans-job-finished)'); if (retryTasks.length > 0) { initAllInDoc(targetDoc); return; } addLog('确认无待处理任务,跳过章节', 'info'); scheduleSkipChapter(3000); }, 4000); } } } var studyObserver = null; var lastChapterNavTs = 0; var lastLoadingLogTs = 0; var processingBatch = false; var isSkippingChapter = false; var pendingSkipTimeoutId = null; var initRetryCount = 0; var blankSectionChecks = 0; var lastBlankSectionUrl = ''; function startStudyChapters() { if (isStudyingChapters) { addLog('刷章节已在运行', 'info'); return; } isStudyingChapters = true; setPanelFixedHeight(360); updateStudyButtons(true); addLog('正在初始化中', 'info'); initRetryCount = 0; setTimeout(function() { if (!isStudyingChapters) return; addLog('开始刷章节...', 'success'); function findCardsDoc() { var cd = null; forEachSameOriginFrame(function(doc) { if (cd) return; try { if (isStudyContentDoc(doc)) { cd = doc; } } catch(e) {} }); if (!cd && isStudyContentDoc(document)) { cd = document; } return cd; } // 兜底入口:原逻辑找不到学习页 / 识别不到 .ans-* 容器时才走这里 // (扫描函数 isTaskIframeFinished / classifyTaskIframe / collectTasksFromIframes 定义在脚本外层) function tryIframeFallback() { if (processingBatch) return false; if (arrayEchelon.length > 0 && isMainInstance) return false; if (Date.now() - lastChapterNavTs < 5000) return false; var scanned = collectTasksFromIframes(document); if (scanned.total === 0) return false; if (scanned.pending.length === 0) { if (!autoSkipDone) { autoSkipDone = true; addLog('检测到章节任务均已完成,准备跳转', 'info'); scheduleSkipChapter(2000); } return true; } isMainInstance = true; autoSkipDone = false; processingBatch = true; arrayEchelon = distributeAns( scanned.pending.map(function(t) { return t.type; }), scanned.pending.map(function(t) { return { doc: t.doc, dataStr: t.dataStr }; }) ); addLog('兼容模式:识别到 ' + arrayEchelon.length + ' 个任务点', 'info'); startProgressMonitor(); setTimeout(function() { document.dispatchEvent(dealEvent); }, 0); return true; } // ================================================================ function checkAndInit(cardsDoc) { if (!cardsDoc) return false; if (processingBatch) return false; if (Date.now() - lastChapterNavTs < 5000) { if (Date.now() - lastLoadingLogTs > 8000) { lastLoadingLogTs = Date.now(); addLog('页面内容加载中,请稍等', 'info'); } return false; } var unfinished = cardsDoc.querySelectorAll('.ans-attach-ct:not(.ans-job-finished), .ans-job-ct:not(.ans-job-finished)'); if (unfinished.length === 0) { var anyExists = cardsDoc.querySelectorAll('.ans-attach-ct, .ans-job-ct, .chapter-item').length; if (anyExists > 0) { blankSectionChecks = 0; lastBlankSectionUrl = ''; addLog('检测到章节任务均已完成,准备跳转', 'info'); scheduleSkipChapter(2000); return false; } if (docHasTaskFrames(cardsDoc)) { if (tryIframeFallback()) return true; if (Date.now() - lastLoadingLogTs > 8000) { lastLoadingLogTs = Date.now(); addLog('已定位到任务点,正在等待任务信息加载完成...', 'info'); } return false; } var currentUrl = (cardsDoc.location ? cardsDoc.location.href : '') || location.href; if (currentUrl !== lastBlankSectionUrl) { blankSectionChecks = 0; lastBlankSectionUrl = currentUrl; } blankSectionChecks++; if (blankSectionChecks >= 2) { blankSectionChecks = 0; addLog('检测到空白小节,自动跳过', 'info'); scheduleSkipChapter(500); } else { addLog('检测到空白小节,等待确认', 'info'); } return false; } blankSectionChecks = 0; lastBlankSectionUrl = ''; processingBatch = true; addLog('发现 ' + unfinished.length + ' 个待处理任务', 'info'); initAllInDoc(cardsDoc); return true; } function setupObserver(cardsDoc) { if (!cardsDoc) return; try { if (studyObserver) studyObserver.disconnect(); } catch(e) {} var target = cardsDoc.querySelector('.wrap, .ans-cc, #coursetest, [class*="catalog"]') || cardsDoc.body || cardsDoc.documentElement; if (!target) return; try { studyObserver = new MutationObserver(function() { if (!isStudyingChapters || processingBatch) return; if (Date.now() - lastChapterNavTs < 5000) { if (Date.now() - lastLoadingLogTs > 8000) { lastLoadingLogTs = Date.now(); addLog('页面内容加载中,请稍等', 'info'); } return; } var cd = findCardsDoc(); if (!cd) return; var freshTasks = cd.querySelectorAll('.ans-attach-ct:not(.ans-job-finished), .ans-job-ct:not(.ans-job-finished)'); if (freshTasks.length > 0 && arrayEchelon.length === 0) { addLog('检测到章节内容变化,重新加载任务', 'info'); checkAndInit(cd); } }); studyObserver.observe(target, { childList: true, subtree: true }); } catch(e) {} } var initialCardsDoc = findCardsDoc(); if (initialCardsDoc) { addLog('正在检测任务', 'info'); setupObserver(initialCardsDoc); checkAndInit(initialCardsDoc); } else if (!tryIframeFallback()) { addLog('正在定位学习页面...', 'info'); } if (studyIntervalId) clearInterval(studyIntervalId); studyIntervalId = setInterval(function() { if (!isStudyingChapters) { clearInterval(studyIntervalId); studyIntervalId = null; return; } // 跨域接力:发起帧持续续期;接管帧发现会话结束后自动停止 if (studySessionInitiator) { publishStudySession(true); } else if (!isStudySessionActive()) { addLog('刷章节会话已结束,停止接管', 'info'); stopStudyChapters(); return; } if (processingBatch) return; if (Date.now() - lastChapterNavTs < 5000) { if (Date.now() - lastLoadingLogTs > 8000) { lastLoadingLogTs = Date.now(); addLog('页面内容加载中,请稍等', 'info'); } return; } if (arrayEchelon.length > 0 && isMainInstance) { return; } var cardsDoc = findCardsDoc(); if (!cardsDoc) { if (tryIframeFallback()) { initRetryCount = 0; return; } // 跨域时由内容帧接管,本帧只提示一次,不再刷“等待学习页面” var adoptTs = Number(gmStoreGet(STUDY_ADOPT_KEY, 0)) || 0; if (adoptTs > 0 && adoptTs !== lastAdoptNotifiedTs && (Date.now() - adoptTs) <= STUDY_SESSION_TTL) { lastAdoptNotifiedTs = adoptTs; initRetryCount = 0; addLog('已由内容帧接管,刷章节进行中...', 'info'); return; } initRetryCount++; if (initRetryCount === 3) { addLog('正在等待学习页面加载完成...', 'info'); } if (initRetryCount % 10 === 0) { addLog('仍在等待学习页面,请确认在课程章节页面中', 'warning'); } return; } initRetryCount = 0; setupObserver(cardsDoc); checkAndInit(cardsDoc); }, 3000); }, 3000); } function stopStudyChapters() { if (!isStudyingChapters) return; isStudyingChapters = false; // 发起帧结束时通知其他 frame 停止接力 if (studySessionInitiator) { studySessionInitiator = false; publishStudySession(false); } isMainInstance = false; processingBatch = false; isSkippingChapter = false; if (pendingSkipTimeoutId) { clearTimeout(pendingSkipTimeoutId); pendingSkipTimeoutId = null; } autoSkipDone = false; arrayEchelon = []; completedJobCache.clear(); studyPageDefaults = null; studyPageTasks = null; setPanelFixedHeight(320); if (studyIntervalId) { clearInterval(studyIntervalId); studyIntervalId = null; } try { if (studyObserver) { studyObserver.disconnect(); studyObserver = null; } } catch(e) {} forEachSameOriginFrame((doc) => { try { doc.querySelectorAll('video, .video-js video').forEach(v => { try { v.pause(); } catch {} }); } catch {} }); updateStudyButtons(false); addLog('已暂停刷章节', 'info'); } function getIframesType(arrayAns) { return Array.from(arrayAns).map(function(ans) { const iframe = ans.querySelector("iframe"); const jsonStr = iframe ? iframe.getAttribute("data") : null; let type = null; if (!jsonStr) { const src = iframe ? iframe.getAttribute("src") : null; if (src) { if (src.includes('work') || src.includes('exam') || src.includes('test') || src.includes('quiz') || src.includes('homework')) { type = "workA"; } else if (src.includes('video') || src.includes('.mp4')) { type = ".mp4"; } else if (src.includes('.pdf') || src.includes('.ppt')) { type = ".pdf"; } } } else { try { const json = JSON.parse(jsonStr); if (json && json.type) { type = json.type; } else if (json && json.worktype) { type = json.worktype; } else if (json && json.resourceType) { type = json.resourceType; } } catch (error) {} } if (!type) { const className = ans.className || ''; if (className.includes('work') || className.includes('exam') || className.includes('test')) { type = "workA"; } } return type; }).filter(function(type) { return type !== null; }); } function getAllIframesDocument(arrayAns) { return Array.from(arrayAns).map(function(ans) { var iframe = ans.querySelector('iframe'); var idoc = iframe ? (iframe.contentWindow ? iframe.contentWindow.document : null) : null; var dataStr = iframe ? iframe.getAttribute('data') : null; return { doc: idoc, dataStr: dataStr }; }).filter(function(item) { return item !== undefined && item !== null; }); } const handlerMap = { ".mp4": videoHandler, ".wmv": videoHandler, ".avi": videoHandler, ".mkv": videoHandler, ".flv": videoHandler, ".mov": videoHandler, ".doc": pptxHandler, ".docx": pptxHandler, ".pptx": pptxHandler, ".pdf": pptxHandler, ".ppt": pptxHandler, ".mp3": audioHandler, ".wav": audioHandler, "workA": testHandler, "workB": testHandler, "workC": testHandler, "workD": testHandler, "homework": testHandler, "exam": testHandler, "test": testHandler, "quiz": testHandler }; function distributeAns(arrayType, arrayItems) { return arrayType.map(function(type, index) { var handler = handlerMap[type] || ignoreAns; return { document: arrayItems[index].doc, handler: handler, type: type, dataStr: arrayItems[index].dataStr }; }); } function dealSingleAns(singleAns) { if (!isStudyingChapters) { processingBatch = false; return; } if (singleAns && singleAns.handler && typeof singleAns.handler === 'function') { singleAns.handler(singleAns); } else { document.dispatchEvent(dealEvent); } } function dealAnsEchelon(echelonArray) { if (!isStudyingChapters) { processingBatch = false; return; } var remainingTasks = echelonArray.length; if (remainingTasks === 0) { stopProgressMonitor(); processingBatch = false; addLog('该章节任务已处理完成,即将跳转下一章节', 'success'); scheduleSkipChapter(3000); return; } processingBatch = true; var nextTask = echelonArray.shift(); try { dealSingleAns(nextTask); } catch (error) { addLog('处理任务时发生错误,继续处理下一个', 'error'); dealAnsEchelon(echelonArray); } } function testHandler(entry) { var idocument = entry.document; addLog("处理章节测试任务中...", 'info'); if (ifTest == 'skip') { setTimeout(function() { addLog("根据配置选项,跳过章节测试", 'info'); document.dispatchEvent(dealEvent); }, 1000); return; } var retryCount = 0; var maxRetries = 10; function tryFindQuestions() { try { var foundDoc = null; var foundQuestions = null; var iframe = idocument.querySelector("iframe"); if (iframe) { try { var sDocument = iframe.contentWindow ? iframe.contentWindow.document : null; if (sDocument) { var form = sDocument.querySelector("form"); if (form) { foundQuestions = form.querySelectorAll('.singleQuesId'); if (foundQuestions.length > 0) foundDoc = sDocument; } } } catch(e) {} } if (!foundDoc || foundQuestions.length === 0) { var qContainers = getQuestionContainers(idocument); if (qContainers.length > 0) { addLog('在页面中直接找到章节测验内容', 'info'); handleQuizTest(idocument).then(function() { addLog('章节测验答题完成', 'success'); submitQuizAndContinue(idocument); }).catch(function(e) { addLog('答题失败: ' + e.message, 'error'); setTimeout(function() { document.dispatchEvent(dealEvent); }, 2000); }); return; } } if (foundDoc && foundQuestions && foundQuestions.length > 0) { addLog('找到 ' + foundQuestions.length + ' 道章节测试题,开始答题', 'info'); handleQuizTest(foundDoc).then(function() { addLog('章节测试答题完成', 'success'); submitQuizAndContinue(foundDoc); }).catch(function(e) { addLog('答题失败: ' + e.message, 'error'); setTimeout(function() { document.dispatchEvent(dealEvent); }, 2000); }); return; } if (retryCount >= 3) { tryEnterQuizAndAnswer(document).then(function(handled) { if (handled) { addLog('章节测验标签页答题完成', 'success'); setTimeout(function() { document.dispatchEvent(dealEvent); }, 2000); } else { retryCount++; if (retryCount < maxRetries) { addLog("等待章节测验加载 (" + retryCount + "/" + maxRetries + ")", 'info'); setTimeout(tryFindQuestions, 2500); } else { addLog("无法找到章节测验内容,跳过", 'warning'); setTimeout(function() { document.dispatchEvent(dealEvent); }, 1000); } } }); return; } retryCount++; if (retryCount < maxRetries) { addLog("等待章节测验加载 (" + retryCount + "/" + maxRetries + ")", 'info'); setTimeout(tryFindQuestions, 2500); } else { addLog("无法找到章节测验内容,跳过", 'warning'); setTimeout(function() { document.dispatchEvent(dealEvent); }, 1000); } } catch(e) { retryCount++; if (retryCount < maxRetries) { addLog("等待章节测验加载 (" + retryCount + "/" + maxRetries + ")", 'info'); setTimeout(tryFindQuestions, 2500); } else { addLog("无法找到章节测验内容,跳过", 'warning'); setTimeout(function() { document.dispatchEvent(dealEvent); }, 1000); } } } function submitQuizAndContinue(doc) { var btnDoc = doc.querySelector(".ZY_sub"); if (!btnDoc) btnDoc = doc; var submit_btn = btnDoc.querySelector('.btnSubmit') || doc.querySelector('.btnSubmit'); var save_btn = btnDoc.querySelector('.btnSave') || doc.querySelector('.btnSave'); if (submit_btn) { submit_btn.click(); addLog('章节测试答案已提交', 'success'); } else if (save_btn) { save_btn.click(); addLog('章节测试答案已保存', 'info'); } else { addLog('未找到提交/保存按钮', 'warning'); } setTimeout(function() { document.dispatchEvent(dealEvent); }, 3000); } tryFindQuestions(); } function audioHandler(entry) { var idocument = entry.document; addLog("处理音频任务中...", 'info'); var retries = 0; var maxRetries = 8; function tryFindAudio() { try { const audio = idocument.querySelector("audio"); const audioPlayButton = idocument.querySelector(".vjs-play-control.vjs-control.vjs-button, .vjs-big-play-button"); if (!audio) { retries++; if (retries < maxRetries) { setTimeout(tryFindAudio, 2000); } else { addLog("没有找到音频元素,跳过", 'warning'); setTimeout(function() { document.dispatchEvent(dealEvent); }, 1000); } return; } if (audioPlayButton) audioPlayButton.click(); audio.addEventListener("ended", function() { addLog("音频任务已完成", 'success'); setTimeout(function() { document.dispatchEvent(dealEvent); }, 500); }, { once: true }); audio.addEventListener("pause", function() { if (audioPlayButton) audioPlayButton.click(); }); } catch(e) { retries++; if (retries < maxRetries) { setTimeout(tryFindAudio, 2000); } else { addLog('音频处理出错: ' + e.message, 'error'); setTimeout(function() { document.dispatchEvent(dealEvent); }, 2000); } } } tryFindAudio(); } function getUrlParam(name) { var searchStr = ''; try { searchStr = window.top.location.search; } catch(e) { searchStr = location.search; } var match = searchStr.match(new RegExp('[?&]' + name + '=([^&]*)')); return match ? decodeURIComponent(match[1]) : ''; } function extractStudyPayloadFromPage(pageDoc) { if (!pageDoc || !pageDoc.scripts) return null; var scripts = Array.from(pageDoc.scripts); for (var i = 0; i < scripts.length; i++) { var content = String(scripts[i].textContent || ''); if (!content.includes('mArg') || !content.includes('attachments')) continue; var match = content.match(/try\s*\{\s*mArg\s*=\s*([\s\S]*?)\s*;\s*\}\s*catch/); if (!match || !match[1]) continue; var raw = match[1].trim(); if (!raw || raw === '""' || raw === '$mArg') continue; try { return JSON.parse(raw); } catch(e) {} } return null; } async function callDocApi(path, jobid, jtoken) { var courseid = (studyPageDefaults && (studyPageDefaults.courseId || studyPageDefaults.courseid)) || getUrlParam('courseId'); var clazzid = (studyPageDefaults && (studyPageDefaults.clazzId || studyPageDefaults.clazzid)) || getUrlParam('clazzId'); var knowledgeid = (studyPageDefaults && (studyPageDefaults.knowledgeId || studyPageDefaults.knowledgeid)) || getUrlParam('knowledgeId'); var url = new URL(path, location.origin); if (jobid) url.searchParams.set('jobid', jobid); if (knowledgeid) url.searchParams.set('knowledgeid', knowledgeid); if (courseid) url.searchParams.set('courseid', courseid); if (clazzid) url.searchParams.set('clazzid', clazzid); if (jtoken) url.searchParams.set('jtoken', jtoken); url.searchParams.set('_dc', String(Date.now())); try { var response = await fetch(url.toString(), { credentials: 'include' }); return response.ok; } catch(e) { return false; } } function pptxHandler(entry) { var idocument = entry.document; addLog("处理 PPT/PDF 任务中...", 'info'); var retries = 0; var maxRetries = 6; function markTaskDomFinished(jobid) { completedJobCache.add(jobid); setTimeout(function() { completedJobCache.delete(jobid); }, 120000); try { var cd = null; forEachSameOriginFrame(function(doc) { if (cd) return; try { if (isStudyContentDoc(doc)) cd = doc; } catch(e) {} }); if (!cd) return; var attachCts = cd.querySelectorAll('.ans-attach-ct'); for (var i = 0; i < attachCts.length; i++) { var iframe = attachCts[i].querySelector('iframe'); if (!iframe) continue; var dataStr = iframe.getAttribute('data'); if (!dataStr) continue; try { var d = JSON.parse(dataStr); var dj = d.jobid || (d.property && d.property.jobid) || ''; if (dj === jobid) { attachCts[i].classList.add('ans-job-finished'); break; } } catch(e) {} } } catch(e) {} } function tryApiThenDoc() { var taskData = null; if (entry.dataStr) { try { taskData = JSON.parse(entry.dataStr); } catch(e) {} } if (taskData && (taskData.jobid || (taskData.property && taskData.property.jobid))) { var jobid = taskData.jobid || (taskData.property && taskData.property.jobid); if (completedJobCache.has(jobid)) { addLog('该文档任务已完成(缓存中),跳过', 'info'); setTimeout(function() { document.dispatchEvent(dealEvent); }, 500); return; } var jtoken = taskData.jtoken || ''; if (!jtoken && studyPageTasks) { for (var t = 0; t < studyPageTasks.length; t++) { var pt = studyPageTasks[t]; var ptJobid = (pt && pt.property && pt.property.jobid) || (pt && pt.jobid) || ''; if (ptJobid === jobid) { jtoken = pt.jtoken || ''; break; } } } var apiPaths = getReaderMissionApiPaths({ type: entry.type }); addLog('正在阅读', 'info'); tryApiRecursive(apiPaths, 0, jobid, jtoken); } else { addLog('未获取到任务数据,直接翻页处理', 'info'); fallbackToPageTurning(); } } function tryApiRecursive(paths, index, jobid, jtoken) { if (index >= paths.length) { addLog('API回调全部失败,转为翻页处理', 'warning'); fallbackToPageTurning(); return; } callDocApi(paths[index], jobid, jtoken).then(function(success) { if (success) { markTaskDomFinished(jobid); addLog('文档任务已完成 (' + paths[index] + ')', 'success'); setTimeout(function() { document.dispatchEvent(dealEvent); }, 1500); } else { addLog('任务失败,尝试下一个', 'info'); tryApiRecursive(paths, index + 1, jobid, jtoken); } }); } function fallbackToPageTurning() { try { var handled = handlePPTInDocument(idocument); if (!handled) { var hasReader = idocument.querySelector('.reader, .ppt, .ppt-play, .reader-box, .reader_container, .np-viewer, .file-reader, .slide-content, .swiper-slide'); if (!hasReader) { retries++; if (retries < maxRetries) { setTimeout(tryApiThenDoc, 2000); } else { addLog('未找到文档阅读器,跳过', 'warning'); setTimeout(function() { document.dispatchEvent(dealEvent); }, 1000); } return; } } setTimeout(function() { document.dispatchEvent(dealEvent); }, 2000); } catch(e) { retries++; if (retries < maxRetries) { setTimeout(tryApiThenDoc, 2000); } else { addLog('文档处理出错: ' + e.message, 'error'); setTimeout(function() { document.dispatchEvent(dealEvent); }, 2000); } } } tryApiThenDoc(); } function ignoreAns(entry) { var idocument = entry.document; addLog('忽略未知任务类型', 'info'); setTimeout(function() { document.dispatchEvent(dealEvent); }, 2000); } function removeNewlines(str) { return str.replace(/\n/g, ''); } function extractText(str) { var match = str.match(/【(.*?)】/); return match ? match[1] : null; } function getTrueStatusPercentage(arr) { var trueCount = arr.filter(function(item) { return item.status === true; }).length; return (trueCount / arr.length) * 100; } function formatTime(seconds) { var m = Math.floor(seconds / 60); var s = Math.floor(seconds % 60); return m + ':' + (s < 10 ? '0' : '') + s; } function startProgressMonitor() { if (progressMonitor) return; progressMonitor = setInterval(function() { var anyDone = false; forEachSameOriginFrame(function(doc) { if (anyDone) return; try { var at = doc.querySelectorAll('.ans-attach-ct, .ans-job-ct, .chapter-item'); var dt = doc.querySelectorAll('.ans-attach-ct.ans-job-finished, .ans-job-ct.ans-job-finished'); if (at.length > 0) { var taskPct = ((dt.length / at.length) * 100).toFixed(0); if (dt.length === at.length && !autoSkipDone) { autoSkipDone = true; addLog('[进度] 当前章节所有任务已全部完成,准备自动跳转下一章', 'info'); stopProgressMonitor(); scheduleSkipChapter(3000); } anyDone = true; } } catch(e) {} }); }, 30000); } function stopProgressMonitor() { if (progressMonitor) { clearInterval(progressMonitor); progressMonitor = null; } } document.addEventListener("redeal", function() { if (!isMainInstance || !isStudyingChapters) return; setTimeout(function() { dealAnsEchelon(arrayEchelon); }, 1500); }); const LOG_MAX_ITEMS = 120; function addLog(message, type = 'info') { try { const logContainer = document.getElementById('answer-log'); if (!logContainer) return; const text = String(message || '') .replace(/\s+/g, ' ') .slice(0, 140); const logItem = document.createElement('div'); logItem.className = `log-item ${type}`; logItem.textContent = `${new Date().toLocaleTimeString()} - ${text}`; logContainer.appendChild(logItem); const items = logContainer.querySelectorAll('.log-item'); if (items.length > LOG_MAX_ITEMS) { const removeCount = items.length - LOG_MAX_ITEMS; for (let i = 0; i < removeCount; i++) { const n = logContainer.firstElementChild; if (n) logContainer.removeChild(n); } } logContainer.scrollTop = logContainer.scrollHeight; } catch {} } async function updateTrialBadge() { try { const el = document.getElementById('cx_trial_badge'); if (!el) return; const licensed = await checkLicensePaid(); if (licensed) { el.innerHTML = '永久激活,感谢打赏'; return; } const used = getFreeUsedCount(); const remain = Math.max(0, getFreeLimit() - used); el.innerHTML = `试用剩余:${remain}/${getFreeLimit()} `; const btn = document.getElementById('buy-license'); if (btn) { btn.addEventListener('click', async () => { try { await showPayModalPaid('免费试用,打赏8元永久解锁哦'); } catch (e) { addLog('打开支付弹窗失败: ' + (e && e.message ? e.message : e), 'error'); } }); } } catch {} } function getQuestionInfo(questionElement) { try { const questionId = questionElement.id || ''; const possibleTypeSelectors = [ '.type_title', '.mark_name', '.questionType', 'div[class*="type"]', 'div[class*="Type"]', '.subject_type', '.q-type', 'div[class*="questionType"]', '.stem_type' ]; const possibleQuestionSelectors = [ '.subject_describe', '.mark_name', '.questionContent', '.title', 'div[class*="title"]', '.subject_stem', '.q-body', '.question-content', '.stem-content', '.stem_txt' ]; let typeText = ''; for (let selector of possibleTypeSelectors) { const element = questionElement.querySelector(selector); if (element) { typeText = element.textContent.trim(); break; } } let type = ''; if (typeText.includes('单选题')) type = 'single'; else if (typeText.includes('多选题')) type = 'multiple'; else if (typeText.includes('判断题')) type = 'judge'; else if (typeText.includes('填空题')) type = 'blank'; else if (typeText.includes('简答题')) type = 'short'; else if (typeText.includes('名词解释')) type = 'term'; else if (typeText.includes('论述题')) type = 'essay'; else if (typeText.includes('计算题')) type = 'calculation'; else if (typeText.includes('完形填空')) type = 'cloze'; else if (typeText.includes('写作题')) type = 'writing'; else if (typeText.includes('连线题')) type = 'matching'; else if (typeText.includes('分录题')) type = 'accounting'; let questionText = ''; for (let selector of possibleQuestionSelectors) { const element = questionElement.querySelector(selector); if (element) { questionText = element.textContent.trim(); break; } } const optionSelectors = [ '.stem_answer > div', '.stem_answer div[class*="option"]', 'div.stem_answer > div', '.answer_p', '.subject_node', '.answer_options', '.options div' ]; if (questionId) { const escapedId = (window.CSS && typeof window.CSS.escape === 'function') ? window.CSS.escape(questionId) : questionId.replace(/(["'\\.#:[\],>+~*^$(){}| ])/g, '\\$1'); optionSelectors.splice(3, 0, `#${escapedId} > div.stem_answer > div`); } let options = []; let foundSelector = ''; for (let selector of optionSelectors) { let elements = []; try { elements = questionElement.querySelectorAll(selector); } catch (e) { continue; } if (elements.length > 0) { options = Array.from(elements).map((option, index) => { const text = option.textContent.trim(); const letter = String.fromCharCode(65 + index); return text; }); foundSelector = selector; break; } } if (options.length === 0 && questionId) { const escapedId = (window.CSS && typeof window.CSS.escape === 'function') ? window.CSS.escape(questionId) : questionId.replace(/(["'\\.#:[\],>+~*^$(){}| ])/g, '\\$1'); for (let i = 1; i <= 6; i++) { const specificSelector = `#${escapedId} > div.stem_answer > div:nth-child(${i})`; const element = document.querySelector(specificSelector); if (element) { options.push(element.textContent.trim()); } } } if (!type || !questionText) { addLog('题目已完成', 'error'); } return { type, question: questionText, options, foundSelector, questionId }; } catch (error) { addLog(`解析题目失败: ${error.message}`, 'error'); return null; } } function getModelParams(questionType) { const preciseTypes = ['single', 'multiple', 'blank', 'cloze', 'judge', 'term']; if (preciseTypes.includes(questionType)) { return { temperature: 0.1, max_tokens: 100, top_p: 0.1, frequency_penalty: 0.1, presence_penalty: 0.1 }; } else { return { temperature: 0.5, max_tokens: 400, top_p: 0.8, frequency_penalty: 0.3, presence_penalty: 0.3 }; } } async function getAnswer(questionInfo) { try { await ensureAccessAllowed(); } catch (e) { addLog(String(e && e.message ? e.message : e), 'error'); return null; } const prompt = generatePrompt(questionInfo); try { const modelParams = getModelParams(questionInfo.type); const data = await deepseekChat([ { role: "user", content: prompt } ], modelParams); if (!data.choices || !data.choices[0] || !data.choices[0].message) { throw new Error('Invalid API response format'); } const answer = data.choices[0].message.content.trim(); return answer; } catch (error) { addLog(`API调用失败: ${error.message}`, 'error'); return null; } } function generatePrompt(questionInfo) { let prompt = `直接给出答案不要解释 \n题目:${questionInfo.question}\n`; if (questionInfo.type === 'single' || questionInfo.type === 'multiple' || questionInfo.type === 'judge') { if (questionInfo.options && questionInfo.options.length > 0) { prompt += '选项:\n'; questionInfo.options.forEach((option, index) => { const letter = String.fromCharCode(65 + index); const cleanOption = option.replace(/^[A-Z][\s.、.。]+|^\d+[\s.、.。]+/, '').trim(); prompt += `${letter}. ${cleanOption}\n`; }); if (questionInfo.type === 'single') { prompt += '\n请直接回答选项字母(A/B/C/D/...)'; } else if (questionInfo.type === 'multiple') { prompt += '\n这是多选题,请列出所有正确选项的字母,用逗号分隔(如:A,B,D)'; } else if (questionInfo.type === 'judge') { prompt += '\n这是判断题,请回答A表示正确,B表示错误'; } } } else if (questionInfo.type === 'blank') { prompt += '\n这是填空题,请按顺序给出每个空的答案,用逗号分隔'; } else if (questionInfo.type === 'short' || questionInfo.type === 'term' || questionInfo.type === 'essay') { prompt += '\n请输出为纯文本不要带任何格式'; } return prompt; } (function(){ function __getDSAuth(){ const k = 71; const arr = [41,46,42,38,52,46,54,50,38,41,45,46,38]; return String.fromCharCode(...arr.map(n => n ^ k)); } window.__getDSAuth = __getDSAuth; })(); function fillAnswer(answer, questionElement, type) { try { let filled = false; const questionId = questionElement.id; switch (type) { case 'blank': case 'cloze': { const answers = answer.split(/[,,;;、]\s*/).map(a => a.trim()).filter(a => a); const editorElements = questionElement.querySelectorAll('[id^="answerEditor"]'); if (editorElements.length > 0) { editorElements.forEach((editorElement, index) => { const editorId = editorElement.id; if (index < answers.length) { const currentAnswer = answers[index]; try { if (typeof UE !== 'undefined' && UE.getEditor) { const editor = UE.getEditor(editorId); if (editor) { if (editor.ready) { editor.ready(() => { editor.setContent(currentAnswer); if (typeof editor.fireEvent === 'function') { editor.fireEvent('contentChange'); } }); filled = true; } } } if (!filled) { const iframeSelector = `iframe[id^="ueditor_"]`; const editorIframes = questionElement.querySelectorAll(iframeSelector); const editorIframe = editorIframes[index]; if (editorIframe) { try { const iframeDoc = editorIframe.contentDocument || editorIframe.contentWindow.document; const editorBody = iframeDoc.body; if (editorBody) { editorBody.innerHTML = currentAnswer; editorBody.dispatchEvent(new Event('input', { bubbles: true })); filled = true; } } catch (e) { addLog(`iframe操作失败: ${e.message}`, 'error'); } } } const textarea = document.getElementById(editorId); if (textarea) { textarea.value = currentAnswer; textarea.dispatchEvent(new Event('change', { bubbles: true })); textarea.dispatchEvent(new Event('input', { bubbles: true })); } } catch (e) { addLog(`处理第${index + 1}个空时出错: ${e.message}`, 'error'); } } else { addLog(`警告:第${index + 1}个空没有对应的答案`, 'error'); } }); } if (!filled) { const blankInputs = [ ...questionElement.querySelectorAll('input[type="text"]'), ...questionElement.querySelectorAll('.blank'), ...questionElement.querySelectorAll('.fill-blank'), ...questionElement.querySelectorAll('[class*="blank"]'), ...questionElement.querySelectorAll('[class*="fill"]'), ...questionElement.querySelectorAll('textarea') ]; if (blankInputs.length > 0) { blankInputs.forEach((input, index) => { if (index < answers.length) { try { input.value = answers[index]; input.dispatchEvent(new Event('input', { bubbles: true })); input.dispatchEvent(new Event('change', { bubbles: true })); filled = true; } catch (e) { addLog(`填写第${index + 1}个空失败: ${e.message}`, 'error'); } } else { addLog(`警告:第${index + 1}个输入框没有对应的答案`, 'error'); } }); } } break; } case 'short': case 'term': case 'essay': case 'writing': case 'calculation': case 'matching': case 'accounting': { const textInputs = [ ...questionElement.querySelectorAll('textarea'), ...questionElement.querySelectorAll('.answer-area'), ...questionElement.querySelectorAll('.writing-area'), ...questionElement.querySelectorAll('[class*="answer"]'), ...questionElement.querySelectorAll('[class*="text-area"]'), ...questionElement.querySelectorAll('div[contenteditable="true"]') ]; if (textInputs.length > 0) { textInputs.forEach(input => { try { if (input.tagName.toLowerCase() === 'textarea' || input.tagName.toLowerCase() === 'input') { input.value = answer; input.dispatchEvent(new Event('input', { bubbles: true })); input.dispatchEvent(new Event('change', { bubbles: true })); } else if (input.getAttribute('contenteditable') === 'true') { input.innerHTML = answer; input.dispatchEvent(new Event('input', { bubbles: true })); } filled = true; } catch (e) { addLog(`填写答案失败: ${e.message}`, 'error'); } }); } const editors = [ ...questionElement.querySelectorAll('.editor'), ...questionElement.querySelectorAll('[class*="editor"]'), ...questionElement.querySelectorAll('iframe') ]; editors.forEach(editor => { try { if (editor.tagName.toLowerCase() === 'iframe') { const iframeDoc = editor.contentDocument || editor.contentWindow.document; const editorBody = iframeDoc.body; if (editorBody) { editorBody.innerHTML = answer; editorBody.dispatchEvent(new Event('input', { bubbles: true })); filled = true; } } } catch (e) { addLog(`访问富文本编辑器失败: ${e.message}`, 'error'); } }); break; } case 'single': case 'multiple': case 'judge': { const isExamPage = /mooc1\.chaoxing\.com\/exam-ans/.test(location.href); let answerLetters; if (type === 'multiple') { answerLetters = answer.toUpperCase().split(/[,,、\s]+/).map(l => l.trim()); } else { answerLetters = [answer.toUpperCase().trim()]; } answerLetters = answerLetters.map(l => { if (/^[A-Z]$/.test(l)) return l; const match = l.match(/^([A-Z])[..、\s]/); if (match) return match[1]; if (type === 'judge') { if (/^正确$|^对$|^A$|^√$/.test(l)) return 'A'; if (/^错误$|^错$|^B$|^×$/.test(l)) return 'B'; } if (type === 'single') { if (/^正确$|^对$/.test(l)) return 'A'; if (/^错误$|^错$/.test(l)) return 'B'; } return l; }); const qid = (questionElement.getAttribute('data') || questionElement.dataset?.qid || '').toString().trim(); const optionBlocks = Array.from(questionElement.querySelectorAll('.stem_answer > div, .stem_answer .answerBg, ul.Zy_ulTop li, li[onclick][qid]')); if (isExamPage) { addLog(`考试页面模式:已生成答案 [${answerLetters.join(', ')}],请手动勾选`, 'info'); } for (const letter of answerLetters) { if (!/^[A-Z]$/.test(letter)) { addLog(`跳过无效的选项字母: ${letter}`, 'error'); continue; } let optionElement = null; if (qid) { const marker = questionElement.querySelector(`.choice${qid}[data="${letter}"], .choice${qid}.num_option[data="${letter}"], .choice${qid}.num_option_dx[data="${letter}"]`); if (marker) optionElement = marker.closest('li, div.answerBg, div'); } if (!optionElement) { optionElement = optionBlocks.find(el => { const aria = (el.getAttribute('aria-label') || '').trim(); const txt = (el.textContent || '').trim(); return new RegExp(`^${letter}[、\\.\\s]`).test(txt) || aria.startsWith(`${letter} `) || aria.startsWith(`${letter}、`); }) || null; } if (!optionElement) { const index = letter.charCodeAt(0) - 65; if (index >= 0 && index < optionBlocks.length) optionElement = optionBlocks[index]; } if (optionElement) { try { if (isExamPage) { optionElement.style.border = '2px solid #4CAF50'; optionElement.style.backgroundColor = 'rgba(76, 175, 80, 0.1)'; filled = true; } else { optionElement.click(); const input = optionElement.querySelector('input'); if (input) { input.click(); input.checked = true; input.dispatchEvent(new Event('change', { bubbles: true })); } const label = optionElement.querySelector('label'); if (label) { label.click(); } filled = true; } } catch (e) { addLog(`处理选项 ${letter} 失败: ${e.message}`, 'error'); } } else { addLog(`未找到选项元素: ${letter}`, 'error'); } } break; } default: break; } if (filled) { if (!/mooc1\.chaoxing\.com\/exam-ans/.test(location.href)) { addLog(`答案填写成功`, 'success'); } } else { addLog(`答案可能未成功填写,请检查`, 'error'); } try { const submitButtons = [ ...questionElement.querySelectorAll('button[type="submit"]'), ...questionElement.querySelectorAll('input[type="submit"]'), ...questionElement.querySelectorAll('.submit-btn'), ...questionElement.querySelectorAll('.save-btn'), ...questionElement.querySelectorAll('[class*="submit"]'), ...questionElement.querySelectorAll('[class*="save"]') ]; if (submitButtons.length > 0) { submitButtons[0].click(); } } catch (e) { } } catch (error) { addLog(`答案填写失败: ${error.message}`, 'error'); } } function debugPageStructure() { const possibleContainers = QUESTION_CONTAINER_SELECTOR.split(',').map(s => s.trim()); for (let selector of possibleContainers) { const elements = document.querySelectorAll(selector); if (elements.length > 0) { } } const allElements = document.querySelectorAll('*'); const relevantElements = Array.from(allElements).filter(el => { const className = el.className || ''; const id = el.id || ''; return (className + id).toLowerCase().includes('question') || (className + id).toLowerCase().includes('answer') || (className + id).toLowerCase().includes('option') || (className + id).toLowerCase().includes('subject'); }); relevantElements.forEach(el => { }); const inputs = document.querySelectorAll('input[type="radio"], input[type="checkbox"], textarea'); inputs.forEach(input => { }); } function updateStatus(running) { const startButton = document.getElementById('start-answer'); const pauseButton = document.getElementById('pause-answer'); if (running) { startButton.style.display = 'none'; pauseButton.style.display = 'flex'; } else { startButton.style.display = 'flex'; pauseButton.style.display = 'none'; } } function hasQuestions() { return getQuestionContainers(document).length > 0; } function showNoTaskToast() { const toast = document.createElement('div'); toast.id = 'no-task-toast'; toast.textContent = '该页面无任务'; document.body.appendChild(toast); setTimeout(() => { if (toast && toast.parentNode) { toast.parentNode.removeChild(toast); } }, 3000); } function isQuestionAnswered(q) { try { if (q.querySelector('.newAnswerBx .myAnswer, .answerCon, .myAllAnswerBx, .answerScore, .CorrectOrNot')) { return true; } const choiceInputs = q.querySelectorAll('input[type="radio"], input[type="checkbox"]'); if (choiceInputs.length > 0) { return Array.from(choiceInputs).some(i => i.checked); } const textInputs = q.querySelectorAll('input[type="text"], textarea'); if (textInputs.length > 0) { if (Array.from(textInputs).some(t => (t.value || '').trim().length > 0)) return true; } const editableDivs = q.querySelectorAll('[contenteditable="true"]'); if (editableDivs.length > 0) { if (Array.from(editableDivs).some(d => (d.innerText || d.textContent || '').trim().length > 0)) return true; } const ueTextareas = q.querySelectorAll('[id^="answerEditor"]'); for (const ta of ueTextareas) { const id = ta.id; try { if (typeof UE !== 'undefined' && UE.getEditor) { const ed = UE.getEditor(id); if (ed && ed.getContentTxt && ed.getContentTxt().trim().length > 0) return true; } } catch {} if ((ta.value || '').trim().length > 0) return true; } const ifr = q.querySelector('iframe[id^="ueditor_"]'); if (ifr) { try { const doc = ifr.contentDocument || ifr.contentWindow.document; const txt = (doc && doc.body && (doc.body.innerText || doc.body.textContent)) || ''; if (txt.trim().length > 0) return true; } catch {} } } catch {} return false; } function isSectionDone(contextDoc) { const doc = contextDoc || document; try { const videos = doc.querySelectorAll('video, .video-js video'); for (const v of videos) { try { const d = v.duration || 0; const t = v.currentTime || 0; if (!(v.ended || (d > 0 && t / d >= 0.985))) { return false; } } catch { return false; } } const questions = getQuestionContainers(doc); for (const q of questions) { if (!isQuestionAnswered(q)) { return false; } } return true; } catch { return false; } } async function ensureSectionCompletedAndAdvance(contextDoc) { if (!isStudyingChapters) return; try { const doc = contextDoc || document; await autoAnswerInDocument(doc); addLog('检测到当前小节已完成,准备跳转下一小节', 'success'); scheduleSkipChapter(1000); } catch (e) { addLog(`完成度检测出错: ${e.message}`, 'error'); } } async function autoAnswer() { if (isAnswering) { return; } isAnswering = true; updateStatus(true); const isExamPage = /mooc1\.chaoxing\.com\/exam-ans/.test(location.href); if (isExamPage) { saveAnsweringState(); } try { if (isExamPage) { addLog('检测到考试页面,正在点击整卷预览...', 'info'); const previewBtn = document.querySelector('a.completeBtn[onclick*="topreview"]') || Array.from(document.querySelectorAll('a')).find(el => el.textContent.trim() === '整卷预览'); if (previewBtn) { previewBtn.click(); addLog('已点击整卷预览,等待页面加载...', 'info'); await new Promise(resolve => setTimeout(resolve, 2000)); } else { addLog('未找到整卷预览按钮,直接开始答题', 'warning'); } } let questions = getQuestionContainers(document); let foundSelector = QUESTION_CONTAINER_SELECTOR; if (questions.length > 0) { } if (questions.length === 0) { const allElements = document.querySelectorAll('*'); const possibleQuestions = Array.from(allElements).filter(el => { const className = el.className || ''; const id = el.id || ''; const text = el.textContent || ''; return (className + id + text).toLowerCase().includes('题目') || (className + id).toLowerCase().includes('question') || (className + id).toLowerCase().includes('subject') || /^\d+[\.。]/.test(text.trim()); }); if (possibleQuestions.length > 0) { questions = possibleQuestions; } } if (questions.length === 0) { addLog('未找到任何题目,请确保页面已完全加载', 'error'); return; } addLog(`共找到 ${questions.length} 个题目`, 'info'); addLog('正在初始化,请稍候', 'info'); Array.from(questions).forEach((q, idx) => { }); let isFirstProcessedQuestion = true; for (let question of questions) { if (!isAnswering) { break; } const questionInfo = getQuestionInfo(question); if (!questionInfo) { addLog('题目信息获取失败,跳过当前题目', 'error'); continue; } if (!isFirstProcessedQuestion) { addLog(`正在处理题目: ${questionInfo.question.substring(0, 30)}...`); } isFirstProcessedQuestion = false; const answer = await getAnswer(questionInfo); if (answer) { addLog(`获取到答案: ${answer}`); fillAnswer(answer, question, questionInfo.type); } if (isAnswering) { await new Promise(resolve => setTimeout(resolve, 2000)); } } } catch (error) { addLog(`自动答题过程出错: ${error.message}`, 'error'); } finally { isAnswering = false; updateStatus(false); clearAnsweringState(); addLog('答题过程结束', 'success'); } } function init() { const pageTitle = document.title || ''; const currentUrl = location.href || ''; if (isPanelBlockedPage(currentUrl)) { return; } if (pageTitle.includes('课程') || pageTitle === '课程' || pageTitle.includes('课表') || pageTitle === '课表' || pageTitle.includes('AI工作台') || pageTitle === 'AI工作台' || pageTitle.includes('知识点') || pageTitle === '知识点' || pageTitle.includes('章节') || pageTitle === '章节' || pageTitle.includes('资料') || pageTitle === '资料' || pageTitle.includes('错题集') || pageTitle === '错题集' || pageTitle.includes('学习记录') || pageTitle === '学习记录') { let pageType = ''; if (pageTitle.includes('课表')) pageType = '课表'; else if (pageTitle.includes('课程')) pageType = '课程'; else if (pageTitle.includes('AI工作台')) pageType = 'AI工作台'; else if (pageTitle.includes('知识点')) pageType = '知识点'; else if (pageTitle.includes('章节')) pageType = '章节'; else if (pageTitle.includes('资料')) pageType = '资料'; else if (pageTitle.includes('错题集')) pageType = '错题集'; else if (pageTitle.includes('学习记录')) pageType = '学习记录'; addLog(`检测到${pageType}页面,不展现脚本面板`, 'info'); return; } const isCourseDetailPage = () => { if (currentUrl.includes('/mooc2-ans/mycourse/stu') || currentUrl.includes('/mycourse/studentcourse') || currentUrl.includes('course/') && !currentUrl.includes('knowledge')) { const hasNavigationMenu = document.querySelector('.nav-content ul, .stuNavigationList ul'); const hasModuleLinks = document.querySelectorAll('a[title="章节"], a[title="作业"], a[title="考试"], a[title="资料"]').length >= 3; const hasCourseInfo = document.querySelector('.classDl, .sideCon, .nav_side'); const hasCourseId = document.querySelector('#courseid, input[name="courseid"]'); if ((hasNavigationMenu || hasModuleLinks) && hasCourseInfo && hasCourseId) { return true; } } return false; }; if (isCourseDetailPage()) { addLog('检测到课程详情页面,不展现脚本面板', 'info'); return; } const isChapterListPage = () => { const hasChapterList = document.querySelector('.fanyaChapter, .chapter_body, .xs_table'); const hasChapterItems = document.querySelectorAll('.chapter_unit, .chapter_item').length > 0; const hasChapterStructure = document.querySelector('.chapter_th, .chapter_td'); const hasProgressInfo = document.querySelector('.catalog_points_yi, .chapter_head'); const hasSearchBox = document.querySelector('#searchChapterListByName, .dataSearch'); const hasTypicalStructure = hasChapterList && hasChapterStructure && hasProgressInfo; const hasChapterTitles = document.querySelectorAll('.catalog_name, .newCatalog_name').length > 2; const urlIndicatesChapterList = currentUrl.includes('/mycourse/studentcourse') || currentUrl.includes('/studentstudy') && !currentUrl.includes('chapterId='); const hasNoLearningContent = !document.querySelector('video, .video-js, iframe[src*="chaoxing"], .questionLi, .TiMu'); return hasTypicalStructure && hasChapterItems && hasChapterTitles && urlIndicatesChapterList && hasNoLearningContent; }; if (isChapterListPage()) { addLog('检测到章节列表页面,不展现脚本面板', 'info'); return; } if (!hasQuestions() && !hasStudyContentDeep() && !isStudyPage()) { showNoTaskToast(); return; } if (!claimOwnership()) { return; } createdPanelEl = createPanel(); bindPanelEvents(); startHeartbeat(); const answeringState = getAnsweringState(); if (answeringState) { addLog('检测到答题状态,自动继续答题...', 'info'); setTimeout(() => { autoAnswer(); }, 1500); } } // 跨域接力:任何 frame 每 3 秒检查一次会话,必要时自己接管刷章节 setInterval(function() { try { tryAdoptStudySession(); } catch(e) {} }, 3000); if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } function isQuizPageDoc(doc) { try { if (doc.getElementById('form1') && doc.querySelector('#RightCon .newTestTitle')) return true; if (doc.querySelector('.newTestCon form#form1') && doc.querySelector('.ans-cc')) return true; if (doc.querySelector('#form1 input[id^="answertype"]')) return true; if (doc.querySelector('.TiMu, .singleQuesId, .Zy_TItle, ul.Zy_ulTop')) return true; } catch {} return false; } function collectQuizQuestions(doc) { const questions = []; try { const typeInputs = doc.querySelectorAll('input[id^="answertype"]'); typeInputs.forEach((inp) => { try { const id = inp.id.replace('answertype', ''); const qid = id.trim(); const block = doc.querySelector(`.singleQuesId[data="${qid}"]`) || inp.closest('.TiMu') || doc; const typeVal = (inp.value || '').trim(); let type = ''; if (typeVal === '0') type = 'single'; else if (typeVal === '1') type = 'multiple'; else if (typeVal === '3') type = 'judge'; else if (typeVal === '2') type = 'blank'; else if (typeVal === '4') type = 'short'; else { const hasTextInput = block.querySelector('input[type="text"], textarea, [contenteditable="true"], [id^="answerEditor"], iframe[id^="ueditor_"]'); type = hasTextInput ? 'short' : 'text'; } const opts = []; const lis = block.querySelectorAll(`ul.Zy_ulTop li[onclick][qid="${qid}"]`); lis.forEach((li, idx) => { const span = li.querySelector('.num_option, .num_option_dx'); const letter = span?.getAttribute('data') || String.fromCharCode(65 + idx); const txt = (li.querySelector('a.after')?.textContent || '').trim(); opts.push(`${letter}. ${txt}`); }); let qtext = ''; const label = block.querySelector('.Zy_TItle .fontLabel'); if (label) qtext = label.textContent.replace(/\s+/g, ' ').trim(); questions.push({ qid, type, question: qtext, options: opts }); } catch {} }); } catch {} return questions; } function dispatchFieldEvents(el) { try { el.dispatchEvent(new Event('input', { bubbles: true })); } catch {} try { el.dispatchEvent(new Event('change', { bubbles: true })); } catch {} try { el.dispatchEvent(new Event('blur', { bubbles: true })); } catch {} } function getQuizDocUE(doc) { try { const win = (doc && doc.defaultView) || window; if (win && win.UE) return win.UE; } catch {} try { if (typeof UE !== 'undefined') return UE; } catch {} return null; } async function waitForQuizEditorInstance(doc, editorId, timeoutMs = 5000) { const end = Date.now() + timeoutMs; while (Date.now() < end) { try { const UERef = getQuizDocUE(doc); if (UERef && typeof UERef.getEditor === 'function') { const editor = UERef.getEditor(editorId); if (editor) return editor; } } catch {} await new Promise(r => setTimeout(r, 120)); } return null; } async function waitForUeEditorReady(editor, timeoutMs = 4000) { if (!editor) return false; try { if (editor.isReady) return true; } catch {} return await new Promise((resolve) => { let done = false; const finish = (ok) => { if (done) return; done = true; resolve(!!ok); }; try { if (typeof editor.ready === 'function') { editor.ready(() => finish(true)); } } catch {} setTimeout(() => { try { finish(!!editor.isReady); } catch { finish(false); } }, timeoutMs); }); } async function prewarmQuizEditors(doc) { try { const areas = Array.from(doc.querySelectorAll('[id^="answerEditor"]')); if (!areas.length) return; for (let i = 0; i < areas.length; i++) { const area = areas[i]; const editor = await waitForQuizEditorInstance(doc, area.id, 5000); if (!editor) { addLog(`编辑器未就绪:${area.id}`, 'warning'); continue; } const ready = await waitForUeEditorReady(editor, 5000); if (!ready) { addLog(`编辑器初始化超时:${area.id}`, 'warning'); continue; } if (i === 0) await new Promise(r => setTimeout(r, 150)); } const body = doc.body; if (body && body.dataset.cxQuizEditorWarmDelayDone !== '1') { await new Promise(r => setTimeout(r, 3000)); body.dataset.cxQuizEditorWarmDelayDone = '1'; } } catch (e) { addLog(`预热章节测验编辑器失败: ${e.message}`, 'warning'); } } async function setQuizEditorValue(doc, textareaEl, value) { const val = String(value || '').trim(); if (!textareaEl || !val) return false; let done = false; const UERef = getQuizDocUE(doc); try { if (UERef && typeof UERef.getEditor === 'function') { const editor = await waitForQuizEditorInstance(doc, textareaEl.id, 5000); if (editor) { const ready = await waitForUeEditorReady(editor, 4500); if (ready) { editor.setContent(val); try { if (typeof editor.fireEvent === 'function') editor.fireEvent('contentChange'); } catch {} try { if (typeof editor.fireEvent === 'function') editor.fireEvent('selectionchange'); } catch {} done = true; } else { addLog(`编辑器未 ready,转为普通回填:${textareaEl.id}`, 'warning'); } } else { addLog(`未找到编辑器实例,转为普通回填:${textareaEl.id}`, 'warning'); } } } catch (e) { addLog(`编辑器写入失败,转为普通回填:${e.message}`, 'warning'); } try { textareaEl.value = val; dispatchFieldEvents(textareaEl); done = true; } catch (e) { addLog(`textarea 回填失败:${e.message}`, 'warning'); } try { const ifr = textareaEl.parentElement?.querySelector('iframe[id^="ueditor_"]') || textareaEl.closest('.subEditor, .eidtDiv, .divText, .textDIV, .editor')?.querySelector('iframe[id^="ueditor_"]') || doc.querySelector(`iframe[id^="ueditor_"][id*="${textareaEl.id.replace(/[^\w-]/g, '')}"]`); const iframeDoc = ifr && (ifr.contentDocument || ifr.contentWindow?.document); const body = iframeDoc && iframeDoc.body; if (body) { body.innerHTML = val; dispatchFieldEvents(body); done = true; } } catch (e) { addLog(`iframe 编辑区回填失败:${e.message}`, 'warning'); } if (!done) addLog(`编辑器内容写入失败:${textareaEl.id}`, 'error'); return done; } async function fillQuizAnswer(doc, qid, type, answer) { try { const block = doc.querySelector(`.singleQuesId[data="${qid}"]`) || doc; if (!block) return false; if (type === 'single' || type === 'multiple' || type === 'judge') { let letters = []; if (type === 'multiple') { letters = (answer || '').toUpperCase().split(/[,,、\s]+/).filter(Boolean); } else if (type === 'judge') { const val = String(answer || '').trim().toLowerCase(); if (/^a$|对|true|正确/.test(val)) letters = ['A']; else if (/^b$|错|false|错误/.test(val)) letters = ['B']; else if (/^t$/.test(val)) letters = ['A']; else if (/^f$/.test(val)) letters = ['B']; else letters = [(val.match(/[ab]/i) || ['A'])[0].toUpperCase()]; } else { const m = String(answer || '').toUpperCase().match(/[A-Z]/g); letters = m ? m : []; } const ul = block.querySelector('ul.Zy_ulTop'); if (!ul) return false; letters.forEach((L) => { let target = null; if (type === 'judge') { const dataVal = (L === 'A') ? 'true' : 'false'; target = ul.querySelector(`li .num_option[data='${dataVal}'], li .num_option_dx[data='${dataVal}']`) || ul.querySelector(`li .num_option[data='${L}'], li .num_option_dx[data='${L}']`); } else { target = ul.querySelector(`li .num_option[data='${L}'], li .num_option_dx[data='${L}']`); } if (target) { const li = target.closest('li'); safeClick(li); } }); const hidden = doc.getElementById(`answer${qid}`); if (hidden) { const want = (type === 'judge') ? (letters[0] === 'A' ? 'true' : 'false') : letters.join(''); if (!hidden.value || (type !== 'multiple' && hidden.value.toLowerCase() !== want)) { hidden.value = want; const spans = ul.querySelectorAll(`.choice${qid}`); spans.forEach(s => s.classList.remove('check_answer', 'check_answer_dx')); letters.forEach((L) => { let sel = null; if (type === 'judge') { const dv = (L === 'A') ? 'true' : 'false'; sel = ul.querySelector(`.choice${qid}[data='${dv}']`) || ul.querySelector(`.choice${qid}[data='${L}']`); } else { sel = ul.querySelector(`.choice${qid}[data='${L}']`); } if (sel) { const isMulti = !!ul.querySelector('.num_option_dx'); sel.classList.add(isMulti ? 'check_answer_dx' : 'check_answer'); const li = sel.closest('li'); if (li) { li.setAttribute('aria-checked', 'true'); li.setAttribute('aria-pressed', 'true'); } } }); } } return true; } else if (type === 'blank') { const answers = String(answer || '').split(/[,,;;、]\s*/).map(s => s.trim()).filter(Boolean); const ueAreas = Array.from(block.querySelectorAll('[id^="answerEditor"]')); for (let i = 0; i < ueAreas.length; i++) { const ta = ueAreas[i]; const val = answers[i] || ''; if (!val) continue; await setQuizEditorValue(doc, ta, val); if (i === 0) await new Promise(r => setTimeout(r, 120)); } const ifrs = block.querySelectorAll('iframe[id^="ueditor_"]'); ifrs.forEach((ifr, i) => { const val = answers[i] || ''; if (!val) return; try { const d = ifr.contentDocument || ifr.contentWindow?.document; const body = d && d.body; if (body) { body.innerHTML = val; dispatchFieldEvents(body); } } catch {} }); const inputs = [ ...block.querySelectorAll('input[type="text"]'), ...block.querySelectorAll('textarea:not([id^="answerEditor"])') ]; inputs.forEach((el, i) => { const val = answers[i] || ''; if (!val) return; try { const tag = (el.tagName || '').toLowerCase(); if (tag === 'input' || tag === 'textarea') { el.value = val; dispatchFieldEvents(el); } } catch {} }); const hidden = doc.getElementById(`answer${qid}`); if (hidden) hidden.value = answers.join(' '); return true; } else if (type === 'text' || type === 'short' || type === 'essay' || type === 'writing') { const val = String(answer || '').trim(); if (!val) return false; const ueAreas = Array.from(block.querySelectorAll('[id^="answerEditor"]')); for (let i = 0; i < ueAreas.length; i++) { const ta = ueAreas[i]; await setQuizEditorValue(doc, ta, val); if (i === 0) await new Promise(r => setTimeout(r, 120)); } const ifrs = block.querySelectorAll('iframe[id^="ueditor_"]'); ifrs.forEach((ifr) => { try { const d = ifr.contentDocument || ifr.contentWindow?.document; const body = d && d.body; if (body) { body.innerHTML = val; dispatchFieldEvents(body); } } catch {} }); const inputs = [ ...block.querySelectorAll('textarea:not([id^="answerEditor"])'), ...block.querySelectorAll('input[type="text"]') ]; inputs.forEach((el) => { try { const tag = (el.tagName || '').toLowerCase(); if (tag === 'input' || tag === 'textarea') { el.value = val; dispatchFieldEvents(el); } } catch {} }); const hidden = doc.getElementById(`answer${qid}`); if (hidden) hidden.value = val; return true; } return false; } catch { return false; } } function findAndClickQuizSubmitButton(doc) { try { const targetWindow = doc.defaultView || window; const chaoxingSubmitMethods = [ () => { if (typeof targetWindow.btnBlueSubmit === 'function') { targetWindow.btnBlueSubmit(); return true; } return false; }, () => { if (typeof targetWindow.submitCheckTimes === 'function') { targetWindow.submitCheckTimes(); return true; } return false; }, () => { if (typeof targetWindow.submitWork === 'function') { targetWindow.submitWork(); return true; } return false; }, () => { const forms = doc.querySelectorAll('form'); for (const form of forms) { const formAction = form.action || ''; if (formAction.includes('work') || formAction.includes('quiz') || formAction.includes('submit')) { try { form.submit(); return true; } catch (e) { addLog(`提交失败: ${e.message}`, 'error'); } } } return false; } ]; for (const method of chaoxingSubmitMethods) { try { if (method()) return true; } catch (e) { addLog(`提交失败: ${e.message}`, 'error'); } } const submitSelectors = [ 'input[type="submit"][value*="提交"]', 'button[type="submit"]', 'input[value="提交答案"]', 'input[value="提交"]', 'button[onclick*="submit"]', 'button[onclick*="btnBlueSubmit"]', 'button[onclick*="submitCheckTimes"]', '.submit-btn', '.btn-submit', '#submit', '.submit', 'input[id*="submit"]', 'button[id*="submit"]', 'a[onclick*="submit"]', 'input[onclick*="tijiao"]', 'button[onclick*="tijiao"]' ]; for (const selector of submitSelectors) { const submitBtn = doc.querySelector(selector); if (submitBtn && !submitBtn.disabled && !submitBtn.classList.contains('disabled')) { try { const onclick = submitBtn.getAttribute('onclick'); if (onclick) { try { const func = new targetWindow.Function(onclick); func.call(submitBtn); return true; } catch (e) { addLog(`提交失败: ${e.message}`, 'error'); } } if (safeClick(submitBtn)) { return true; } } catch (e) { addLog(`提交失败: ${e.message}`, 'error'); } } } const clickableElements = Array.from(doc.querySelectorAll('input, button, a, span, div')); for (const el of clickableElements) { const text = (el.textContent || el.value || '').trim(); if (/^(提交|提交答案|完成|确认提交)$/.test(text)) { try { const onclick = el.getAttribute('onclick'); if (onclick) { try { const func = new targetWindow.Function(onclick); func.call(el); return true; } catch (e) { addLog(`提交失败: ${e.message}`, 'error'); } } if (safeClick(el)) { return true; } } catch (e) { addLog(`提交失败: ${e.message}`, 'error'); } } } addLog('未找到章节测验提交按钮', 'error'); return false; } catch (e) { addLog(`查找提交按钮失败: ${e.message}`, 'error'); return false; } } function validateAndFixSubmitParams(doc) { try { const targetWindow = doc.defaultView || window; if (typeof targetWindow.workRelationId === 'undefined') { const workIdInputs = doc.querySelectorAll('input[name*="workRelationId"], input[id*="workRelationId"]'); if (workIdInputs.length > 0) { targetWindow.workRelationId = workIdInputs[0].value; } } if (typeof targetWindow.courseId === 'undefined') { const courseIdInputs = doc.querySelectorAll('input[name*="courseId"], input[id*="courseId"]'); if (courseIdInputs.length > 0) { targetWindow.courseId = courseIdInputs[0].value; } } if (typeof targetWindow.classId === 'undefined') { const classIdInputs = doc.querySelectorAll('input[name*="classId"], input[id*="classId"]'); if (classIdInputs.length > 0) { targetWindow.classId = classIdInputs[0].value; } } const questions = doc.querySelectorAll('[class*="TiMu"], [class*="timu"]'); questions.forEach((q, index) => { const qid = q.getAttribute('id') || `question_${index}`; let answerInput = doc.querySelector(`input[name="answer${qid}"], input[id="answer${qid}"]`); if (!answerInput) { answerInput = doc.createElement('input'); answerInput.type = 'hidden'; answerInput.name = `answer${qid}`; answerInput.id = `answer${qid}`; q.appendChild(answerInput); } }); addLog('提交参数验证完成', 'info'); return true; } catch (e) { addLog(`提交参数检查失败: ${e.message}`, 'error'); return false; } } async function handleSubmitConfirmDialog(doc, timeoutMs = 3000) { const startTime = Date.now(); while (Date.now() - startTime < timeoutMs) { try { const confirmDialogSelectors = [ '.popDiv', '.modal', '.dialog', '.alert', '.layui-layer', '.confirm-dialog', '.submit-confirm', '[class*="confirm"]', '[class*="dialog"]', '[class*="modal"]' ]; for (const selector of confirmDialogSelectors) { const dialog = doc.querySelector(selector); if (dialog && dialog.style.display !== 'none' && (dialog.textContent.includes('确认提交') || dialog.textContent.includes('提交') || dialog.textContent.includes('确定'))) { const confirmButtonSelectors = [ 'button[onclick*="submit"]', 'button[value*="提交"]', 'button[value*="确定"]', 'button[value*="确认"]', 'input[type="button"][value*="提交"]', 'input[type="button"][value*="确定"]', 'input[type="button"][value*="确认"]', '.confirm-btn', '.submit-btn', '.ok-btn', 'button:contains("提交")', 'button:contains("确定")', 'button:contains("确认")', 'a[onclick*="submit"]' ]; for (const btnSelector of confirmButtonSelectors) { const confirmBtn = dialog.querySelector(btnSelector) || doc.querySelector(`${selector} ${btnSelector}`); if (confirmBtn && !confirmBtn.disabled) { try { const onclick = confirmBtn.getAttribute('onclick'); if (onclick) { const targetWindow = doc.defaultView || window; const func = new targetWindow.Function(onclick); func.call(confirmBtn); return true; } if (safeClick(confirmBtn)) { return true; } } catch (e) { addLog(`确认提交失败: ${e.message}`, 'error'); } } } const allButtons = dialog.querySelectorAll('button, input[type="button"], a'); for (const btn of allButtons) { const text = (btn.textContent || btn.value || '').trim(); if (/^(提交|确定|确认|OK)$/.test(text)) { try { if (safeClick(btn)) { return true; } } catch (e) { addLog(`确认提交失败: ${e.message}`, 'error'); } } } } } } catch (e) { } await new Promise(r => setTimeout(r, 200)); } return false; } async function waitForQuizSubmitCompletion(doc, timeoutMs = 5000) { const startTime = Date.now(); const originalUrl = doc.location.href; while (Date.now() - startTime < timeoutMs) { try { const successIndicators = [ '.success', '.alert-success', '.msg-success', '[class*="success"]', '[class*="complete"]', '*[text()*="提交成功"]', '*[text()*="完成"]' ]; for (const selector of successIndicators) { const indicator = doc.querySelector(selector); if (indicator && indicator.textContent.includes('成功')) { return true; } } if (doc.location.href !== originalUrl) { return true; } const nextStepSelectors = [ 'button[onclick*="next"]', 'a[onclick*="next"]', 'input[value*="下一"]', 'button[value*="下一"]', '.next-btn', '.btn-next', '#next' ]; for (const selector of nextStepSelectors) { if (doc.querySelector(selector)) { return true; } } } catch (e) { } await new Promise(r => setTimeout(r, 200)); } addLog('章节测验提交等待超时', 'error'); return false; } async function autoAnswerQuizInDocument(doc) { try { if (!isStudyingChapters) return false; if (!isQuizPageDoc(doc)) return false; if (isQuizEffectivelyCompletedDoc(doc)) { markChapterQuizHandledForCurrentSection(); return true; } await injectConsoleDecryptCode(doc); await prewarmQuizEditors(doc); const qs = collectQuizQuestions(doc); if (!qs || qs.length === 0) { if (isQuizEffectivelyCompletedDoc(doc)) { markChapterQuizHandledForCurrentSection(); return true; } return false; } addLog('正在自动完成章节测验...', 'info'); for (const q of qs) { if (!isStudyingChapters) return false; const promptInfo = { type: q.type, question: q.question || `题目 ${q.qid}`, options: q.options || [] }; const ans = await getAnswer(promptInfo); if (ans) { await fillQuizAnswer(doc, q.qid, q.type, ans); } await new Promise(r => setTimeout(r, 1000)); } addLog('章节测验答题完成,正在提交...', 'success'); await new Promise(r => setTimeout(r, 1000)); validateAndFixSubmitParams(doc); let submitSuccess = false; const targetWindow = doc.defaultView || window; try { const originalAlert = targetWindow.alert; targetWindow.alert = function(msg) { if (msg && msg.includes('code-1')) { return; } return originalAlert.call(this, msg); }; if (typeof targetWindow.btnBlueSubmit === 'function') { targetWindow.btnBlueSubmit(); await new Promise(r => setTimeout(r, 1000)); if (typeof targetWindow.submitCheckTimes === 'function') { targetWindow.submitCheckTimes(); } if (typeof targetWindow.noSubmit === 'function') {} submitSuccess = true; } else if (typeof targetWindow.submitWork === 'function') { targetWindow.submitWork(); submitSuccess = true; } else { submitSuccess = findAndClickQuizSubmitButton(doc); } } catch (e) { addLog(`提交流程执行失败: ${e.message}`, 'error'); submitSuccess = findAndClickQuizSubmitButton(doc); } if (submitSuccess) { await new Promise(r => setTimeout(r, 500)); const confirmHandled = await handleSubmitConfirmDialog(doc, 3000); if (confirmHandled) {} const submitCompleted = await waitForQuizSubmitCompletion(doc, 8000); if (submitCompleted) { addLog('章节测验提交完成,准备跳转下一节...', 'success'); markChapterQuizHandledForCurrentSection(); await new Promise(r => setTimeout(r, 2000)); if (isStudyingChapters) { addLog('章节测验提交完成,准备跳转下一节', 'success'); scheduleSkipChapter(2000); } } else { markChapterQuizHandledForCurrentSection(); await new Promise(r => setTimeout(r, 1500)); if (isStudyingChapters) { addLog('章节测验已提交,准备跳转', 'info'); scheduleSkipChapter(1500); } } } else { markChapterQuizHandledForCurrentSection(); await new Promise(r => setTimeout(r, 1000)); if (isStudyingChapters) { addLog('章节测验处理完成,准备跳转', 'info'); scheduleSkipChapter(1500); } } return true; } catch (e) { return false; } } })();