// ==UserScript==
// @name 2026年5月广东省教师继续教育专业课、公需课-自动学习、答题
// @namespace auto-study-gds
// @version 3.2
// @description 自动播放课程视频、自动切换下一节、自动答题、防挂机检测
// @match https://jsxx.gds.edu.cn/study/course/*
// @match https://jsglpt.gds.edu.cn/study/course/*
// @match https://jsxx.gds.edu.cn/*/study/course/*
// @match https://jsglpt.gds.edu.cn/*/study/course/*
// @grant none
// @run-at document-idle
// ==/UserScript==
(function () {
'use strict';
// ============ 配置 ============
var CONFIG = {
VIDEO_CHECK_INTERVAL: 5000, // 每5秒检查视频
MOUSE_MOVE_INTERVAL: 15000, // 每15秒移动鼠标
SCROLL_INTERVAL: 25000, // 每25秒微滚动
SAFE_CLICK_INTERVAL: 45000, // 每45秒安全点击
QUIZ_CHECK_INTERVAL: 2000, // 每2秒检查答题弹窗
NEXT_VIDEO_DELAY: 10000, // 视频结束10秒后切换
RESUME_RETRIES: 3, // 恢复播放重试次数
INIT_DELAY: 3000, // 页面加载后等待3秒
MAX_LOG_LENGTH: 200, // 日志最大条数
};
// 课程ID
var COURSE_ID = (function () {
var m = location.pathname.match(/c_([a-f0-9]{32})/i);
return m ? 'c_' + m[1] : null;
})();
// ============ 日志 ============
var LOG = [];
function log(msg, type) {
type = type || 'info';
var ts = new Date().toLocaleTimeString();
var prefix = {
info: 'INF', warn: 'WRN', error: 'ERR', quiz: 'QUIZ', video: 'VID'
};
var line = '[' + ts + '] [' + (prefix[type] || 'INF') + '] ' + msg;
console.log('[AutoStudy] ' + line);
LOG.push(line);
if (LOG.length > CONFIG.MAX_LOG_LENGTH) LOG.shift();
updatePanelStatus(type + ': ' + msg);
}
// ============ 状态浮窗 ============
var panelCreated = false;
function createStatusPanel() {
if (panelCreated || document.getElementById('_asp')) return;
panelCreated = true;
var panel = document.createElement('div');
panel.id = '_asp';
panel.style.cssText = 'position:fixed;top:10px;right:10px;z-index:999999;' +
'background:rgba(0,0,0,0.88);color:#0f0;padding:10px 14px;border-radius:8px;' +
'font-size:12px;font-family:Consolas,monospace;width:320px;' +
'box-shadow:0 2px 12px rgba(0,0,0,0.4);user-select:none;';
panel.innerHTML =
'
AutoStudy v3.0
' +
'等待视频...
' +
'' +
'' +
'' +
'' +
'' +
'' +
'
';
document.body.appendChild(panel);
// 暂停/继续
document.getElementById('_aspToggle').onclick = function () {
window._autoStudyPaused = !window._autoStudyPaused;
this.textContent = window._autoStudyPaused ? '继续' : '暂停';
this.style.background = window._autoStudyPaused ? '#533' : '#333';
log(window._autoStudyPaused ? '脚本已暂停' : '脚本已继续', 'warn');
};
// 手动下一节
document.getElementById('_aspNext').onclick = function () {
log('手动切换下一节', 'warn');
forceGoToNextVideo();
};
// 显示/隐藏日志
var logVisible = false;
document.getElementById('_aspLogToggle').onclick = function () {
logVisible = !logVisible;
var logEl = document.getElementById('_aspLog');
logEl.style.display = logVisible ? 'block' : 'none';
logEl.style.maxHeight = logVisible ? '200px' : '60px';
if (logVisible) {
logEl.textContent = LOG.slice(-20).join('\n');
}
};
}
function updatePanelStatus(msg) {
var el = document.getElementById('_aspLog');
if (el && el.style.display !== 'none') {
el.textContent = LOG.slice(-20).join('\n');
}
}
function updateVideoDisplay() {
var v = getVideo();
var el = document.getElementById('_aspVideo');
if (!el) return;
if (!v || !v.duration) {
el.textContent = '等待视频加载...';
return;
}
var cur = formatTime(v.currentTime);
var dur = formatTime(v.duration);
var pct = (v.currentTime / v.duration * 100).toFixed(1);
var state = v.paused ? ' || PAUSED' : v.ended ? ' || ENDED' : ' || PLAYING';
el.textContent = cur + '/' + dur + ' (' + pct + '%)' + state;
// 标题
var titleEl = document.getElementById('_aspTitle');
if (titleEl) {
var h3 = document.querySelector('.g-studyAct-box h3');
var name = h3 ? h3.textContent.trim().substring(0, 28) : '未知';
titleEl.textContent = 'AutoStudy - ' + name;
}
}
// ============ 防挂机 ============
function spoofVisibility() {
try {
Object.defineProperty(document, 'hidden', { get: function () { return false; }, configurable: true });
} catch (e) { }
try {
Object.defineProperty(document, 'visibilityState', { get: function () { return 'visible'; }, configurable: true });
} catch (e) { }
// 阻止 visibilitychange / blur 事件
['visibilitychange', 'webkitvisibilitychange'].forEach(function (evtName) {
document.addEventListener(evtName, function (e) { e.stopImmediatePropagation(); }, true);
});
window.addEventListener('blur', function (e) { e.stopImmediatePropagation(); }, true);
log('页面可见性伪装已启用');
}
// ============ 鼠标模拟 ============
function simulateMouseMove(x, y) {
var el = document.elementFromPoint(x, y);
['mouseenter', 'mouseover', 'mousemove'].forEach(function (type) {
var evt = new MouseEvent(type, {
clientX: x, clientY: y,
bubbles: true, cancelable: true, view: window
});
if (el) el.dispatchEvent(evt);
document.dispatchEvent(evt);
});
}
function simulateSafeClick(x, y) {
var el = document.elementFromPoint(x, y);
if (!el) return;
var tag = el.tagName.toLowerCase();
if (['video', 'input', 'select', 'textarea', 'a', 'button'].indexOf(tag) >= 0) return;
if (el.closest('video, input, select, textarea, a.btn, button')) return;
['mousedown', 'mouseup', 'click'].forEach(function (type) {
el.dispatchEvent(new MouseEvent(type, {
clientX: x, clientY: y,
bubbles: true, cancelable: true, button: 0
}));
});
}
// ================================================================
// 自动答题模块
// 弹窗结构: .mylayer-layer > .mylayer-content > #questionDiv +