// ==UserScript== // @name 智慧树助手 // @namespace zhs.bzm // @version 1.0.4 // @author BZM // @description 智慧树视频 + 答题,题库对接 tk.n1t.cn // @license MIT // @icon https://pp.myapp.com/ma_icon/0/icon_10559400_1789717606/96 // @match *://*.zhihuishu.com/* // @match *://*.polymas.com/* // @require https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js // @require https://cdn.bootcdn.net/ajax/libs/limonte-sweetalert2/11.1.0/sweetalert2.all.min.js // @require https://cdn.bootcdn.net/ajax/libs/blueimp-md5/2.19.0/js/md5.min.js // @grant unsafeWindow // @grant GM_xmlhttpRequest // @grant GM_setValue // @grant GM_getValue // @grant GM_info // @grant GM_openInTab // @connect tk.n1t.cn // @connect tk.swk.tw // @run-at document-end // ==/UserScript== (function () { 'use strict'; /********************************* * 1. BZM 反检测层(原样保留) *********************************/ (function () { 'use strict'; try { Object.defineProperty(document, 'visibilityState', { get: function () { return 'visible'; }, configurable: true }); Object.defineProperty(document, 'hidden', { get: function () { return false; }, configurable: true }); Object.defineProperty(document, 'webkitVisibilityState', { get: function () { return 'visible'; }, configurable: true }); Object.defineProperty(document, 'webkitHidden', { get: function () { return false; }, configurable: true }); document.addEventListener('visibilitychange', function (e) { e.stopImmediatePropagation(); }, true); document.addEventListener('webkitvisibilitychange', function (e) { e.stopImmediatePropagation(); }, true); } catch (e) {} try { window.requestAnimationFrame = function (cb) { return setTimeout(function () { cb(Date.now()); }, 16); }; window.cancelAnimationFrame = function (id) { clearTimeout(id); }; } catch (e) {} try { Object.defineProperty(navigator, 'webdriver', { get: function () { return undefined; }, configurable: true }); } catch (e) {} try { if (!window.chrome) window.chrome = {}; if (!window.chrome.runtime) window.chrome.runtime = {}; } catch (e) {} try { document.hasFocus = function () { return true; }; window.onblur = null; window.onfocus = null; ['blur', 'focus', 'pagehide', 'pageshow'].forEach(function (evt) { window.addEventListener(evt, function (e) { e.stopImmediatePropagation(); }, true); }); } catch (e) {} try { var _origQuery = navigator.permissions && navigator.permissions.query; if (_origQuery) { navigator.permissions.query = function (desc) { if (desc && desc.name === 'notifications') return Promise.resolve({ state: 'prompt', onchange: null }); return _origQuery.call(navigator.permissions, desc); }; } } catch (e) {} try { var _OrigMO = window.MutationObserver; if (_OrigMO) { window.MutationObserver = function (callback) { var wrapped = function (mutations, observer) { var delay = 50 + Math.random() * 150; setTimeout(function () { callback(mutations, observer); }, delay); }; return new _OrigMO(wrapped); }; window.MutationObserver.prototype = _OrigMO.prototype; } } catch (e) {} try { var _origAppendChild = Node.prototype.appendChild; Node.prototype.appendChild = function (node) { var result = _origAppendChild.call(this, node); try { if (node && node.tagName === 'IFRAME') { setTimeout(function () { patchIframe(node); }, 500); node.addEventListener('load', function () { patchIframe(node); }); } } catch (e) {} return result; }; function keepAliveIframeWindow(win) { try { if (!win || !win.document) return; Object.defineProperty(win.document, 'visibilityState', { get: function () { return 'visible'; }, configurable: true }); Object.defineProperty(win.document, 'hidden', { get: function () { return false; }, configurable: true }); Object.defineProperty(win.document, 'webkitVisibilityState', { get: function () { return 'visible'; }, configurable: true }); Object.defineProperty(win.document, 'webkitHidden', { get: function () { return false; }, configurable: true }); win.document.hasFocus = function () { return true; }; win.document.addEventListener('visibilitychange', function (e) { e.stopImmediatePropagation(); }, true); win.document.addEventListener('webkitvisibilitychange', function (e) { e.stopImmediatePropagation(); }, true); ['blur', 'focus', 'pagehide', 'pageshow'].forEach(function (evt) { win.document.addEventListener(evt, function (e) { e.stopImmediatePropagation(); }, true); }); if (win.navigator) { try { Object.defineProperty(win.navigator, 'webdriver', { get: function () { return undefined; }, configurable: true }); } catch (e) {} } } catch (e) {} } function patchIframe(iframe) { try { var d = iframe.contentDocument || (iframe.contentWindow && iframe.contentWindow.document); if (!d) return; keepAliveIframeWindow(iframe.contentWindow); if (d.__antiDetectPatched) return; d.__antiDetectPatched = true; Object.defineProperty(d, 'visibilityState', { get: function () { return 'visible'; }, configurable: true }); Object.defineProperty(d, 'hidden', { get: function () { return false; }, configurable: true }); Object.defineProperty(d, 'webkitVisibilityState', { get: function () { return 'visible'; }, configurable: true }); Object.defineProperty(d, 'webkitHidden', { get: function () { return false; }, configurable: true }); d.hasFocus = function () { return true; }; d.addEventListener('visibilitychange', function (e) { e.stopImmediatePropagation(); }, true); d.addEventListener('webkitvisibilitychange', function (e) { e.stopImmediatePropagation(); }, true); ['blur', 'focus', 'pagehide', 'pageshow'].forEach(function (evt) { d.addEventListener(evt, function (e) { e.stopImmediatePropagation(); }, true); }); try { if (d.defaultView && d.defaultView.navigator) { Object.defineProperty(d.defaultView.navigator, 'webdriver', { get: function () { return undefined; }, configurable: true }); } } catch (e) {} } catch (e) {} } setTimeout(function () { document.querySelectorAll('iframe').forEach(patchIframe); }, 1000); } catch (e) {} console.log('[笨蛋树助手] 反检测层已加载'); })(); /********************************* * 2. BZM 配置管理(原样保留) *********************************/ var SettingsManager = { _defaults: { showBox: 1, showMaskImg: 1, videoRate: 1.5, forceRate: 0, reviewMode: 0, handleVideoQuiz: 1, answerInterval: 2000, autoAnswer: 1, autoSubmit: 0, autoChangeChapter: 1, insertAnswer: 1, showProgressBar: 1, accuracy: 80 }, get: function (key) { var gv = GM_getValue('setting_' + key, null); if (gv !== null && gv !== undefined) { this._defaults[key] = gv; return gv; } var stored = localStorage.getItem('setting_' + key); if (stored !== null) { if (stored === 'true') return true; if (stored === 'false') return false; if (!isNaN(parseFloat(stored)) && isFinite(stored)) return parseFloat(stored); return stored; } return this._defaults[key]; }, set: function (key, value) { localStorage.setItem('setting_' + key, value); GM_setValue('setting_' + key, value); this._defaults[key] = value; return value; }, load: function () { for (var key in this._defaults) { var v = this.get(key); if (v !== undefined) this._defaults[key] = v; } if (this._defaults.videoRate > 8) this._defaults.videoRate = 8; return this._defaults; }, saveVideoRate: function (rate) { if (rate > 8) rate = 8; this.set('videoRate', rate); GM_setValue('videoRate', rate); }, saveProgressBar: function (show) { this.set('showProgressBar', show ? 1 : 0); GM_setValue('showProgressBar', show ? 1 : 0); } }; var CONFIG = SettingsManager.load(); if (CONFIG.videoRate > 8) CONFIG.videoRate = 8; function updateConfig(key, value) { var oldValue = CONFIG[key]; CONFIG[key] = SettingsManager.set(key, value); if (key === 'videoRate' && CONFIG.videoRate > 8) { CONFIG.videoRate = 8; SettingsManager.set('videoRate', 8); } log('⚙️ 配置已更新: ' + key + ' = ' + value + (oldValue !== value ? ' (已生效)' : ''), 'blue'); } function getRandomInterval() { var base = CONFIG.answerInterval || 2000; return Math.floor(base * (0.8 + Math.random() * 0.7)); } function getPlaybackRate(maxWithoutForce) { maxWithoutForce = maxWithoutForce || 2; var rate = Number(CONFIG.videoRate) || 1; if (CONFIG.forceRate) return Math.min(rate, 16); return Math.min(rate, maxWithoutForce); } function forceRateOn() { return !!CONFIG.forceRate; } /********************************* * 3. BZM 日志(原样保留) *********************************/ var logQueue = []; var logTimer = null; function getTopLogContainer() { try { var topDoc = top.document; var container = topDoc.getElementById('ne-21log'); if (container) return $(container); container = document.getElementById('ne-21log'); if (container) return $(container); return null; } catch (e) { return null; } } function flushLogs() { if (logQueue.length === 0) return; var container = getTopLogContainer(); if (container && container.length) { var logsToShow = logQueue.slice(); logQueue = []; for (var i = logsToShow.length - 1; i >= 0; i--) { container.prepend(logsToShow[i]); } container.scrollTop(0); } if (logTimer) { clearTimeout(logTimer); logTimer = null; } } function scheduleFlushLogs() { if (logTimer) return; logTimer = setTimeout(flushLogs, 100); } function log(str, color) { var time = new Date().toLocaleTimeString(); var logHtml = '
[' + time + '] ' + str + '
'; logQueue.push(logHtml); scheduleFlushLogs(); console.log('[笨蛋树助手][' + time + ']', str); } /****************************** * 4. BZM 题库 API(原样保留) *********************************/ var API_SERVERS = [ { url: 'https://tk.n1t.cn/api/search.php', name: '接口1' }, { url: 'https://tk.swk.tw/api/search.php', name: '接口2' } ]; var currentServerIndex = 0; function getApiKey() { return GM_getValue('api_key', ''); } function hasApiKey() { var key = getApiKey(); return key && key.trim() !== ''; } function getQuotaInfo() { return { remaining: GM_getValue('quota_remaining', 0), freeRemaining: GM_getValue('quota_free_remaining', 0), rechargeBalance: GM_getValue('quota_recharge_balance', 0), todayFreeUsage: GM_getValue('quota_today_free_usage', 0) }; } var quotaInfo = getQuotaInfo(); function saveQuotaInfo() { GM_setValue('quota_remaining', quotaInfo.remaining); GM_setValue('quota_free_remaining', quotaInfo.freeRemaining); GM_setValue('quota_recharge_balance', quotaInfo.rechargeBalance); GM_setValue('quota_today_free_usage', quotaInfo.todayFreeUsage); } function updateQuotaInfo(newQuotaInfo) { if (newQuotaInfo) { if (newQuotaInfo.remaining !== undefined) quotaInfo.remaining = newQuotaInfo.remaining; if (newQuotaInfo.free_remaining !== undefined) quotaInfo.freeRemaining = newQuotaInfo.free_remaining; if (newQuotaInfo.recharge_balance !== undefined) quotaInfo.rechargeBalance = newQuotaInfo.recharge_balance; if (newQuotaInfo.today_free_usage !== undefined) quotaInfo.todayFreeUsage = newQuotaInfo.today_free_usage; saveQuotaInfo(); updateQuotaDisplay(); } } function updateQuotaDisplay() { try { var targetDoc = top.document; if (!targetDoc.querySelector('#quotaInfo')) targetDoc = document; var $quotaInfo = $('#quotaInfo', targetDoc); if ($quotaInfo.length) { if (quotaInfo.remaining > 0) $quotaInfo.html('📊 剩余次数: ' + quotaInfo.remaining + ' | 免费: ' + quotaInfo.freeRemaining); else if (quotaInfo.remaining === 0 && hasApiKey()) $quotaInfo.html('⚠️ 次数已用完 | 点击充值'); else if (!hasApiKey()) $quotaInfo.html('⚠️ 请先设置API密钥'); else $quotaInfo.html('📊 剩余次数: 加载中...'); } } catch (e) {} } function hasEnoughQuota() { quotaInfo = getQuotaInfo(); return hasApiKey() && (quotaInfo.remaining > 0 || quotaInfo.freeRemaining > 0); } function getSubmitDecision() { if (CONFIG.autoSubmit && hasEnoughQuota()) return { action: 'submit', reason: '自动提交' }; return { action: 'save', reason: '自动保存' }; } function testApi(key, serverIndex) { return new Promise(function (resolve, reject) { var apiUrl = API_SERVERS[serverIndex || 0].url; var formData = 'question=' + encodeURIComponent('test') + '&key=' + encodeURIComponent(key) + '&type=' + encodeURIComponent('单选题') + '&options=' + encodeURIComponent(JSON.stringify(['选项A', '选项B'])); var timeoutId = setTimeout(function () { reject('请求超时'); }, 10000); GM_xmlhttpRequest({ method: 'POST', url: apiUrl, headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, data: formData, timeout: 10000, onload: function (xhr) { clearTimeout(timeoutId); try { var res = JSON.parse(xhr.responseText); if (res.code == 1 || res.code == 400) { if (res.quota_info) updateQuotaInfo(res.quota_info); resolve(res); } else reject(res.msg || '密钥无效'); } catch (e) { reject('解析响应失败'); } }, onerror: function () { clearTimeout(timeoutId); reject('网络请求失败'); }, ontimeout: function () { clearTimeout(timeoutId); reject('请求超时'); } }); }); } function getAnswer(typeStr, questionText, optionsData, retryCount) { retryCount = retryCount || 0; return new Promise(function (resolve, reject) { var apiKey = getApiKey(); if (!apiKey || apiKey.trim() === '') { log('❌ 请先设置API密钥', 'red'); reject('NO_API_KEY'); return; } quotaInfo = getQuotaInfo(); if (quotaInfo.remaining <= 0 && quotaInfo.freeRemaining <= 0) { log('❌ API次数已用完', 'red'); reject('QUOTA_EXHAUSTED'); return; } var typeText = TYPE_STR_TO_API_NAME[typeStr] || '单选题'; function tryServer(index) { if (index >= API_SERVERS.length) { log('❌ 所有接口均失败', 'red'); reject('ALL_FAIL'); return; } var apiUrl = API_SERVERS[index].url; var formData = 'question=' + encodeURIComponent(questionText) + '&key=' + encodeURIComponent(apiKey) + '&type=' + encodeURIComponent(typeText); if (optionsData && optionsData.length) formData += '&options=' + encodeURIComponent(JSON.stringify(optionsData)); var timeoutId = setTimeout(function () { tryServer(index + 1); }, 15000); GM_xmlhttpRequest({ method: 'POST', url: apiUrl, headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, data: formData, timeout: 15000, onload: function (xhr) { clearTimeout(timeoutId); try { var res = JSON.parse(xhr.responseText); if (res.code == 1 && res.data) { if (res.quota_info) updateQuotaInfo(res.quota_info); currentServerIndex = index; GM_setValue('current_server_index', index); log('✅ [' + typeText + '] ' + res.data.substring(0, 100), 'purple'); resolve(res.data); } else if (res.code == 400) { log('❌ ' + (res.msg || '密钥无效'), 'red'); reject('KEY_INVALID'); } else tryServer(index + 1); } catch (e) { tryServer(index + 1); } }, onerror: function () { clearTimeout(timeoutId); tryServer(index + 1); }, ontimeout: function () { clearTimeout(timeoutId); tryServer(index + 1); } }); } tryServer(retryCount); }); } /********************************* * 5. BZM 统一题型识别(原样保留) *********************************/ var TYPE_TAG_RE = /【\s*[^】]{1,8}\s*】/; var TYPE_TAG_RE_M = /\[\s*[^\[\]]{1,8}\s*\]/; var TYPE_LABELS = [ [/单选/, 'single'], [/多选/, 'multiple'], [/判断/, 'judge'], [/完型填空|完形填空/, 'cloze'], [/填空/, 'fill'], [/名词解释/, 'term'], [/听力/, 'listening'], [/阅读|材料题?/, 'reading'], [/简答|计算|分析|案例/, 'short'], [/论述/, 'essay'], [/分录/, 'entry'], [/排序/, 'sort'], [/连线|匹配/, 'match'] ]; var TYPE_TO_ID = { single: 0, multiple: 1, fill: 2, cloze: 2, term: 4, judge: 3, listening: 0, reading: 4, short: 4, essay: 4, entry: 4, sort: 4, match: 4, unknown: 4 }; var TYPE_STR_TO_API_NAME = { single: '单选题', multiple: '多选题', judge: '判断题', fill: '填空题', cloze: '填空题', term: '名词解释', listening: '听力题', reading: '阅读理解', short: '简答题', essay: '论述题', entry: '分录题', sort: '排序题', match: '连线题', unknown: '简答题' }; var TYPE_TO_NAME = { single: '单选题', multiple: '多选题', fill: '填空题', cloze: '填空题', term: '名词解释', judge: '判断题', listening: '听力题', reading: '阅读理解', short: '简答题', essay: '论述题', entry: '分录题', sort: '排序题', match: '连线题', unknown: '简答题' }; var CHOICE_ONLY_TYPES = ['single', 'multiple', 'judge', 'listening', 'reading']; var TEXT_ONLY_TYPES = ['fill', 'cloze', 'term', 'short', 'essay', 'entry']; function collectAnswerControls($block) { var out = []; $block.find('input[type=radio],input[type=checkbox],input[type=text],textarea,[contenteditable="true"]').each(function () { if (this.disabled || this.readOnly) return; if (this.type === 'hidden') return; if ($(this).closest('#ne-21box,#ne-21log,#video-progress-bar').length) return; var ph = (this.placeholder || '') + '|' + (this.name || '') + '|' + (this.id || ''); if (/search|captcha|verify|code|keyword|kwd/i.test(ph)) return; out.push(this); }); return out; } function detectQuestionType($block, ctx) { ctx = ctx || {}; var blockText = ''; try { var $clone = $block.clone(); $clone.find('input,textarea,select,button,script,style').remove(); blockText = ($clone.text() || '').replace(/\s+/g, ' ').trim(); } catch (e) { blockText = ($block.text() || '').replace(/\s+/g, ' ').trim(); } var tag = (blockText.match(TYPE_TAG_RE) || blockText.match(TYPE_TAG_RE_M) || [''])[0]; if (tag) { for (var i = 0; i < TYPE_LABELS.length; i++) { if (TYPE_LABELS[i][0].test(tag)) { var tagType = TYPE_LABELS[i][1]; var ctrlsForCheck = collectAnswerControls($block); var hasChoice0 = ctrlsForCheck.some(function (c) { return c.type === 'radio' || c.type === 'checkbox'; }) || $block.find('input[type=radio],input[type=checkbox]').length > 0; var hasText0 = ctrlsForCheck.some(function (c) { return c.tagName === 'TEXTAREA' || c.isContentEditable || (c.tagName === 'INPUT' && (c.type === 'text' || c.type === '')); }) || $block.find('textarea').length > 0; var choiceOnly0 = hasChoice0 && !hasText0; var textOnly0 = hasText0 && !hasChoice0; if (choiceOnly0 && TEXT_ONLY_TYPES.indexOf(tagType) !== -1) break; if (textOnly0 && CHOICE_ONLY_TYPES.indexOf(tagType) !== -1) break; return tagType; } } } var ctrls = collectAnswerControls($block); var hasCheckbox = ctrls.some(function (c) { return c.type === 'checkbox'; }) || $block.find('input[type=checkbox]').length > 0; var hasRadio = ctrls.some(function (c) { return c.type === 'radio'; }) || $block.find('input[type=radio]').length > 0; var hasText = ctrls.some(function (c) { return c.tagName === 'TEXTAREA' || c.isContentEditable || (c.tagName === 'INPUT' && (c.type === 'text' || c.type === '')); }) || $block.find('textarea').length > 0; if (hasCheckbox) return 'multiple'; if (hasRadio) { var $radios = $block.find('input[type=radio]'); if ($radios.length === 2) { var joined = ''; $radios.each(function () { var t = ($(this).closest('label').text() || $(this).text() || '').trim(); joined += t; }); joined = joined.replace(/[\s(()).,.、,::A-Za-z]/g, ''); if (/^(对|错|正确|错误|是|否|√|×|TF|TRUEFALSE)+$/i.test(joined)) return 'judge'; } return 'single'; } if (hasText) return 'fill'; return 'unknown'; } function detectQuestionTypeInfo($block, ctx) { var t = detectQuestionType($block, ctx); return { typeStr: t, typeId: TYPE_TO_ID[t] !== undefined ? TYPE_TO_ID[t] : 4, typeName: TYPE_TO_NAME[t] || '简答题' }; } /********************************* * 6. BZM 通用工具(原样保留) *********************************/ function cleanOptionText(text) { if (!text) return text; var cleaned = text.replace(/<[^>]*>/g, ''); cleaned = cleaned.replace(/^[A-Z][\s]*[.、.))\s]+/, ''); cleaned = cleaned.replace(/^\([A-Z]\)[\s]*/, ''); cleaned = cleaned.replace(/^([A-Z])[\s]*/, ''); cleaned = cleaned.replace(/^[:\s\-_]+/, ''); return cleaned.trim(); } function cleanQuizOptionText(text) { if (!text) return ''; var cleaned = String(text).replace(/<[^>]*>/g, '').replace(/ /g, ' ').trim(); for (var round = 0; round < 3; round++) { var before = cleaned; cleaned = cleaned.replace(/^\s*[A-Za-z]\s*[.、.,,::))]+\s*/, ''); cleaned = cleaned.replace(/^\s*[((]\s*[A-Za-z]\s*[))]\s*/, ''); cleaned = cleaned.replace(/^\s*\d+\s*[.、.,,::))]+\s*/, ''); cleaned = cleaned.replace(/^\s*[((]\s*\d+\s*[))]\s*/, ''); cleaned = cleaned.replace(/^\s*[A-Za-z]\s+/, ''); if (cleaned === before) break; } return cleaned.trim(); } function tidyString(s) { if (!s) return null; var cleaned = s.replace(/<(?!img).*?>/g, '').replace(/^【.*?】\s*/, '').replace(/\s*(\d+\.\d+分)$/, '').trim().replace(/ /g, ''); return cleanOptionText(cleaned); } function tidyQuestion(s) { if (!s) return null; var cleaned = s.replace(/<(?!img).*?>/g, '').replace(/^【.*?】\s*/, '').replace(/\s*(\d+\.\d+分)$/, '').replace(/^\d+[.、]?/, '').trim(); return cleanOptionText(cleaned); } function normalizeForCompare(text) { if (!text) return ''; return text.toUpperCase().replace(/[^\u4e00-\u9fa5A-Z0-9]/g, '').trim(); } function splitAnswer(answer) { if (!answer) return []; var parts = answer.split(/[#]+/).map(function (a) { return a.trim(); }).filter(function (a) { return a !== ''; }); if (parts.length === 0 && answer.trim() !== '') return [answer.trim()]; return parts; } function matchByLengthGuard(normalizedOpts, normalizedAnswerArr, strictOnly) { var hitIdx = []; normalizedOpts.forEach(function (opt, i) { for (var j = 0; j < normalizedAnswerArr.length; j++) { if (opt === normalizedAnswerArr[j] && hitIdx.indexOf(i) === -1) { hitIdx.push(i); break; } } }); if (strictOnly) return hitIdx; if (hitIdx.length < normalizedAnswerArr.length) { normalizedOpts.forEach(function (opt, i) { if (hitIdx.indexOf(i) !== -1) return; for (var j = 0; j < normalizedAnswerArr.length; j++) { var na = normalizedAnswerArr[j]; if (!na) continue; if (opt.indexOf(na) !== -1 && (opt.length - na.length) <= 1) { hitIdx.push(i); break; } if (na.indexOf(opt) !== -1 && (na.length - opt.length) <= 1) { hitIdx.push(i); break; } } }); } if (hitIdx.length > normalizedAnswerArr.length) hitIdx = hitIdx.slice(0, normalizedAnswerArr.length); return hitIdx; } function matchMultiChoiceIndices(optsText, answer) { var ansArr = splitAnswer(answer); var normalizedOpts = optsText.map(function (opt) { return normalizeForCompare(opt); }); var normalizedAnsArr = ansArr.map(function (a) { return normalizeForCompare(a); }).filter(function (a) { return a !== ''; }); return matchByLengthGuard(normalizedOpts, normalizedAnsArr, false); } function isTaskCompleted(task) { if (!task) return false; if (task.isPassed === true) return true; if (task.status === 'completed' || task.status === 'finished') return true; if (task.finished === true) return true; return false; } /********************************* * 7. 引擎层别名表(洗后 ABC → 指向 BZM 原函数) *********************************/ var classifyQ = detectQuestionType; var qFetch = getAnswer; var randGap = getRandomInterval; var pickRate = getPlaybackRate; var multiPickIdx = matchMultiChoiceIndices; function sleep(ms) { return new Promise(function (r) { setTimeout(r, ms * 1000); }); } function awaitEl(fn, timeout, interval) { timeout = timeout || 10000; interval = interval || 300; return new Promise(function (resolve, reject) { var start = Date.now(); var t = setInterval(function () { var r; try { r = typeof fn === 'function' ? fn() : document.querySelector(fn); } catch (e) { r = null; } if (r) { clearInterval(t); resolve(r); return; } if (Date.now() - start > timeout) { clearInterval(t); reject(new Error('等待超时')); } }, interval); }); } function pinRate(video, rate) { try { video.playbackRate = rate; } catch (e) {} try { Object.defineProperty(video, 'playbackRate', { configurable: true, get: function () { return rate; }, set: function () {} }); } catch (e) {} } /********************************* * 8. 进度条(洗后命名) *********************************/ var currentProgressBar = null; var currentVideoName = null; function unmountBar() { if (currentProgressBar) { try { $(currentProgressBar).remove(); } catch (e) {} currentProgressBar = null; } try { var td = top.document; var ex = td.querySelector('#video-progress-bar'); if (ex) $(ex).remove(); } catch (e) {} } function formatDuration(seconds) { if (!seconds || seconds < 0) return '00:00'; var total = Math.max(0, Math.floor(Number(seconds) || 0)); var hours = Math.floor(total / 3600); var minutes = Math.floor((total % 3600) / 60); var secs = total % 60; var pad = function (n) { return String(n).padStart(2, '0'); }; if (hours > 0) return pad(hours) + ':' + pad(minutes) + ':' + pad(secs); return pad(minutes) + ':' + pad(secs); } function mountBar(videoName, duration) { if (!CONFIG.showProgressBar) return; unmountBar(); currentVideoName = videoName; var targetDoc = top.document; var barHtml = '
' + '
' + '🎬 ' + videoName + '' + '0%
' + '
' + '
' + '
' + '00:00' + '剩余: --:--' + '' + formatDuration(duration) + '
' + '
' + '● 学习中' + '⚡ ' + pickRate().toFixed(2) + 'x
'; try { $(targetDoc.body).append(barHtml); currentProgressBar = targetDoc.querySelector('#video-progress-bar'); } catch (e) { $(document.body).append(barHtml); currentProgressBar = document.querySelector('#video-progress-bar'); } } function syncBar(cur, dur) { if (!currentProgressBar || !dur) return; var p = Math.min(100, Math.round((cur / dur) * 100)); var r = Math.max(0, dur - cur); var f = currentProgressBar.querySelector('#progress-bar-fill'); var pe = currentProgressBar.querySelector('#progress-percent'); var ce = currentProgressBar.querySelector('#progress-current'); var re = currentProgressBar.querySelector('#progress-remaining'); if (f) f.style.width = p + '%'; if (pe) pe.textContent = p + '%'; if (ce) ce.textContent = formatDuration(cur); if (re) re.textContent = '剩余: ' + formatDuration(r); } /********************************* * 9. 弹题卡片(洗后结构) *********************************/ var popCard = { match: function (node) { if (!node || node.nodeType !== 1) return false; var h = node.querySelector && (node.querySelector('.TiMu') || node.querySelector('.ans-videoquiz') || node.querySelector('.ans-videoquiz-title') || node.querySelector('.videoquiz') || node.querySelector('.ans-videoquiz-opt')); var i = node.querySelector && (node.querySelector('input[type=radio]') || node.querySelector('input[type=checkbox]') || node.querySelector('.ans-videoquiz-opt label')); return !!(h && i); }, read: function (el) { var root = el.querySelector('.TiMu') || el.querySelector('.ans-videoquiz-title') || el.querySelector('.videoquiz-title') || el.querySelector('.ans-videoquiz') || el; var title = (root.textContent || '').trim(); var arr = []; var ins = el.querySelectorAll('input[type=radio],input[type=checkbox]'); if (ins.length) { ins.forEach(function (x, k) { var lb = x.closest('label') || x.parentElement; var raw = (lb.textContent || '').trim(); arr.push({ i: k, t: cleanQuizOptionText(raw), r: raw, el: x }); }); } else { el.querySelectorAll('.ans-videoquiz-opt li,.ans-videoquiz-opt label').forEach(function (x, k) { var raw = (x.textContent || '').trim(); arr.push({ i: k, t: cleanQuizOptionText(raw), r: raw, el: x }); }); } var type = classifyQ($(el), {}); return { title: title, options: arr, typeStr: type, el: el }; }, tap: function (o) { try { o.el && o.el.click && o.el.click(); var lb = o.el && o.el.closest && o.el.closest('label'); if (lb && lb.click) lb.click(); } catch (e) {} }, write: function (card, ans) { if (!card.options.length) return false; if (card.typeStr === 'judge') { var yes = /对|正确|是|√|T|ri/i.test(ans); var k = yes ? 0 : 1; if (card.options[k]) { this.tap(card.options[k]); return true; } } if (card.typeStr === 'multiple') { var hit = multiPickIdx(card.options.map(function (o) { return o.t; }), ans); if (hit.length) { var self = this; hit.forEach(function (i) { self.tap(card.options[i]); }); return true; } return false; } var arr = splitAnswer(ans); var hit2 = false; var self2 = this; card.options.forEach(function (o) { var no = normalizeForCompare(o.t); arr.forEach(function (a) { if (no === normalizeForCompare(a)) { self2.tap(o); hit2 = true; } }); }); if (hit2) return true; card.options.forEach(function (o) { var no = normalizeForCompare(o.t); arr.forEach(function (a) { var na = normalizeForCompare(a); if ((no.indexOf(na) !== -1 && no.length - na.length <= 1) || (na.indexOf(no) !== -1 && na.length - no.length <= 1)) { self2.tap(o); hit2 = true; } }); }); return hit2; }, roll: function (card) { var arr = card.options || []; if (!arr.length) return false; if (card.typeStr === 'multiple') { var n = 1 + Math.floor(Math.random() * arr.length); var pool = arr.slice(); for (var k = 0; k < n && pool.length; k++) { var pi = Math.floor(Math.random() * pool.length); this.tap(pool[pi]); pool.splice(pi, 1); } return true; } this.tap(arr[Math.floor(Math.random() * arr.length)]); return true; }, fire: function (el) { var b = el.querySelector('.ans-videoquiz-submit,.submit-btn,.confirm,button[type=submit]'); if (b) setTimeout(function () { b.click(); }, 400 + Math.random() * 500); }, resume: function (el, v) { var done = false; function go() { if (done) return; done = true; if (v && v.paused) v.play().catch(function () {}); } try { var mo = new MutationObserver(function () { if (!el.isConnected) { mo.disconnect(); go(); } }); mo.observe(el.parentNode || document.body, { childList: true, subtree: true }); } catch (e) {} var t = setInterval(function () { if (!el.isConnected || el.style.display === 'none') { clearInterval(t); go(); } }, 500); setTimeout(function () { clearInterval(t); go(); }, 8000); } }; function resolvePopQuiz(el) { if (!CONFIG.handleVideoQuiz || el.dataset.__zsx === '1') return; el.dataset.__zsx = '1'; var doc = el.ownerDocument || document; var v = doc.querySelector('video'); if (v && !v.paused) v.pause(); var card = popCard.read(el); var optsData = card.typeStr === 'judge' ? ['对', '错'] : card.options.map(function (o) { return o.t; }); qFetch(card.typeStr, card.title, optsData).then(function (a) { if (!a || a === '暂无答案' || !popCard.write(card, a)) popCard.roll(card); popCard.fire(el); popCard.resume(el, v); }).catch(function () { popCard.roll(card); popCard.fire(el); popCard.resume(el, v); }); } function watchModal(doc) { if (!CONFIG.handleVideoQuiz || !doc || !doc.body) return; doc.querySelectorAll('.ans-videoquiz,.TiMu,.videoquiz').forEach(function (el) { if (popCard.match(el)) resolvePopQuiz(el); }); var mo = new MutationObserver(function (ms) { ms.forEach(function (m) { m.addedNodes.forEach(function (n) { if (n.nodeType !== 1) return; if (popCard.match(n)) resolvePopQuiz(n); else if (n.querySelectorAll) { n.querySelectorAll('.ans-videoquiz,.TiMu,.videoquiz').forEach(function (x) { if (popCard.match(x)) resolvePopQuiz(x); }); } }); }); }); mo.observe(doc.body, { childList: true, subtree: true }); doc.__zsxWatch = true; } function keepModalWatch() { setInterval(function () { if (!CONFIG.handleVideoQuiz) return; document.querySelectorAll('iframe').forEach(function (f) { var d; try { d = f.contentDocument; } catch (e) { return; } if (!d || !d.body || !d.querySelector('video') || d.__zsxWatch) return; watchModal(d); }); }, 3000); } function loopModalCheck(doc) { setInterval(function () { if (!CONFIG.handleVideoQuiz) return; try { doc.querySelectorAll('.ans-videoquiz,.TiMu,.videoquiz').forEach(function (el) { if (popCard.match(el) && el.dataset.__zsx !== '1') resolvePopQuiz(el); }); } catch (e) {} }, 1500); } /********************************* * 10. 视频页(共享课 studyvideoh5) *********************************/ function courseRuntime() { var ctx = { v: null, timer: null, busy: false }; function bridge() { var s = document.createElement('script'); s.textContent = "(function(){addEventListener('message',function(e){if(e.data&&e.data.__k==='zhs_q'){try{var d=document.querySelector('#playTopic-dialog');if(!d||!d.__vue__){postMessage({__k:'zhs_r',ok:0},'*');return}var vm=d.__vue__.$parent.$parent;if(!vm||!vm.topicInfo){postMessage({__k:'zhs_r',ok:0},'*');return}var dto=vm.topicInfo.lessonTestQuestionUseInterfaceDtos;if(!dto||!dto[0]){postMessage({__k:'zhs_r',ok:0},'*');return}var opts=dto[0].testQuestion.questionOptions;var hits=dto[0].answerUs;if(!opts||!hits){postMessage({__k:'zhs_r',ok:0},'*');return}var n=0;opts.forEach(function(o,i){if(hits.indexOf(o.sortUs)!==-1){setTimeout(function(){vm.topicClickQot(opts[i])},800*n);n++}});var delay=Math.max(n*800+2000,5000);setTimeout(function(){vm.testDialog=false;postMessage({__k:'zhs_r',ok:1,n:n},'*')},delay)}catch(err){postMessage({__k:'zhs_r',ok:0,err:err.message},'*')}}})})();"; (document.head || document.documentElement).appendChild(s); s.remove(); } function clrOverlay() { var m = document.querySelector('.v-modal'); if (m) m.remove(); var w = document.querySelector('.brush-warning'); if (w) w.remove(); } function isDone(el) { return !!(el && el.querySelector('.time_icofinish')); } function waitVideo(timeout) { timeout = timeout || 15000; return new Promise(function (resolve, reject) { var start = Date.now(); var t = setInterval(function () { var v = document.querySelector('video'); if (v) { clearInterval(t); resolve(v); return; } if (Date.now() - start > timeout) { clearInterval(t); reject(new Error('等待视频超时')); } }, 500); }); } function tap(el) { if (el) el.dispatchEvent(new MouseEvent('click', { bubbles: true })); } function curTitle() { var t = document.querySelector('li.video.current_play .catalogue_title'); return (t && t.innerText && t.innerText.trim()) || '未知小节'; } function chapterLabel(el) { var t = el.querySelector('.catalogue_title'); var n = el.querySelector('.hour'); var tt = (t && t.innerText && t.innerText.trim()) || ''; var nn = (n && n.textContent && n.textContent.trim()) || ''; return nn ? nn + ' ' + tt : tt; } function playNow() { clrOverlay(); ctx.v = document.querySelector('#vjs_container_html5_api'); if (!ctx.v) return; ctx.v.muted = true; ctx.v.play().catch(function () {}); } function bindVideo() { playNow(); if (!ctx.v) return; ctx.v.addEventListener('play', function () { log(curTitle() + ' - 视频开始播放', 'blue'); var r = forceRateOn() ? pickRate(16) : pickRate(2); pinRate(ctx.v, r); log('已设置倍速: ' + r + 'x', 'blue'); mountBar(curTitle(), ctx.v.duration || 0); }, { once: true }); ctx.v.addEventListener('playing', clrOverlay); ctx.v.addEventListener('pause', function () { if (ctx.v.ended || ctx.busy) return; if (!document.querySelector('#playTopic-dialog')) log('视频暂停,下次轮询恢复..', 'orange'); }); ctx.v.addEventListener('ended', function () { log('当前视频播放完毕', 'green'); unmountBar(); advance(); }); ctx.v.ontimeupdate = function () { syncBar(ctx.v.currentTime, ctx.v.duration || 1); }; } function closeQuizDlg() { var d = document.querySelector('#playTopic-dialog'); var b = d && d.querySelector('.el-dialog__headerbtn'); if (b) tap(b); } function handlePopQuiz() { if (ctx.busy) return Promise.resolve(); var dlg = document.querySelector('#playTopic-dialog'); if (!dlg) return Promise.resolve(); var headerText = dlg.querySelector('.el-dialog__header h4'); headerText = (headerText && headerText.innerText) || ''; if (headerText.indexOf('给你出题啦') === -1 && headerText.indexOf('弹题测验') === -1) return Promise.resolve(); var tt = dlg.querySelector('.radio .topic-title'); if (tt && (tt.querySelector('.right') || tt.querySelector('.error'))) { ctx.busy = true; log('弹题已作答过,关闭..', 'blue'); return sleep(2).then(function () { closeQuizDlg(); ctx.busy = false; return sleep(1).then(playNow); }); } log('检测到弹题,自动作答..', 'purple'); ctx.busy = true; return new Promise(function (resolve) { var to = setTimeout(function () { window.removeEventListener('message', handler); log('弹题作答超时,关闭..', 'orange'); closeQuizDlg(); ctx.busy = false; setTimeout(playNow, 1000); resolve(); }, 20000); function handler(e) { if (!e.data || e.data.__k !== 'zhs_r') return; clearTimeout(to); window.removeEventListener('message', handler); if (e.data.ok) log('弹题自动完成(' + e.data.n + '题)', 'green'); else { log('弹题失败' + (e.data.err ? ': ' + e.data.err : '') + ',关闭..', 'orange'); closeQuizDlg(); } ctx.busy = false; setTimeout(playNow, 1000); resolve(); } window.addEventListener('message', handler); window.postMessage({ __k: 'zhs_q' }, '*'); }); } function onFloat() { var vis = Array.from(document.querySelectorAll('.el-dialog')).find(function (d) { return d.parentElement && d.parentElement.style.display !== 'none'; }); if (!vis) { clrOverlay(); return; } if (vis.ariaLabel === '温馨提示') { var b = vis.querySelector('.el-button.btn'); if (b && b.textContent === '我知道了') { tap(b); log('已关闭温馨提示', 'blue'); setTimeout(playNow, 500); return; } } if (vis.ariaLabel === '弹题测验') handlePopQuiz(); } function findNext() { var all = Array.from(document.querySelectorAll('li.video')); var cur = document.querySelector('li.video.current_play'); if (!cur || !all.length) return null; var idx = all.indexOf(cur); var ordered = all.slice(idx + 1).concat(all.slice(0, idx)); return ordered.find(function (el) { return !isDone(el); }); } function advance() { if (!CONFIG.autoChangeChapter) { log('自动切换章节未开启', 'orange'); return; } var cur = document.querySelector('li.video.current_play .catalogue_title'); cur = (cur && cur.innerText) || '当前章节'; log('「' + cur + '」已完成', 'green'); var next = findNext(); if (next) { log('跳转: ' + chapterLabel(next), 'blue'); tap(next); setTimeout(launch, 5000); } else { log('所有章节已完成!', 'green'); if (ctx.timer) { clearInterval(ctx.timer); ctx.timer = null; } } } function tick() { var cur = document.querySelector('.video.current_play'); if (!cur) return; if (isDone(cur)) { advance(); return; } var v = document.querySelector('#vjs_container_html5_api'); if (v && v.paused && !v.ended && !ctx.busy) onFloat(); } function launch() { clrOverlay(); waitVideo(10000).then(function (v) { ctx.v = v; bindVideo(); if (!ctx.timer) ctx.timer = setInterval(tick, 2000); }).catch(function (e) { log(e.message + ',跳转下一节..', 'orange'); advance(); }); } function start() { log('进入智慧树视频学习页面,开始工作..', 'green'); bridge(); sleep(5).then(function () { var close = document.querySelector('.dialog-read .iconguanbi'); if (close) { var p = close.closest && close.closest('.dialog-read'); if (p && getComputedStyle(p).display !== 'none') { tap(close); log('已关闭阅读弹窗', 'blue'); } } onFloat(); launch(); }); } start(); } /********************************* * 11. AI智慧课 *********************************/ function aiCourseRuntime() { log('进入智慧树AI智慧课页面,开始工作..', 'green'); var RES = { VIDEO: 'video', BOOK: 'book', PPT: 'ppt', TEXT: 'text', LINK: 'link', UNKNOWN: 'unknown' }; var runId = 0, v = null, urlW = null; function tap(el) { if (!el) return; if (typeof el.click === 'function') { el.click(); return; } el.dispatchEvent(new MouseEvent('click', { bubbles: true })); } function getCards() { return Array.from(document.querySelectorAll('[class*="card-container"]')); } function getActive() { return getCards().find(function (c) { return c.classList.contains('active'); }); } function cardTitle(c) { return (c && c.querySelector('.common-text')) || (c && c.querySelector('.video-title')) || (c && c.querySelector('.basic-info-video-card-container')) || c; } function cardType(c) { var ib = c && c.querySelector('.icon-box'); var cls = (ib && ib.className) || ''; if (cls.indexOf('video') !== -1) return RES.VIDEO; if (cls.indexOf('book') !== -1) return RES.BOOK; if (cls.indexOf('ppt') !== -1 || cls.indexOf('other') !== -1) return RES.PPT; if (cls.indexOf('text') !== -1) return RES.TEXT; if (cls.indexOf('link') !== -1) return RES.LINK; return RES.UNKNOWN; } function cardDone(c) { var i = c && c.querySelector('.finished-icon'); if (!i) return false; var t = (i.textContent || '').trim(); return !t || t.indexOf('已完成') !== -1; } function curName() { var s = document.querySelector('.point-title-text'); s = (s && s.textContent && s.textContent.trim()) || ''; if (!s) { var ss = document.querySelector('.section-item-collapse-info.active .section-item-collapse-title'); s = (ss && ss.textContent && ss.textContent.trim()) || '未知章节'; } var c = getActive(); var c1 = c && c.querySelector('.video-title'); var c2 = c && c.querySelector('.common-text'); var cn = (c1 && c1.textContent && c1.textContent.trim()) || (c2 && c2.textContent && c2.textContent.trim()) || '未知小节'; return s + '-' + cn; } function nextCard() { var cs = getCards(); if (!cs.length) return null; var i = cs.findIndex(function (c) { return c.classList.contains('active'); }); var start = i >= 0 ? i + 1 : 0; for (var k = start; k < cs.length; k++) if (!cardDone(cs[k])) return cs[k]; return null; } function parseProgress(s) { var t = s && s.querySelector('.collapse-info-progress .progress-text'); t = (t && t.textContent) || ''; var m = t.replace(/\s/g, '').match(/(\d+)\/(\d+)/); if (!m) return null; return { progress: +m[1], total: +m[2] }; } function nextSection() { var ss = Array.from(document.querySelectorAll('.section-item-collapse-info')); var active = ss.find(function (s) { return s.classList.contains('active'); }); var unf = ss.map(function (s) { return { s: s, p: parseProgress(s) }; }) .filter(function (x) { return x.p && x.p.progress < x.p.total; }); if (!unf.length) return null; var i = unf.findIndex(function (x) { return x.s === active; }); if (i >= 0) return unf[i + 1] && unf[i + 1].s; return unf[0] && unf[0].s; } function setActive(c) { getCards().forEach(function (x) { x.classList.remove('active'); }); if (c) c.classList.add('active'); } function tapCard(c) { tap(cardTitle(c)); setActive(c); return sleep(1); } function cleanV() { if (v) { v.onpause = null; v.onended = null; v = null; } } function processNext(rid) { if (rid !== runId) return Promise.resolve(); var n = nextCard(); if (n) { var c1 = n.querySelector('.common-text'); var c2 = n.querySelector('.video-title'); var t = (c1 && c1.textContent && c1.textContent.trim()) || (c2 && c2.textContent && c2.textContent.trim()) || '下一资源'; log('跳转资源: ' + t, 'blue'); return tapCard(n).then(function () { return sleep(3); }).then(doWork); } return nextSectionJob(rid); } function processVideo(rid) { return new Promise(function (resolve) { var wrapper = document.querySelector('.video-player-wrapper'); if (!wrapper || wrapper.style.display === 'none') { log('视频未加载,跳过', 'orange'); sleep(3).then(function () { processNext(rid).then(resolve); }); return; } awaitEl(function () { var m = document.querySelector('video,audio'); return m && m.src ? m : null; }, 8000).then(function (m) { if (rid !== runId) { resolve(); return; } v = m; v.currentTime = Math.max(v.currentTime, 1); v.volume = 0; v.muted = true; var r = pickRate(2); pinRate(v, r); log('已设置倍速: ' + r + 'x', 'blue'); v.play().catch(function () {}); log('学习中: ' + curName(), 'green'); mountBar(curName(), v.duration || 0); v.ontimeupdate = function () { syncBar(v.currentTime, v.duration || 1); }; v.onpause = function () { if (rid !== runId || !v || v.ended) return; sleep(1).then(function () { v && v.play && v.play().catch(function () {}); }); }; v.onended = function () { if (rid !== runId) return; log('视频完成', 'green'); unmountBar(); sleep(3).then(function () { processNext(rid).then(resolve); }); }; }).catch(function (e) { log('未找到视频,跳转下一资源: ' + e.message, 'orange'); sleep(3).then(function () { processNext(rid).then(resolve); }); }); }); } function processPpt(rid) { return new Promise(function (resolve) { var c = document.querySelector('.preview-section .ppt-preview-container'); if (!c) { processPassive(rid, 'PPT资源').then(resolve); return; } function getPage() { var cu = parseInt((c.querySelector('.page-current') || {}).textContent, 10) || 0; var to = parseInt((c.querySelector('.page-total') || {}).textContent, 10) || 0; return [cu, to]; } var p = getPage(); var cu = p[0], to = p[1]; if (!cu || !to) { processPassive(rid, 'PPT资源').then(resolve); return; } log('PPT共 ' + to + ' 页,从 ' + cu + ' 页开始', 'blue'); function step() { if (rid !== runId || cu >= to) { log('PPT翻页完成', 'green'); processNext(rid).then(resolve); return; } tap(c.querySelector('.next-page-btn')); sleep(4).then(function () { var q = getPage(); cu = q[0]; to = q[1]; step(); }); } step(); }); } function processBook(rid) { return new Promise(function (resolve) { awaitEl('.preview-section .book-mark .book-container', 5000).then(function () { var c = document.querySelector('.preview-section .book-mark .book-container'); if (!c) { processPassive(rid, '电子书资源').then(resolve); return; } log('发现电子书,阅读中..', 'blue'); function step() { if (rid !== runId || c.scrollTop + c.clientHeight >= c.scrollHeight - 2) { log('电子书完成', 'green'); processNext(rid).then(resolve); return; } c.scrollTo({ top: Math.min(c.scrollTop + Math.max(c.clientHeight * 0.8, 200), c.scrollHeight), behavior: 'smooth' }); sleep(1.5).then(step); } step(); }).catch(function () { processPassive(rid, '电子书资源').then(resolve); }); }); } function processPassive(rid, label) { if (rid !== runId) return Promise.resolve(); log(label + '已处理', 'blue'); return sleep(3).then(function () { return processNext(rid); }); } function doWork() { var rid = ++runId; cleanV(); return sleep(5).then(function () { if (rid !== runId) return; var c = getActive(); if (c && cardDone(c)) { log('当前已完成,跳过', 'blue'); c = nextCard(); if (!c) return nextSectionJob(rid); return tapCard(c).then(function () { return sleep(3); }).then(function () { var c2 = getActive() || c; return dispatch(c2, rid); }); } if (!c) c = nextCard(); if (!c && getCards().length) return nextSectionJob(rid); if (!c) { log('无学习资源,跳转下一章节', 'orange'); return nextSectionJob(rid); } return dispatch(c, rid); }); } function dispatch(c, rid) { var t = cardType(c); log('资源类型: ' + t, 'blue'); if (t === RES.VIDEO) return processVideo(rid); if (t === RES.BOOK) return processBook(rid); if (t === RES.PPT) return processPpt(rid); return processPassive(rid, '当前资源'); } function nextSectionJob(rid) { if (rid !== runId) return Promise.resolve(); var n = nextSection(); if (!n) { log('所有必学章节已完成!', 'green'); return Promise.resolve(); } var t = n.querySelector('.section-item-collapse-title'); t = (t && t.textContent && t.textContent.trim()) || '下一章节'; log('跳转: ' + t, 'blue'); tap(n); return sleep(5).then(doWork); } function dismiss() { var d = document.querySelector('.read-note-dialog'); if (d && getComputedStyle(d).display !== 'none') { var b = d.querySelector('button'); if (b) { tap(b); log('已关闭阅读弹窗', 'blue'); } } } function watchUrl() { if (urlW) clearInterval(urlW); var prev = location.href; urlW = setInterval(function () { if (location.href === prev) return; prev = location.href; if (location.href.indexOf('learnPage') !== -1 || location.href.indexOf('singleCourse') !== -1) { log('URL变化,重新初始化..', 'blue'); doWork(); } }, 1500); } function start() { if (location.href.indexOf('singleCourse') !== -1) { log('请点击任意章节开始学习..', 'orange'); watchUrl(); return; } sleep(6).then(function () { dismiss(); awaitEl('.section-item-collapse-info', 15000).catch(function () { log('章节列表加载超时..', 'orange'); }); watchUrl(); doWork(); }); } start(); } /********************************* * 12. 新共享课 *********************************/ function plusCourseRuntime() { log('进入智慧树新共享课页面,开始工作..', 'green'); var v = null, quizT = null, finishT = null, visT = null; var switching = false, busyQuiz = false, visWarn = false; function tap(el) { if (!el) return Promise.resolve(false); el.scrollIntoView && el.scrollIntoView({ behavior: 'smooth', block: 'center' }); return sleep(0.2).then(function () { el.click && el.click(); el.dispatchEvent && el.dispatchEvent(new MouseEvent('click', { bubbles: true })); return true; }); } function rate() { return pickRate(2); } function courseName() { var e = document.querySelector('.top-back-box > span:nth-child(2), .course-name'); var t = (e && e.textContent && e.textContent.trim()) || ''; return t.replace(/^课程名称[::]\s*/, '') || '未知课程'; } function items() { return Array.from(document.querySelectorAll('.child-main')).filter(function (el) { return el.parentElement && el.parentElement.querySelector('.child-time'); }); } function cur() { return items().find(function (el) { return el.classList.contains('current'); }); } function title(i) { var t = i && i.parentElement && i.parentElement.textContent; return (t && t.replace(/\s+/g, ' ').trim()) || '未知章节'; } function done(i) { return !!(i && i.parentElement && i.parentElement.querySelector('.finish-icon')); } function next() { var its = items().filter(function (i) { return !done(i); }); if (!its.length) return null; var c = cur(); var i = its.indexOf(c); return i >= 0 ? its[i + 1] : its[0]; } function cleanV() { if (v) { v.onpause = null; v.onended = null; v = null; } } function handleQuiz() { if (busyQuiz) return Promise.resolve(); var d = document.querySelector('.ai-test-question-wrapper'); if (!d) return Promise.resolve(); busyQuiz = true; var done2 = d.querySelector('.done'); var close = d.querySelector('.close-box'); if (done2) return tap(close).then(function () { return sleep(1).then(function () { busyQuiz = false; }); }); var opts = Array.from(d.querySelectorAll('.options .option')); var p = Promise.resolve(); if (opts.length) p = p.then(function () { return tap(opts[Math.floor(Math.random() * opts.length)]); }).then(function () { return sleep(1); }); return p.then(function () { return tap(document.querySelector('.submit-btn .submits')); }).then(function () { return sleep(1); }).then(function () { return tap(close); }) .then(function () { log('随堂练习已处理', 'green'); }) .then(function () { return sleep(1); }) .then(function () { busyQuiz = false; }); } function fixBar() { var b = document.querySelector('.controlsBar'); if (b) { b.style.display = 'block'; b.style.zIndex = '2'; } } function play() { cleanV(); fixBar(); return awaitEl(function () { var m = document.querySelector('video, audio'); return m && m.src ? m : null; }, 15000).then(function (m) { v = m; v.currentTime = Math.max(v.currentTime, 1); v.volume = 0; v.muted = true; var r = rate(); pinRate(v, r); log('学习中: ' + title(cur()), 'green'); log('已设置倍速: ' + r + 'x', 'blue'); v.play().catch(function () {}); mountBar(title(cur()), v.duration || 0); v.ontimeupdate = function () { syncBar(v.currentTime, v.duration || 1); }; v.onpause = function () { if (!v || v.ended || switching || busyQuiz) return; setTimeout(function () { v && v.play && v.play().catch(function () {}); }, 1000); }; v.onended = function () { if (switching) return; unmountBar(); sleep(2).then(nextChapter); }; }).catch(function (e) { log((e.message || '视频加载失败') + ',跳转下一节..', 'orange'); sleep(2).then(nextChapter); }); } function nextChapter() { if (switching) return Promise.resolve(); if (!CONFIG.autoChangeChapter) { log('自动切换未开启', 'orange'); return Promise.resolve(); } switching = true; cleanV(); var n = next(); if (!n) { log('所有章节已完成!', 'green'); switching = false; return Promise.resolve(); } log('跳转: ' + title(n), 'blue'); return tap(n).then(function () { return sleep(5); }).then(function () { switching = false; play(); }); } function startPoll() { quizT = setInterval(handleQuiz, 3000); finishT = setInterval(function () { if (switching) return; var c = cur(); if (c && done(c)) nextChapter(); }, 2000); visT = setInterval(function () { if (document.visibilityState === 'hidden' && !visWarn) { visWarn = true; log('页面不在前台..', 'orange'); } else if (document.visibilityState === 'visible') visWarn = false; }, 1000); } function start() { log('进入新共享课: ' + courseName(), 'green'); sleep(5).then(function () { awaitEl('.child-main', 20000).catch(function () { log('章节列表加载超时..', 'orange'); }); document.querySelectorAll('.el-overlay,.el-dialog').forEach(function (d) { d.style.display = 'none'; }); startPoll(); play(); }); } start(); } /********************************* * 13. 融合课 *********************************/ function fusionCourseRuntime() { log('进入融合课页面,开始工作..', 'green'); var RES = { VIDEO: 'video', PPT: 'ppt' }; var v = null, idx = 0, list = [], curType = '', stopped = false; var chObs = null, resObs = null; function waitH(sel, t) { t = t || 15000; return new Promise(function (res, rej) { var s = Date.now(); function c() { var el = document.querySelector(sel); if (el && el.offsetHeight > 0) { res(el); return; } if (Date.now() - s > t) { rej(new Error('超时')); return; } setTimeout(c, 100); } c(); }); } function tap(el) { if (el) el.dispatchEvent(new MouseEvent('click', { bubbles: true })); } function type(r) { var ft = r && r.querySelector('.file-type'); if (!ft) return 'unknown'; var sc = ft.classList[1]; if (!sc) return 'unknown'; var t = sc.split('-')[1]; if (t === 'video') return RES.VIDEO; if (t === 'ppt' || t === 'other') return RES.PPT; return t || 'unknown'; } function cards() { return Array.from(document.querySelectorAll('.resources-list .resources-item')); } function curCard() { return cards()[list[idx]] || null; } function detect() { var el = curCard(); curType = el ? type(el) : 'unknown'; } function findV(r, i) { r = r || 3; i = i || 500; return new Promise(function (res) { function c(k) { var m = document.querySelector('#container #vjs_container video'); if (m) { res(m); return; } if (k <= 0) { res(null); return; } setTimeout(function () { c(k - 1); }, i); } c(r); }); } function cleanV() { if (v) { v.removeEventListener('ended', onEnd); v.removeEventListener('playing', onPlay); v.removeEventListener('pause', onPause); v = null; } } function onPlay() { log('视频播放中..', 'blue'); } function onEnd() { log('视频完成', 'green'); unmountBar(); idx++; detect(); processNext(); } function onPause() { if (!v || v.ended) return; log('视频暂停,尝试恢复..', 'orange'); setTimeout(function () { v && !v.ended && v.paused && v.play().catch(function () {}); }, 2000); } function processVideo() { log('发现视频,播放中..', 'blue'); cleanV(); return findV().then(function (m) { if (!m) { log('无视频元素,跳过', 'orange'); idx++; detect(); processNext(); return; } v = m; v.muted = true; var r = forceRateOn() ? pickRate(16) : pickRate(2); pinRate(v, r); log('已设置倍速: ' + r + 'x', 'blue'); v.play().catch(function () {}); mountBar('视频', v.duration || 0); v.ontimeupdate = function () { syncBar(v.currentTime, v.duration || 1); }; v.addEventListener('playing', onPlay, { once: true }); v.addEventListener('pause', onPause); v.addEventListener('ended', onEnd, { once: true }); }).catch(function (e) { log('视频出错: ' + e.message, 'orange'); idx++; detect(); processNext(); }); } function pptInfo() { var c = document.querySelector('.ppt-btn-container'); if (!c) return []; var cu = parseInt((c.querySelector('.page-content .page-current') || {}).innerText, 10) || 0; var to = parseInt((c.querySelector('.page-content .page-total') || {}).innerText, 10) || 0; return cu && to ? [cu, to] : []; } function processPpt() { log('发现PPT,翻页中..', 'blue'); var c = document.querySelector('.ppt-box .ppt-preview-container'); if (!c) { idx++; detect(); processNext(); return Promise.resolve(); } var p = pptInfo(); var cu = p[0], to = p[1]; if (!cu || !to) { idx++; detect(); processNext(); return Promise.resolve(); } if (cu >= to) { log('PPT已到末尾', 'blue'); return sleep(2).then(function () { idx++; detect(); processNext(); }); } log('PPT ' + to + ' 页,从 ' + cu + ' 页开始', 'blue'); function step() { if (stopped || cu >= to) { log('PPT完成', 'green'); return sleep(2).then(function () { idx++; detect(); processNext(); }); } tap(c.querySelector('.next-page-btn')); return sleep(4).then(function () { var q = pptInfo(); cu = q[0]; to = q[1]; return step(); }); } return step(); } function processNext() { if (idx >= list.length) { log('当前小节资源完成', 'green'); return nextSection(); } var el = curCard(); if (!el) { idx++; detect(); return processNext(); } if (idx > 0) { var t = type(el); if (t !== 'video' && t !== 'ppt') { idx++; detect(); return processNext(); } tap(el.querySelector('.basic-info-video-card-container') || el); return sleep(3).then(function () { detect(); log('资源 [' + (idx + 1) + '/' + list.length + '] 类型: ' + curType, 'blue'); if (curType === RES.VIDEO) return processVideo(); if (curType === RES.PPT) return processPpt(); log('不支持: ' + curType, 'orange'); idx++; detect(); return processNext(); }); } detect(); log('资源 [' + (idx + 1) + '/' + list.length + '] 类型: ' + curType, 'blue'); if (curType === RES.VIDEO) return processVideo(); if (curType === RES.PPT) return processPpt(); log('不支持: ' + curType, 'orange'); idx++; detect(); return processNext(); } function allChapters() { return Array.from(document.querySelectorAll('.inner-li li.clearfix')).filter(function (el) { return !el.classList.contains('child-chapter') && !el.classList.contains('forbidClick'); }); } function nextSection() { if (!CONFIG.autoChangeChapter) { log('自动切换未开启', 'orange'); return Promise.resolve(); } var cs = allChapters(); var active = document.querySelector('.clearfix.video.activeNode'); if (!active) { log('无当前章节', 'orange'); return Promise.resolve(); } var t = active.querySelector('.title'); var c = (t && t.innerText && t.innerText.trim()) || '当前章节'; log('「' + c + '」完成', 'green'); var i = cs.indexOf(active); var n = cs[i + 1]; if (n) { var nt = n.querySelector('.title'); log('跳转: ' + ((nt && nt.innerText && nt.innerText.trim()) || '下一节'), 'blue'); n.click(); n.scrollIntoView({ behavior: 'smooth' }); return Promise.resolve(); } log('所有章节已完成!', 'green'); return Promise.resolve(); } function obsChapter() { var l = document.querySelector('.chapterList-box .list'); if (!l) return; chObs = new MutationObserver(function (ms) { ms.forEach(function (m) { if (m.type !== 'attributes' || m.attributeName !== 'class') return; if (m.target.classList.contains('activeNode') || m.target.classList.contains('active')) onChg(); }); }); chObs.observe(l, { attributes: true, attributeFilter: ['class'], subtree: true }); } function obsRes() { var l = document.querySelector('.resource-warp .resources-list'); if (!l) return; resObs = new MutationObserver(function (ms) { ms.forEach(function (m) { if (m.type !== 'attributes' || m.attributeName !== 'class') return; if (m.target.classList.contains('active')) onChg(); }); }); resObs.observe(l, { attributes: true, attributeFilter: ['class'], subtree: true }); } function onChg() { sleep(5).then(function () { stopped = false; initRes(); }); } function initRes() { if (resObs) resObs.disconnect(); return awaitEl('.resource-warp .resources-list .resources-item', 15000).then(function () { var its = cards(); if (!its.length) { log('无资源,跳转下一节..', 'orange'); return nextSection(); } var ai = its.findIndex(function (el) { return el.classList.contains('active'); }); idx = ai >= 0 ? ai : 0; list = its.map(function (_, i) { return i; }); log('发现 ' + list.length + ' 个资源', 'blue'); obsRes(); detect(); return processNext(); }).catch(function (e) { log('资源初始化失败: ' + e.message, 'orange'); return nextSection(); }); } function dismiss() { var p = document.querySelector('.courseRemind.khfaPop'); var b = p && p.querySelector('.header-slot-box .el-icon-error'); if (b) { b.click(); log('已关闭提醒弹窗', 'blue'); } } function watchUrl() { var prev = location.href; setInterval(function () { if (location.href === prev) return; prev = location.href; if (location.href.indexOf('fusioncourseh5') !== -1 || location.href.indexOf('smartcoursestudent') !== -1 || location.href.indexOf('ai-smart-course-student-pro') !== -1) { log('URL变化,重初始化..', 'blue'); setTimeout(initAll, 3000); } }, 1500); } function initAll() { return waitH('.preview-warp').catch(function () { log('页面加载超时..', 'orange'); }).then(function () { dismiss(); obsChapter(); return sleep(5); }).then(initRes); } function start() { sleep(6).then(function () { watchUrl(); initAll(); }); } start(); } /********************************* * 14. 新形态 learnPage *********************************/ function lpCourseRuntime() { log('进入新形态学习页面,开始工作..', 'green'); var v = null, idx = 0, list = []; var RES = { VIDEO: 'video', BOOK: 'book', PPT: 'other' }; function tap(el) { if (el) el.dispatchEvent(new MouseEvent('click', { bubbles: true })); } function type(r) { var ib = r && r.querySelector('.icon-box'); if (!ib) return 'unknown'; var cls = ib.className; if (cls.indexOf('video') !== -1) return RES.VIDEO; if (cls.indexOf('book') !== -1) return RES.BOOK; if (cls.indexOf('other') !== -1 || cls.indexOf('ppt') !== -1) return RES.PPT; return 'unknown'; } function cards() { return Array.from(document.querySelectorAll('.resources-list .resources-item')); } function curCard() { return cards()[list[idx]] || null; } function detect() { var el = curCard(); return el ? type(el) : 'unknown'; } function onComplete() { idx++; return processNext(); } function processVideo() { log('发现视频,播放中..', 'blue'); return sleep(5).then(function () { v = document.querySelector('.preview-section .video-player-wrapper video') || document.querySelector('.preview-section video'); if (!v) { log('无视频元素,跳过', 'orange'); return onComplete(); } v.muted = true; v.play().catch(function () {}); var r = forceRateOn() ? pickRate(16) : pickRate(2); pinRate(v, r); log('已设置倍速: ' + r + 'x', 'blue'); mountBar('视频', v.duration || 0); v.ontimeupdate = function () { syncBar(v.currentTime, v.duration || 1); }; v.addEventListener('pause', function () { if (v.ended) return; if (v.duration > 0 && v.duration - v.currentTime <= 1) { log('视频完成', 'green'); return onComplete(); } setTimeout(function () { v && !v.ended && v.paused && v.play().catch(function () {}); }, 2000); }); v.addEventListener('ended', function () { log('视频完成', 'green'); unmountBar(); onComplete(); }, { once: true }); }); } function processPpt() { log('发现PPT,翻页中..', 'blue'); return sleep(2).then(function () { var tt = document.querySelector('.preview-section .ppt-preview-container .page-total'); var to = parseInt(tt && tt.innerText, 10) || 0; if (!to) { log('无法获取页数,跳过', 'orange'); return onComplete(); } var cs = document.querySelectorAll('.catalogue-container .div-img-container'); var cu = Array.from(cs).findIndex(function (c) { return c.classList.contains('choose'); }) + 1 || 1; log('PPT ' + to + ' 页,从 ' + cu + ' 页开始', 'blue'); function flip(p) { if (p >= to) { log('PPT完成', 'green'); return onComplete(); } tap(document.querySelector('.preview-section .ppt-preview-container .next-page-btn')); return sleep(5).then(function () { return flip(p + 1); }); } return flip(cu); }); } function processBook() { log('发现电子书,阅读中..', 'blue'); return awaitEl('.preview-section .book-mark .book-content').then(function () { return awaitEl('.preview-section .book-mark .canvas-wrapper'); }).then(function () { var ps = document.querySelectorAll('.preview-section .book-mark .canvas-wrapper'); if (!ps.length) { log('无电子书页,跳过', 'orange'); return onComplete(); } var r = ps[0].getBoundingClientRect(); var mt = parseInt(getComputedStyle(ps[0]).marginTop, 10) || 0; var ph = Math.max(r.height + mt, 100); var c = document.querySelector('.preview-section .book-mark .book-container'); if (!c) { log('无滚动容器,跳过', 'orange'); return onComplete(); } function step() { var t = Math.min(c.scrollTop + ph, c.scrollHeight - c.clientHeight); c.scrollTo({ top: t, behavior: 'smooth' }); return sleep(1.5).then(function () { if (c.scrollTop + c.clientHeight >= c.scrollHeight - 2) { log('电子书完成', 'green'); return onComplete(); } return step(); }); } return step(); }).catch(function (e) { log('电子书出错: ' + e.message, 'orange'); return onComplete(); }); } function processNext() { if (idx >= list.length) { log('小节资源完成', 'green'); return nextSection(); } var el = curCard(); if (!el) { log('无资源,跳过', 'orange'); return onComplete(); } var p = Promise.resolve(); if (idx > 0) { tap(el.querySelector('.basic-info-video-card-container') || el); p = sleep(3); } return p.then(function () { var t = type(el); log('资源 [' + (idx + 1) + '/' + list.length + '] 类型: ' + t, 'blue'); if (t === RES.VIDEO) return processVideo(); if (t === RES.BOOK) return processBook(); if (t === RES.PPT) return processPpt(); log('不支持: ' + t, 'orange'); return onComplete(); }); } function nextSection() { if (!CONFIG.autoChangeChapter) { log('自动切换未开启', 'orange'); return Promise.resolve(); } var ss = document.querySelectorAll('.section-item-collapse-info'); var a = document.querySelector('.section-item-collapse-info.active'); if (!a) { log('无活跃章节', 'orange'); return Promise.resolve(); } var t = a.querySelector('.section-item-collapse-title'); var cur = (t && t.innerText && t.innerText.trim()) || '当前'; log('「' + cur + '」完成', 'green'); var arr = Array.from(ss); var i = arr.indexOf(a); if (i + 1 < arr.length) { var n = arr[i + 1]; var nt = n.querySelector('.section-item-collapse-title'); log('跳转: ' + ((nt && nt.innerText && nt.innerText.trim()) || '下一节'), 'blue'); tap(n); return sleep(5).then(initRes); } log('所有章节已完成!', 'green'); return Promise.resolve(); } function initRes() { idx = 0; list = []; v = null; return awaitEl('.resources-detail-card .resources-list .resources-item', 15000).then(function () { var its = cards(); if (!its.length) { log('无资源,跳转下一节..', 'orange'); return nextSection(); } list = its.map(function (_, i) { return i; }); log('发现 ' + list.length + ' 个资源', 'blue'); detect(); return processNext(); }).catch(function (e) { log('资源初始化失败: ' + e.message, 'orange'); return nextSection(); }); } function watchUrl() { var prev = location.href; setInterval(function () { if (location.href === prev) return; var was = prev.indexOf('/learnPage') !== -1; prev = location.href; if (location.href.indexOf('/learnPage') !== -1 && !was) { log('URL变化,重初始化..', 'blue'); setTimeout(initRes, 3000); } }, 1500); } function start() { sleep(6).then(function () { var nd = document.querySelector('.read-note-dialog'); if (nd && getComputedStyle(nd).display !== 'none') { var b = nd.querySelector('button'); if (b) { tap(b); log('已关闭阅读弹窗', 'blue'); } } awaitEl('.preview-content', 15000).catch(function () { log('加载超时..', 'orange'); }); watchUrl(); initRes(); }); } start(); } /********************************* * 15. 新智慧 study/index *********************************/ function wisdomCourseRuntime() { log('进入新智慧学习页面,开始工作..', 'green'); var v = null, vChk = null, quizT = null, pbarT = null, visT = null, finishT = null; var lastReplay = 0, visWarn = false, switching = false, busyQuiz = false; var stopped = false, loggedRate = false; var RK = 'zhs-wisdom-reload'; function tap(el) { if (!el) return Promise.resolve(false); el.scrollIntoView && el.scrollIntoView({ behavior: 'smooth', block: 'center' }); return sleep(0.2).then(function () { var r = el.getBoundingClientRect(); var x = r.left + r.width / 2, y = r.top + r.height / 2; var target = document.elementFromPoint(x, y) || el; var ev = { bubbles: true, cancelable: true, clientX: x, clientY: y, button: 0, buttons: 1 }; target.dispatchEvent(new MouseEvent('mousemove', ev)); target.dispatchEvent(new MouseEvent('mousedown', ev)); target.dispatchEvent(new MouseEvent('mouseup', { bubbles: true, cancelable: true, clientX: x, clientY: y, button: 0, buttons: 0 })); target.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, clientX: x, clientY: y, button: 0, buttons: 0 })); return true; }); } function tapNative(el) { if (!el) return Promise.resolve(false); el.scrollIntoView && el.scrollIntoView({ behavior: 'smooth', block: 'center' }); return sleep(0.2).then(function () { var bc = el.className; var bch = el.querySelector('input') && el.querySelector('input').checked; el.click(); return sleep(0.2).then(function () { var ach = el.querySelector('input') && el.querySelector('input').checked; if (el.className !== bc || ach !== bch) return true; return tap(el); }); }); } function rate() { return pickRate(2); } function getRC() { return Number(sessionStorage.getItem(RK) || 0); } function setRC(n) { sessionStorage.setItem(RK, String(n)); } function fixBar() { var b = document.querySelector('.controlsBar'); if (b) { b.style.display = 'block'; b.style.zIndex = '2'; b.style.overflow = ''; } } function waitCaptcha() { if (!document.querySelector('.yidun_popup')) return Promise.resolve(true); log('检测到验证码,请完成..', 'orange'); return awaitEl(function () { return !document.querySelector('.yidun_popup'); }, 5 * 60 * 1000, 1000) .then(function () { log('验证码完成', 'green'); return true; }) .catch(function () { log('验证码超时..', 'orange'); return false; }); } function courseName() { var e = document.querySelector('.course-name'); var t = (e && e.textContent && e.textContent.trim()) || ''; return t.replace(/^课程名称[::]\s*/, '') || '未知课程'; } function titleOf(i) { return (i && i.textContent && i.textContent.trim()) || '未知章节'; } function items() { var its = Array.from(document.querySelectorAll('.chapter-content .chapter-item')); its = its.map(function (el) { var cs = el.querySelectorAll('.chapter-content-second'); return cs.length ? Array.from(cs) : [el]; }).flat(); return its.filter(function (el) { return !el.querySelector('.finish-icon'); }); } function cur() { return document.querySelector('.chapter-content-second.current, .chapter-item.current'); } function next() { var its = items(); if (!its.length) return null; var c = cur(); var i = its.indexOf(c); return i >= 0 ? its[i + 1] : its[0]; } function closeMastery() { var d = document.querySelector('.masterylevel-body'); if (!d) return Promise.resolve(false); return tap(document.querySelector('.header-box .close-box')).then(function () { return awaitEl(function () { return !document.querySelector('.masterylevel-body'); }, 10000, 200) .then(function () { return true; }).catch(function () { return true; }); }); } function hasWarn() { return Array.from(document.querySelectorAll('.el-overlay .content')).some(function (el) { return el.textContent && el.textContent.indexOf('保持良好的学习习惯') !== -1; }); } function handleWarn() { if (!hasWarn()) return false; stopped = true; log('检测到学习习惯提示,已暂停,请手动处理后刷新..', 'orange'); return true; } function handleQuiz() { if (busyQuiz) return Promise.resolve(); var d = document.querySelector('.ai-class-exercise-dialog'); if (!d) return Promise.resolve(); busyQuiz = true; log('检测到随堂练习', 'purple'); var opts = Array.from(d.querySelectorAll('.ques-list .item .option')); var p = Promise.resolve(); if (opts.length) { p = waitCaptcha().then(function (ok) { if (!ok) return; return tapNative(opts[Math.floor(Math.random() * opts.length)]).then(function () { return sleep(1); }); }); } return p.then(function () { var sb = d.querySelector('.el-dialog__footer .el-button.btn'); if (sb) return tapNative(sb).then(function () { return sleep(1); }); }).then(function () { var cb = d.querySelector('.header-icon'); if (cb) return tapNative(cb); }).then(function () { log('随堂练习完成', 'green'); }) .then(function () { return sleep(1); busyQuiz = false; }); } function setRate(r) { if (!v) return Promise.resolve(); v.playbackRate = r; if (!loggedRate) { loggedRate = true; log('已设置倍速: ' + r + 'x', 'blue'); } return Promise.resolve(); } function cleanV() { if (vChk) { clearInterval(vChk); vChk = null; } if (v) { v.onpause = null; v.onended = null; v = null; } } function replayCurrent() { if (!v || !v.isConnected || !v.ended) return; var now = Date.now(); if (now - lastReplay < 8000) return; lastReplay = now; v.currentTime = 1; setRate(rate()).then(function () { return v.play(); }) .then(function () { log('未出现完成标记,重播当前视频..', 'orange'); }) .catch(function () { log('重播失败,等待下次..', 'orange'); }); } function handleFail(e) { log((e.message || '视频加载失败') + ',刷新重试..', 'orange'); var rc = getRC(); if (rc >= 3) { setRC(0); log('多次失败,请手动刷新..', 'orange'); return; } setRC(rc + 1); setTimeout(function () { location.reload(); }, 3000); } function playVideo() { if (stopped) return Promise.resolve(); return waitCaptcha().then(function (ok) { if (!ok) return; cleanV(); fixBar(); return sleep(1).then(function () { return awaitEl(function () { var m = document.querySelector('video, audio'); return m && m.src ? m : null; }, 15000); }).then(function (m) { v = m; setRC(0); lastReplay = 0; v.currentTime = Math.max(v.currentTime, 1); v.volume = 0; v.muted = true; return setRate(rate()).then(function () { log('学习中: ' + titleOf(cur()), 'green'); mountBar(titleOf(cur()), v.duration || 0); v.ontimeupdate = function () { syncBar(v.currentTime, v.duration || 1); }; v.play().catch(function () {}); vChk = setInterval(function () { if (!v || !v.isConnected) { cleanV(); setTimeout(playVideo, 1500); } }, 3000); v.onpause = function () { if (!v || v.ended || switching) return; setTimeout(function () { v && v.play && v.play().catch(function () {}); }, 1000); }; v.onended = function () { if (!switching) log('视频完成,等待标记..', 'green'); }; }); }); }).catch(function (e) { handleFail(e); }); } function nextChapter() { if (switching || stopped) return Promise.resolve(); if (!CONFIG.autoChangeChapter) { log('自动切换未开启', 'orange'); return Promise.resolve(); } switching = true; cleanV(); var hidden = document.querySelector('.chapter-wrapper.hidden'); var p = hidden ? tap(document.querySelector('.collapse-box') || hidden) : Promise.resolve(); return p.then(function () { document.querySelectorAll('.el-collapse-item__wrap').forEach(function (el) { el.style.display = ''; }); return sleep(0.2); }).then(function () { if (handleWarn()) { switching = false; return; } return closeMastery(); }).then(function () { var n = next(); if (!n) { log('所有章节已完成!', 'green'); switching = false; return; } return tap(n).then(function () { n.scrollIntoView({ behavior: 'smooth', block: 'center' }); return sleep(5); }).then(function () { switching = false; return playVideo(); }); }); } function startPoll() { quizT = setInterval(function () { closeMastery().then(handleQuiz); }, 3000); pbarT = setInterval(fixBar, 1000); finishT = setInterval(function () { if (switching || stopped) return; var c = cur(); if (!c || !c.querySelector('.finish-icon')) { replayCurrent(); return; } nextChapter(); }, 2000); visT = setInterval(function () { if (document.visibilityState === 'hidden' && !visWarn) { visWarn = true; log('页面不在前台..', 'orange'); } else if (document.visibilityState === 'visible') visWarn = false; }, 1000); } function start() { log('进入新智慧: ' + courseName(), 'green'); sleep(5).then(function () { awaitEl('.video-play', 20000).catch(function () { log('视频区域加载超时..', 'orange'); }); document.querySelectorAll('.el-overlay,.el-dialog').forEach(function (d) { d.style.display = 'none'; }); startPoll(); playVideo(); }); } start(); } /********************************* * 16. 教学空间 *********************************/ function studioCourseRuntime() { log('进入教学空间,开始工作..', 'green'); var v = null, finishT = null, visT = null, switching = false, visWarn = false; var PROFILES = [ { name: '校内课', list: '.file-item', current: '.file-item.active', title: '#sourceTit', done: '.icon-finish', media: '#mediaPlayer, video, audio', needs: function (i) { return !i.querySelector('.icon-finish'); } }, { name: '教学空间AI智慧课', list: '.source-icon', current: '.active-file', title: '.active-file', done: 'i.select', media: 'video, audio', map: function (ic) { return ic.parentElement && ic.parentElement.parentElement; }, needs: function (i) { return !i.querySelector('i.select'); } }, { name: '教学空间AI智慧课', list: '.el-tree-node', current: '.el-tree-node.is-current', title: '.file-name', done: 'label.success', media: 'video, audio', filter: function (i) { var c = i.querySelector('.el-tree-node__children'); return !c || !c.children.length; }, needs: function (i) { return !i.querySelector('label.success'); } } ]; function tap(el) { if (!el) return Promise.resolve(false); el.scrollIntoView && el.scrollIntoView({ behavior: 'smooth', block: 'center' }); return sleep(0.2).then(function () { el.click && el.click(); el.dispatchEvent && el.dispatchEvent(new MouseEvent('click', { bubbles: true })); return true; }); } function profile() { return PROFILES.find(function (p) { return document.querySelector(p.list); }); } function items(p) { return Array.from(document.querySelectorAll(p.list)).map(function (i) { return p.map ? p.map(i) : i; }).filter(Boolean).filter(function (i) { return !p.filter || p.filter(i); }); } function cur(p) { return (document.querySelector(p.current) && document.querySelector(p.current).closest(p.list)) || items(p).find(function (i) { return i.matches && i.matches(p.current); }) || null; } function titleOf(i, p) { var el = i && i.querySelector(p.title); return (el && el.textContent && el.textContent.trim()) || (document.querySelector(p.title) && document.querySelector(p.title).textContent.trim()) || (i && i.textContent && i.textContent.trim()) || '未知章节'; } function docs() { var ds = [document]; document.querySelectorAll('iframe').forEach(function (f) { try { if (f.contentDocument) ds.push(f.contentDocument); } catch (e) {} }); return ds; } function isMedia(el) { return el && (el.tagName === 'VIDEO' || el.tagName === 'AUDIO'); } function hasSrc(m) { return !!(m && (m.currentSrc || m.src || (m.querySelector && m.querySelector('source')) || m.readyState > 0)); } function findMedia(sel) { var ds = docs(); for (var i = 0; i < ds.length; i++) { var doc = ds[i]; var cs = Array.from(doc.querySelectorAll(sel)); for (var j = 0; j < cs.length; j++) { var c = cs[j]; var m = isMedia(c) ? c : (c.querySelector && c.querySelector('video, audio')); if (m && (hasSrc(m) || isMedia(m))) return m; } var fb = doc.querySelector('video, audio'); if (fb && (hasSrc(fb) || isMedia(fb))) return fb; } return null; } function next(p) { var its = items(p).filter(function (i) { return p.needs(i); }); if (!its.length) return null; var c = cur(p); var i = its.indexOf(c); return i >= 0 ? its[i + 1] : its[0]; } function cleanV() { if (v) { v.onpause = null; v.onended = null; v = null; } } function play(p) { cleanV(); return awaitEl(function () { return findMedia(p.media); }, 15000).then(function (m) { v = m; v.volume = 0; v.muted = true; var r = forceRateOn() ? pickRate(16) : pickRate(2); pinRate(v, r); log('学习中: ' + titleOf(cur(p), p), 'green'); log('已设置倍速: ' + r + 'x', 'blue'); v.play().catch(function () {}); mountBar(titleOf(cur(p), p), v.duration || 0); v.ontimeupdate = function () { syncBar(v.currentTime, v.duration || 1); }; v.onpause = function () { if (!v || v.ended || switching) return; setTimeout(function () { v && v.play && v.play().catch(function () {}); }, 1000); }; v.onended = function () { unmountBar(); sleep(3).then(function () { nextItem(p); }); }; }).catch(function (e) { log((e.message || '无音视频') + ',跳转下一节..', 'orange'); return sleep(3).then(function () { nextItem(p); }); }); } function nextItem(p) { if (switching) return Promise.resolve(); if (!CONFIG.autoChangeChapter) { log('自动切换未开启', 'orange'); return Promise.resolve(); } switching = true; cleanV(); var n = next(p); if (!n) { log('所有章节已完成!', 'green'); switching = false; return Promise.resolve(); } log('跳转: ' + titleOf(n, p), 'blue'); return tap(n).then(function () { return sleep(5); }).then(function () { switching = false; return play(p); }); } function startPoll(p) { finishT = setInterval(function () { if (switching) return; var c = cur(p); if (c && !p.needs(c)) nextItem(p); }, 2000); visT = setInterval(function () { if (document.visibilityState === 'hidden' && !visWarn) { visWarn = true; log('页面不在前台..', 'orange'); } else if (document.visibilityState === 'visible') visWarn = false; }, 1000); } function start() { if (location.href.indexOf('ai-course-center') !== -1 || (location.href.indexOf('tools-hike') !== -1 && location.href.indexOf('resource') === -1)) { log('请手动进入学习资源页..', 'orange'); return; } sleep(5).then(function () { var p = profile(); if (!p) { log('未识别章节列表,请确认页面已加载..', 'orange'); return; } log('进入教学空间: ' + p.name, 'green'); startPoll(p); play(p); }); } start(); } /********************************* * 17. 答题(智慧树考试/作业) *********************************/ function answerRuntime() { log('进入智慧树答题页面,开始工作..', 'green'); var SHAPES = [ { tag: 'exam-new', root: '.question-area-content', type: "[class*='question-type']", stem: '.text-mainText.font-medium', opts: "label.user-select.group, [role='radio'], [role='checkbox']", before: function () { var fb = document.querySelector('.grid.grid-cols-7 > button'); if (fb) fb.click(); var s = Date.now(); function c() { return new Promise(function (res) { if (Date.now() - s > 10000) { res(); return; } var r = document.querySelector('.question-area-content'); if (r && r.textContent && r.textContent.trim()) { res(); return; } sleep(0.3).then(c).then(res); }); } return c(); }, next: function () { return Array.from(document.querySelectorAll('button')).find(function (b) { return b.textContent && b.textContent.indexOf('下一题') !== -1; }); } }, { tag: 'hw-new', root: '.questionBox', type: '.questionTit', stem: '.questionContent', opts: '.optionUl label', next: 'span.Topicswitchingbtn:nth-child(2)' }, { tag: 'mastery', root: '.questionContent', type: '.questionName', stem: '.questionName .centent-pre', opts: '.radio-view li.clearfix, .checkbox-views label.el-checkbox, .fillAnswer' }, { tag: 'ai-mastery', root: '.exam-item', type: '.quest-type', stem: '.quest-title .option-name', opts: 'label' } ]; function cleanText(t) { return String(t || '').replace(/<[^>]+>/g, '').replace(/ /g, ' ').replace(/\s+/g, ' ').trim(); } function htmlOf(el) { return (el && el.innerHTML) || (el && el.textContent) || ''; } function toSel(s) { return Array.isArray(s) ? s : [s].filter(Boolean); } function queryAll(root, sels) { var r = []; toSel(sels).forEach(function (s) { try { r.push.apply(r, root.querySelectorAll(s)); } catch (e) {} }); return Array.from(new Set(r)); } function queryOne(root, sels) { return queryAll(root, sels)[0] || null; } function pickShape() { var s = Date.now(); function c() { return new Promise(function (res) { if (Date.now() - s > 20000) { res(null); return; } var p = SHAPES.find(function (x) { return document.querySelector(x.root); }); if (p) { res(p); return; } sleep(0.3).then(c).then(res); }); } return c(); } function readShape(shape) { var roots = Array.from(document.querySelectorAll(shape.root)); return roots.map(function (root) { var optEls = queryAll(root, shape.opts).filter(function (o) { return cleanText(o.textContent); }); var optsText = optEls.map(function (o) { return cleanText(o.textContent); }); var optMap = {}; optEls.forEach(function (o, i) { optMap[optsText[i]] = htmlOf(o); }); var titleEl = queryOne(root, shape.stem); var title = cleanText(htmlOf(titleEl)); if (!title) { var set = new Set(optsText.map(function (o) { return normalizeForCompare(o); })); title = queryAll(root, 'div,p,span').map(function (el) { return cleanText(el.textContent); }) .filter(function (t) { return t.length > 4 && !set.has(normalizeForCompare(t)); }) .sort(function (a, b) { return b.length - a.length; })[0] || ''; } return { root: root, title: title, optionsText: optsText, options: optMap, optionEls: optEls }; }).filter(function (q) { return q.title; }); } function isChoice(t) { return ['单选题', '多选题', '判断题'].indexOf(t) !== -1; } function settleChoice(q, arr) { var els = (q.optionEls && q.optionEls.length) ? q.optionEls : queryAll(q.root, "label, [role='radio'], [role='checkbox'], .radio-view li.clearfix, .checkbox-views label.el-checkbox"); var na = arr.map(function (a) { return normalizeForCompare(a); }); var first = na[0]; var yes = /^(正确|是|对|√|t|true|right)$/i.test(first); var no = /^(错误|否|错|×|f|false|wrong)$/i.test(first); var hit = false; els.forEach(function (el) { var t = normalizeForCompare(el.textContent); var j = q.typeStr === 'judge' && ((yes && /^(正确|是|对|√|t|true|right)$/i.test(t)) || (no && /^(错误|否|错|×|f|false|wrong)$/i.test(t))); if (na.indexOf(t) !== -1 || j) { hit = true; el.click(); el.dispatchEvent(new MouseEvent('click', { bubbles: true })); } }); return hit; } function settleBlank(q, arr) { var ins = Array.from(q.root.querySelectorAll('input, textarea, .fillAnswer input, .fillAnswer textarea')); if (!ins.length) return false; ins.forEach(function (el, i) { el.value = arr[i] || arr[0] || ''; el.dispatchEvent(new Event('input', { bubbles: true })); el.dispatchEvent(new Event('change', { bubbles: true })); }); return true; } function settle(q) { var arr = (q.answer && q.answer.answer) || []; if (!arr.length) return false; if (isChoice(q.typeStr)) return settleChoice(q, arr); return settleBlank(q, arr); } function job() { return pickShape().then(function (shape) { if (!shape) { log('未识别答题区', 'orange'); return; } log('进入: ' + shape.tag, 'green'); return Promise.resolve(shape.before && shape.before()).then(function () { var all = []; var ok = 0; var prev = ''; function loop() { var qs = readShape(shape).map(function (q) { q.typeStr = classifyQ($(q.root), {}); return q; }); if (!qs.length) return Promise.resolve(); all.push.apply(all, qs); var chain = Promise.resolve(); qs.forEach(function (q) { chain = chain.then(function () { return qFetch(q.typeStr, q.title, q.optionsText).then(function (a) { if (a && a !== '暂无答案') { q.answer = { code: 1, answer: splitAnswer(a) }; if (settle(q)) ok++; } }).catch(function () {}).then(function () { return sleep(randGap() / 1000); }); }); }); return chain.then(function () { var nxt = typeof shape.next === 'function' ? shape.next() : document.querySelector(shape.next); if (!nxt || nxt.classList.contains('disabled')) { if (!all.length) return; log('答题完成,正确率 ' + (ok / all.length * 100).toFixed(1) + '%', 'green'); return; } var c = (all[all.length - 1] && all[all.length - 1].title) || ''; if (c && c === prev) { if (!all.length) return; log('答题完成,正确率 ' + (ok / all.length * 100).toFixed(1) + '%', 'green'); return; } prev = c; nxt.click(); return sleep(1).then(loop); }); } return loop(); }); }); } job(); } /********************************* * 18. URL 路由表 *********************************/ var URL_TABLE = [ [/ai-smart-course-student-pro\.zhihuishu\.com\/(learnPage|singleCourse)/, aiCourseRuntime], [/smartcoursestudent\.zhihuishu\.com\/(learnPage|singleCourse)/, aiCourseRuntime], [/studyplush5\.zhihuishu\.com/, plusCourseRuntime], [/studywisdomh5\.zhihuishu\.com\/study\/index/, plusCourseRuntime], [/studyvideoh5\.zhihuishu\.com/, courseRuntime], [/smartcourseexam\.zhihuishu\.com\/ReviewExam/, answerRuntime], [/studentexamcomh5\.zhihuishu\.com\/studentReviewTestOrExam/, answerRuntime], [/fusioncourseh5\.zhihuishu\.com\/exam/, answerRuntime], [/studywisdomh5\.zhihuishu\.com\/exam/, answerRuntime], [/wisdom-mooc\.zhihuishu\.com\/exam/, answerRuntime], [/examloop\.zhihuishu\.com\/exam/, answerRuntime], [/zhihuishu\.com\/atHomeworkExam\/stu\/(homeworkQ|examQ)\//, answerRuntime], [/zhihuishu\.com\/aidedteaching\/sourceLearning/, studioCourseRuntime], [/hike-teaching-center\.polymas\.com\/stu-hike\/agent-course-hike\/ai-course-center/, studioCourseRuntime], [/tools-hike\/studentStudyResource/, studioCourseRuntime], [/fusioncourseh5\.zhihuishu\.com/, fusionCourseRuntime], [/zhihuishu\.com\/learnPage/, lpCourseRuntime], [/wisdom-mooc\.zhihuishu\.com\/study\/index/, wisdomCourseRuntime] ]; function dispatchByUrl() { var u = location.href; for (var i = 0; i < URL_TABLE.length; i++) if (URL_TABLE[i][0].test(u)) return URL_TABLE[i][1]; return null; } /********************************* * 19. BZM UI(原样保留) *********************************/ function getPanelHTML() { var actualInterval = getRandomInterval(); return '
' + '
' + '

📚 智慧树助手 v1.0.0 (按住拖动)

' + '✕
' + '
' + '
' + '
' + '
' + '' + '' + '
' + '
📊 剩余次数: 加载中...
' + '
' + '
' + '
' + '🎬 视频倍速' + '' + Number(CONFIG.videoRate).toFixed(2) + 'x
' + '' + '
' + '' + '
' + '' + '' + '
' + '
'; } function bindSettingUI(targetDoc) { var $box = $('#ne-21box', targetDoc); var boxEl = $box[0]; if (boxEl) { var savedLeft = GM_getValue('panel_left', null); var savedTop = GM_getValue('panel_top', null); if (savedLeft !== null && savedTop !== null) { var vw = $(targetDoc).width(); var vh = $(targetDoc).height(); savedLeft = Math.max(0, Math.min(vw - boxEl.offsetWidth, savedLeft)); savedTop = Math.max(0, Math.min(vh - boxEl.offsetHeight, savedTop)); $box.css({ left: savedLeft + 'px', top: savedTop + 'px', right: 'auto' }); } var $handle = $('#ne-21drag-handle', targetDoc); var dragging = false, startX = 0, startY = 0, origLeft = 0, origTop = 0; $handle.off('mousedown.panelDrag').on('mousedown.panelDrag', function (e) { if ($(e.target).is('span, button, input, a')) return; dragging = true; var rect = boxEl.getBoundingClientRect(); origLeft = rect.left; origTop = rect.top; startX = e.clientX; startY = e.clientY; $box.css({ left: origLeft + 'px', top: origTop + 'px', right: 'auto' }); e.preventDefault(); }); $(targetDoc).off('mousemove.panelDrag mouseup.panelDrag') .on('mousemove.panelDrag', function (e) { if (!dragging) return; var dx = e.clientX - startX; var dy = e.clientY - startY; var newLeft = Math.max(0, Math.min($(targetDoc).width() - boxEl.offsetWidth, origLeft + dx)); var newTop = Math.max(0, Math.min($(targetDoc).height() - boxEl.offsetHeight, origTop + dy)); $box.css({ left: newLeft + 'px', top: newTop + 'px' }); }) .on('mouseup.panelDrag', function () { if (!dragging) return; dragging = false; var rect = boxEl.getBoundingClientRect(); GM_setValue('panel_left', Math.round(rect.left)); GM_setValue('panel_top', Math.round(rect.top)); }); } $('#ne-21close', targetDoc).off('click').on('click', function () { $('#ne-21box', targetDoc).hide(); }); var $moreBtn = $('#moreSettingsBtn', targetDoc); var $moreSet = $('#moreSettings', targetDoc); var visible = false; $moreBtn.off('click').on('click', function () { visible = !visible; $moreSet.css('display', visible ? 'block' : 'none'); $moreBtn.text(visible ? '✖️ 收起设置' : '⚙️ 更多设置'); }); var $rateSlider = $('#videoRateSlider', targetDoc); var $rateValue = $('#rateValue', targetDoc); $rateSlider.off('input change').on('input', function () { $rateValue.text(parseFloat(this.value).toFixed(2)); }) .on('change', function () { var newRate = parseFloat(this.value); if (newRate > 8) newRate = 8; updateConfig('videoRate', newRate); SettingsManager.saveVideoRate(CONFIG.videoRate); log('📹 视频倍速: ' + CONFIG.videoRate.toFixed(2) + 'x', 'green'); }); $('#forceRateCheck', targetDoc).off('change').on('change', function () { updateConfig('forceRate', this.checked ? 1 : 0); }); $('#showProgressBarCheck', targetDoc).off('change').on('change', function () { var show = this.checked ? 1 : 0; updateConfig('showProgressBar', show); SettingsManager.saveProgressBar(show); if (show === 0) unmountBar(); }); $('#answerInterval', targetDoc).off('change').on('change', function (e) { var newTime = parseInt(e.target.value); if (newTime >= 500 && newTime <= 10000) updateConfig('answerInterval', newTime); else e.target.value = CONFIG.answerInterval; }); ['autoAnswer', 'autoSubmit', 'autoChangeChapter', 'insertAnswer', 'reviewMode', 'handleVideoQuiz'].forEach(function (id) { var $cb = $('#setting_' + id, targetDoc); if ($cb.length) $cb.off('change').on('change', function () { updateConfig(id, this.checked ? 1 : 0); }); }); $('#goToTkBtn', targetDoc).off('click').on('click', function () { GM_openInTab('https://tk.n1t.cn', { active: true }); }); $('#saveApiKeyBtn', targetDoc).off('click').on('click', function () { var key = $('#apiKeyInput', targetDoc).val(); var $tr = $('#testResult', targetDoc); if (key && key.trim()) { $tr.html('⏳ 测试中...').css('color', '#7d9bb5'); testApi(key.trim(), 0).then(function (res) { GM_setValue('api_key', key.trim()); log('✅ 密钥保存并验证有效', 'green'); if (res.quota_info) { updateQuotaInfo(res.quota_info); $tr.html('✅ 有效!剩余: ' + (res.quota_info.remaining || 0) + ' | 免费: ' + (res.quota_info.free_remaining || 0)).css('color', '#2c7da0'); } else $tr.html('✅ 有效').css('color', '#2c7da0'); $('#apiKeyInput', targetDoc).val('').attr('placeholder', '已设置'); updateQuotaDisplay(); setTimeout(function () { $tr.html(''); }, 5000); }).catch(function (err) { $tr.html('❌ 无效: ' + err).css('color', '#e07c5c'); log('❌ 密钥无效: ' + err, 'red'); setTimeout(function () { $tr.html(''); }, 5000); }); } else { var ek = getApiKey(); if (ek) { testApi(ek, 0).then(function (res) { if (res.quota_info) updateQuotaInfo(res.quota_info); $tr.html('✅ 有效').css('color', '#2c7da0'); updateQuotaDisplay(); setTimeout(function () { $tr.html(''); }, 5000); }).catch(function () { GM_setValue('api_key', ''); quotaInfo = { remaining: 0, freeRemaining: 0, rechargeBalance: 0, todayFreeUsage: 0 }; saveQuotaInfo(); updateQuotaDisplay(); $tr.html('❌ 已失效').css('color', '#e07c5c'); setTimeout(function () { $tr.html(''); }, 5000); }); } else { $tr.html('❌ 请输入密钥').css('color', '#e07c5c'); setTimeout(function () { $tr.html(''); }, 3000); } } }); } function showPikaqiu() { if (!CONFIG.showMaskImg) return; try { var targetDoc = top.document; if (targetDoc.querySelector('#pikaqiu-img')) return; var imgHtml = ''; $(targetDoc.body).append(imgHtml); $('#pikaqiu-img', targetDoc).click(function () { var $box = $('#ne-21box', targetDoc); if ($box.length) { if ($box.css('display') === 'none') $box.css('display', 'block'); else $box.css('display', 'none'); } }); } catch (e) {} } function showBox() { if (!CONFIG.showBox) return; try { var targetDoc = top.document; if (targetDoc.querySelector('#ne-21box')) { flushLogs(); updateQuotaDisplay(); return; } } catch (e) {} setTimeout(function () { try { var targetDoc = top.document; $(targetDoc.body).append(getPanelHTML()); bindSettingUI(targetDoc); $('#ne-21notice', targetDoc).html('
✅ 脚本已加载 v1.0.0 | 智慧树全平台 | 倍速: ' + Number(CONFIG.videoRate).toFixed(2) + 'x
'); var savedKey = getApiKey(); if (savedKey) { $('#apiKeyInput', targetDoc).attr('placeholder', '已设置'); testApi(savedKey, 0).then(function (res) { if (res.quota_info) updateQuotaInfo(res.quota_info); log('✅ 密钥有效', 'green'); }) .catch(function () { GM_setValue('api_key', ''); quotaInfo = { remaining: 0, freeRemaining: 0, rechargeBalance: 0, todayFreeUsage: 0 }; saveQuotaInfo(); updateQuotaDisplay(); log('⚠️ 密钥已失效', 'orange'); }); } else updateQuotaDisplay(); flushLogs(); showPikaqiu(); } catch (e) { console.error('显示悬浮窗失败:', e); } }, 100); } /********************************* * 20. 启动 *********************************/ $(function () { if (CONFIG.videoRate > 8) CONFIG.videoRate = 8; var ss = GM_getValue('current_server_index', 0); if (ss >= 0 && ss < API_SERVERS.length) currentServerIndex = ss; // 反检测:hook setInterval/setTimeout try { var oldSet = unsafeWindow.setInterval; var oldOut = unsafeWindow.setTimeout; unsafeWindow.setInterval = function () { var e = new Error(''); if (e.stack && e.stack.indexOf('checkoutNotTrustScript') !== -1) return -1; return oldSet.apply(this, arguments); }; unsafeWindow.setTimeout = function () { var e = new Error(''); if (e.stack && e.stack.indexOf('checkoutNotTrustScript') !== -1) return -1; return oldOut.apply(this, arguments); }; } catch (e) {} showBox(); var fn = dispatchByUrl(); if (fn) { log('✅ 匹配路由', 'green'); try { fn(); } catch (e) { log('❌ 逻辑执行失败: ' + e.message, 'red'); console.error(e); } } else { log('⚠️ 当前页面暂不支持: ' + location.href, 'orange'); } }); $(document).keydown(function (e) { if (e.keyCode == 120) { var $b = $('#ne-21box'); if (!$b.length) return; if ($b.css('display') === 'none') { $b.show(); log('📌 面板已显示', 'green'); } else { $b.hide(); log('📌 面板已隐藏,按F9恢复', 'blue'); } } }); window.addEventListener('beforeunload', function () { unmountBar(); }); })();