// ==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 (按住拖动)
' +
'✕' +
'
' +
'
' +
'
' +
'
' +
'' +
'' +
'
' +
'
📊 剩余次数: 加载中...
' +
'
' +
'
' +
'
' +
'
' +
'
' +
'
';
}
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();
});
})();