// ==UserScript== // @name 2026 广东专业技术人员继续教育公需课自动刷课助手 // @namespace https://github.com/cxcs // @version 1.1.2 // @description 自用免费,自动挂机自动播放、最小音量、答题、跳转下一节,支持页面刷新后自动恢复,浏览器后台播放 // @author CxCS // @match https://ggfw.hrss.gd.gov.cn/zxpx/auc/play/player* // @tag 刷课 // ==/UserScript== (function () { 'use strict'; if (window.__GD_AUTO_SCRIPT_ACTIVE__) return; window.__GD_AUTO_SCRIPT_ACTIVE__ = true; let isRunning = true; let currentOptionIndex = 0; let hasAttemptedJump = false; let coolUntil = 0; let holdUntil = 0; let fullMute = false; // 随机延迟工具 function rd(min, max) { min = min || 2000; max = max || 5000; return Math.floor(Math.random() * (max - min + 1)) + min; } /** * 控制面板 */ const panel = document.createElement('div'); panel.id = 'gd-auto-panel'; panel.style.cssText = 'position:fixed;top:10px;left:10px;z-index:999999;'; panel.innerHTML = `

广东公需课自动助手 v2.5

作 者:CxCS
自动播放·答题·自动跳转下一章
`; document.body.appendChild(panel); const fullPanel = panel.querySelector('#gd-full'); const collapseBtn = panel.querySelector('#gd-collapse'); const toggleBtn = panel.querySelector('#gd-toggle'); const startBtn = panel.querySelector('#gd-start'); const stopBtn = panel.querySelector('#gd-stop'); function updateBtns() { startBtn.style.display = isRunning ? 'none' : 'block'; stopBtn.style.display = isRunning ? 'block' : 'none'; } toggleBtn.onclick = () => { fullPanel.style.display = 'none'; collapseBtn.style.display = 'block'; }; collapseBtn.onclick = () => { collapseBtn.style.display = 'none'; fullPanel.style.display = 'block'; }; startBtn.onclick = () => { isRunning = true; hasAttemptedJump = false; updateBtns(); console.log('[GD助手] 已启动'); }; stopBtn.onclick = () => { isRunning = false; updateBtns(); console.log('[GD助手] 已暂停'); }; /*const muteBtn = panel.querySelector('#gd-mute'); muteBtn.onclick = () => { fullMute = !fullMute; muteBtn.textContent = fullMute ? '关闭完全静音' : '打开完全静音'; const v = document.querySelector('video'); if (v) keepAudible(v); console.log('[GD助手] 完全静音:' + (fullMute ? '开' : '关')); };*/ updateBtns(); // 默认近静音0.05 function keepAudible(v) { try { if (fullMute) { v.muted = true; v.volume = 0; return; } if (!('userActivation' in navigator) || navigator.userActivation.hasBeenActive) { v.muted = false; v.volume = 0.05; return; } } catch (e) {} v.muted = true; v.volume = 0; } /** * 静音 */ function muteVideo() { const vids = document.querySelectorAll('video'); let ok = false; vids.forEach(v => { if (v.src && (v.src.startsWith('blob:') || v.src.includes('m3u8'))) { try { keepAudible(v); ok = true; } catch (e) {} } }); const vv = document.querySelector('.volume-value'); const vc = document.querySelector('.volume-cursor'); if (vv && vc) { vv.style.height = '0%'; vc.style.bottom = '0%'; const vt = document.querySelector('.prism-info-display'); if (vt) vt.style.display = 'none'; console.log('[GD助手] 强制静音'); return; } if (ok) { console.log('[GD助手] 已静音'); return; } console.warn('[GD助手] 静音失败'); } /** * 跳转下一章 */ let jumpLock = false; function checkVideoCompleted() { if (!isRunning || hasAttemptedJump || jumpLock) return; const p = document.querySelector('.learnpercent span span'); if (!p) return; if (/已完成/.test(p.textContent.trim())) { console.log('[GD助手] 已完成,稍后跳转...'); hasAttemptedJump = true; jumpLock = true; setTimeout(() => { doJump(); jumpLock = false; }, rd(3000, 7000)); } } function doJump() { const items = Array.from(document.querySelectorAll('#content li.level1')); const cur = items.findIndex(li => li.querySelector('.content-learning, .content-finished')); if (cur === -1) { console.log('[GD助手] 无法定位当前项'); return; } for (let i = cur + 1; i < items.length; i++) { const unstart = items[i].querySelector('.content-unstart'); const link = items[i].querySelector('a[href*="regexLogin"]'); if (unstart && link) { const oc = link.getAttribute('onclick'); if (oc && oc.includes('myClick')) { const m = oc.match(/myClick\s*\(\s*['"`]([^'"`]*)['"`]\s*\)/); if (m && m[1]) { try { window.myClick(m[1]); console.log('[GD助手] 已跳转下一节'); return; } catch (e) { console.error('[GD助手] myClick失败:', e); } } } link.click(); return; } } console.log('[GD助手] 全部完成'); } /** * 点击播放 */ function clickPlay() { const btn = document.querySelector('.prism-big-play-btn:not([data-clicked])'); if (btn) { btn.click(); btn.setAttribute('data-clicked', '1'); console.log('[GD助手] 已点击播放'); setTimeout(() => { enablePictureInPicture(); }, 2000); } } // 识别风控提示,触发冷却;返回是否命中 function markRiskCooldown(text) { if (/频繁|异常学习|正常节奏/.test(text)) { coolUntil = Date.now() + rd(120000, 300000); console.log('[GD助手] 检测到风控提示,冷却后再恢复播放:' + Date.now()); return true; } return false; } /** * 关闭弹窗 */ function closeCommonDialogs() { const b = document.querySelector('.messager-button .l-btn'); if (b && b.offsetParent) { const mb = document.querySelector('.messager-body'); if (mb) markRiskCooldown(mb.innerText); console.log('[GD助手] 关闭通用弹窗'); setTimeout(() => b.click(), rd(3000, 6000)); return true; } const f = document.querySelector('.layui-layer-btn a, .layui-layer-btn button, .modal-footer button'); if (f && f.offsetParent && /确定|关闭|知道了/i.test(f.innerText)) { console.log('[GD助手] 关闭结束弹窗'); setTimeout(() => f.click(), rd(3000, 6000)); return true; } return false; } function closeAnswerResultDialog() { const mb = document.querySelector('.messager-button .l-btn'); if (mb && mb.offsetParent) { const body = document.querySelector('.messager-body'); if (body && body.offsetParent) { // 风控弹窗:优先取消/关闭,只有单个"确定"时也点确定 if (markRiskCooldown(body.innerText)) { const btns = Array.from(document.querySelectorAll('.messager-button .l-btn')); const safe = btns.find(x => /取消|关闭|否/.test(x.innerText)) || btns[btns.length - 1] || mb; setTimeout(() => safe.click(), rd(3000, 6000)); return true; } console.log('[GD助手] 关闭答题结果弹窗'); setTimeout(() => mb.click(), rd(3000, 6000)); return true; } } return false; } /** * 自动答题 */ let ansLock = false; function autoAnswer() { if (ansLock) return; const d = document.getElementById('dd'); if (!d) return; const wp = d.closest('.panel.window'); if (!wp || getComputedStyle(wp).display === 'none') return; const form = d.querySelector('#from_ejectque'); if (!form) return; const opts = Array.from(form.querySelectorAll('input[type="radio"], input[type="checkbox"]')); if (!opts.length) return; if (opts.some(o => o.checked)) return; ansLock = true; const isCb = opts[0].type === 'checkbox'; setTimeout(() => { if (isCb) { opts.forEach(o => o.checked = true); } else { opts[currentOptionIndex % opts.length].checked = true; currentOptionIndex++; } setTimeout(() => { const sb = Array.from(d.querySelectorAll('button, a, input[type="button"], input[type="submit"]')) .find(x => /提交|确定|完成|确认/i.test(x.innerText || x.value)); if (sb) sb.click(); ansLock = false; }, rd(1000, 3000)); }, rd()); } /** * 画中画 */ function enablePictureInPicture() { // const v = document.querySelector('video'); // if (!v || !('requestPictureInPicture' in v)) return; // if (document.pictureInPictureElement) return; // v.requestPictureInPicture().catch(() => {}); } /** * 强制播放(节流) */ let lastPlay = 0; let playPending = false; function forcePlay() { const v = document.querySelector('video'); if (!v) return; if (!v.paused && !v.ended) return; if (document.hidden && !document.pictureInPictureElement) return; if (Date.now() < coolUntil) return; const dd = document.getElementById('dd'); const wp = dd && dd.closest('.panel.window'); if (wp && getComputedStyle(wp).display !== 'none') return; if (playPending) return; const now = Date.now(); if (now - lastPlay < 10000) return; playPending = true; lastPlay = now; setTimeout(() => { playPending = false; if (!isRunning) return; if (document.hidden && !document.pictureInPictureElement) return; const vv = document.querySelector('video'); if (!vv || (!vv.paused && !vv.ended)) return; vv.muted = true; vv.volume = 0; try { const p = vv.play(); if (p && p.catch) p.catch(e => { if (e.name !== 'NotAllowedError' && e.name !== 'AbortError') console.warn('[GD助手] 播放失败:', e); }); } catch (e) {} }, rd(3000, 9000)); } /** * 卡死检测 */ let lastT = -1; let stuck = 0; function checkStuck() { if (!isRunning) return; const v = document.querySelector('video'); if (!v || v.paused || v.ended) return; const t = v.currentTime; const dur = v.duration; if (isNaN(dur) || dur <= 0) return; if (lastT === t) { stuck++; if (stuck >= 5) { const n = parseInt(sessionStorage.getItem('gd_reload') || '0'); if (n < 2) { sessionStorage.setItem('gd_reload', n + 1); console.log(`[GD助手] 卡死,第${n + 1}次刷新...`); setTimeout(() => location.reload(), rd(2000, 4000)); } else { console.warn('[GD助手] 刷新次数用完'); sessionStorage.removeItem('gd_reload'); } return; } } else { stuck = 0; } lastT = t; } // 初始化 setInterval(checkStuck, 20000); console.log('[GD助手] 已加载'); setTimeout(() => { if (!isRunning) return; clickPlay(); setTimeout(muteVideo, 5000); }, rd(3000, 6000)); document.addEventListener('visibilitychange', () => { if (!document.hidden) holdUntil = Date.now() + rd(8000, 20000); }); let dialogDirty = true; let lastDialogCheck = 0; new MutationObserver(() => { dialogDirty = true; }).observe(document.body, { childList: true, subtree: true }); /** * 主循环 */ setInterval(() => { if (!isRunning) return; if (Date.now() < holdUntil) return; clickPlay(); forcePlay(); const pv = document.querySelector('video'); if (pv && !pv.paused && !pv.ended) keepAudible(pv); checkVideoCompleted(); if (dialogDirty || Date.now() - lastDialogCheck > 10000) { dialogDirty = false; lastDialogCheck = Date.now(); if (closeAnswerResultDialog()) return; if (closeCommonDialogs()) return; autoAnswer(); } }, 2000); })();