// ==UserScript==
// @name 2026广东干部培训网络学院自动学习助手
// @namespace https://gdpx-shop.pages.dev
// @version 4.1.0
// @description 自动播放、播完自动切换下一课、卡死自动续播、动态水印。内置 20 分钟免费试用,到期后可付费激活永久使用。
// @author LEEMO2023
// @match https://gbpx.gd.gov.cn/*
// @match https://wcs1.shawcoder.xyz/*
// @match https://cs1.gdgbpx.com/*
// @match *://*gzqinghui.com.cn/*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_deleteValue
// @grant GM_xmlhttpRequest
// @grant GM_registerMenuCommand
// @grant GM_setClipboard
// @grant window.close
// @grant unsafeWindow
// @connect *
// @run-at document-idle
// @noframes
// @license MIT
// @homepageURL https://gdpx-shop.pages.dev
// @supportURL https://gdpx-shop.pages.dev/help.html
// @antifeature payment 本脚本内置 20 分钟免费试用,到期后需付费购买激活码才能继续使用
// ==/UserScript==
(function () {
'use strict';
/* ================== 配置区:改成你自己的服务地址 ================== */
var CONFIG = {
// 授权服务端根地址(结尾必须带 /)。没有服务器请留空字符串 '',走纯卡密离线路线
// 当这里填了真实地址,脚本会自动切换成"扫码付款 → 自动出码 → 自动激活"模式
API_BASE: '',
// 购买页地址(暂无在线支付通道时使用)。付款后由客服/网页把激活码发给你
SHOP_URL: 'https://gdpx-shop.pages.dev',
// 免费试用时长(分钟),到点后弹出付款窗并暂停刷课
TRIAL_MINUTES: 20,
// 单个视频最长播放时间(分钟)保险丝,超时强制关窗换下一课
MAX_VIDEO_MINUTES: 12,
// 视频被认为看完的进度阈值(%)
DONE_PERCENT: 98,
// 课程列表页判定"已完成"的阈值(%)
COURSE_DONE_PERCENT: 95,
DEBUG: true
};
/* 服务端公钥(SPKI PEM)。由 tools/genkey.js 生成,与服务端 keys/private.pem 配对 */
var PUBLIC_KEY_PEM = [
'-----BEGIN PUBLIC KEY-----',
'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhMsflDqHj1qGdNcBJrhe',
'0f8GJwsEo1acQFDFcUA6HJFjUzbGO6SbgXsbnuBvUYhL25KbOMFQ9/4udbEIY83y',
'pqOoqEAo9T7ayR2qrx2mR+htvYjTt1gid9D85UA4WAvt0a3GO5mMVhJ8KEzZU4z9',
'PJC4W1eY9AlAAWQeyeivEuiNywBjl8vJNRrRai5pThKWHbgut2dtspdJCQZV4TJV',
'XQd6oVsqfVO3cgJlfoa0a0Ex/vGoj8EKapuoEXAW3WbRAx+v4B3YvRugwmP1KduJ',
'LqWj34SBZYpVEUZfPfz91DWNKD+5yJml8FW7S5coN1YeLwRQGFbQq0i4KvwtHKHw',
'UwIDAQAB',
'-----END PUBLIC KEY-----'
].join('\n');
var LOG_PREFIX = '[学习助手]';
function log() {
if (!CONFIG.DEBUG) return;
var a = Array.prototype.slice.call(arguments).map(function (x) {
return typeof x === 'object' ? JSON.stringify(x) : String(x);
});
console.log(LOG_PREFIX, a.join(' '));
}
/* ================== 存储键 ================== */
var K = {
deviceId: 'gdpx_v4_device_id',
license: 'gdpx_v4_license',
trialUsed: 'gdpx_v4_trial_used_ms', // 累计已试用毫秒(防刷新重置)
trialTick: 'gdpx_v4_trial_tick_at', // 上次心跳时间戳
payOrder: 'gdpx_v4_pending_order'
};
/* ================== 设备号 ================== */
function getDeviceId() {
var id = GM_getValue(K.deviceId, '');
if (!id) {
var t = Date.now().toString(36).toUpperCase();
var r = Math.random().toString(36).substring(2, 8).toUpperCase();
id = 'GD-' + t.substring(0, 4) + '-' + r;
GM_setValue(K.deviceId, id);
log('生成设备号', id);
}
return id;
}
/* ================== 激活码校验核心(可离线验签) ================== */
/* ---BEGIN LICENSE VERIFY CORE--- */
function b64uToBuf(str) {
var s = String(str).replace(/-/g, '+').replace(/_/g, '/');
while (s.length % 4) s += '=';
var bin = atob(s);
var buf = new Uint8Array(bin.length);
for (var i = 0; i < bin.length; i++) buf[i] = bin.charCodeAt(i);
return buf;
}
function pemToBuf(pem) {
var b64 = String(pem)
.replace(/-----BEGIN PUBLIC KEY-----/, '')
.replace(/-----END PUBLIC KEY-----/, '')
.replace(/\s+/g, '');
return b64uToBuf(b64);
}
/**
* 校验激活码
* @returns {Promise<{ok:boolean, reason?:string, expire?:number}>}
*/
async function verifyLicense(license, deviceId) {
license = String(license || '').trim();
var parts = license.split('.');
if (parts.length !== 2) return { ok: false, reason: '激活码格式错误' };
var payload = parts[0];
var sig = parts[1];
// payload: GDPX1|deviceId|expireTs(秒,0=永久)|nonce
// deviceId 为 * 时表示"通用卡密"(发卡平台批量预生成场景),任意机器可用
var f = payload.split('|');
if (f.length !== 4) return { ok: false, reason: '激活码格式错误' };
if (f[0] !== 'GDPX1') return { ok: false, reason: '激活码版本不匹配' };
if (f[1] !== deviceId && f[1] !== '*') return { ok: false, reason: '激活码与本机设备不匹配(一码一机)' };
var expire = parseInt(f[2], 10);
if (isNaN(expire)) return { ok: false, reason: '激活码格式错误' };
if (expire !== 0 && Date.now() / 1000 > expire) return { ok: false, reason: '激活码已过期' };
// 浏览器 Web Crypto 验签(RSASSA-PKCS1-v1_5 + SHA-256)
var floating = (f[1] === '*');
var subtle = (window.crypto && window.crypto.subtle) || (self.crypto && self.crypto.subtle);
if (!subtle) {
// 安全修复:离线卡密路线没有服务端可兜底,此时若放行,
// 任何人只要构造 "GDPX1|*|0|任意.任意" 就能激活,等于授权形同虚设。
// 只有在确实配了在线服务端(激活码由服务端下发)时,才允许信任服务端结果。
if (!hasApi()) {
return { ok: false, reason: '当前页面环境不支持本地验签(需 HTTPS),请换用 HTTPS 页面或联系客服' };
}
return { ok: true, expire: expire, floating: floating, weak: true };
}
try {
var key = await subtle.importKey(
'spki', pemToBuf(PUBLIC_KEY_PEM),
{ name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' },
false, ['verify']
);
var ok = await subtle.verify(
{ name: 'RSASSA-PKCS1-v1_5' },
key,
b64uToBuf(sig),
new TextEncoder().encode(payload)
);
if (!ok) return { ok: false, reason: '激活码签名无效' };
return { ok: true, expire: expire, floating: floating };
} catch (e) {
return { ok: false, reason: '验签失败:' + e.message };
}
}
/* ---END LICENSE VERIFY CORE--- */
/* ================== 服务端通信 ================== */
function api(action, params, method) {
return new Promise(function (resolve, reject) {
var qs = [];
for (var k in params) if (params.hasOwnProperty(k)) {
qs.push(encodeURIComponent(k) + '=' + encodeURIComponent(params[k]));
}
var url = CONFIG.API_BASE + 'api.php?action=' + action + (qs.length ? '&' + qs.join('&') : '');
GM_xmlhttpRequest({
method: method || 'GET',
url: url,
timeout: 20000,
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
onload: function (res) {
try { resolve(JSON.parse(res.responseText)); }
catch (e) { reject(new Error('服务端返回异常:' + res.responseText.slice(0, 200))); }
},
onerror: function (e) { reject(new Error('无法连接授权服务器')); },
ontimeout: function () { reject(new Error('授权服务器响应超时')); }
});
});
}
/* ================== 状态 ================== */
var STATE = {
deviceId: getDeviceId(),
activated: false,
license: '',
expire: 0,
trialUsedMs: 0,
stopped: false // 试用到期后暂停学习
};
(async function boot() {
registerMenus();
var saved = GM_getValue(K.license, '');
if (saved) {
var v = await verifyLicense(saved, STATE.deviceId);
if (v.ok) {
STATE.activated = true;
STATE.license = saved;
STATE.expire = v.expire || 0;
log('已激活', v.weak ? '(在线模式)' : '(离线验签通过)');
} else {
log('本地激活码失效:' + v.reason);
GM_deleteValue(K.license);
}
}
STATE.trialUsedMs = parseInt(GM_getValue(K.trialUsed, '0'), 10) || 0;
if (STATE.activated) {
startStudy();
} else {
startTrialCountdown();
// 先按剩余试用时间跑,余额用尽时自动弹付款窗
if (trialLeftMs() <= 0) {
STATE.stopped = true;
showPayDialog('试用已结束');
} else {
log('试用剩余', Math.ceil(trialLeftMs() / 60000), '分钟');
startStudy();
}
}
})();
function trialTotalMs() { return CONFIG.TRIAL_MINUTES * 60 * 1000; }
function trialLeftMs() { return Math.max(0, trialTotalMs() - STATE.trialUsedMs); }
/** 试用计时:每 30 秒累加一次实际在线时长,刷新/换页不会清零 */
function startTrialCountdown() {
var last = parseInt(GM_getValue(K.trialTick, '0'), 10) || Date.now();
var now = Date.now();
if (now > last) {
var delta = Math.min(now - last, 10 * 60 * 1000); // 单段最多计 10 分钟,防止挂机跨天暴涨
STATE.trialUsedMs += delta;
GM_setValue(K.trialUsed, STATE.trialUsedMs);
}
GM_setValue(K.trialTick, now);
setInterval(function () {
if (STATE.activated || STATE.stopped) {
GM_setValue(K.trialTick, Date.now());
return;
}
var t = Date.now();
var prev = parseInt(GM_getValue(K.trialTick, '0'), 10) || t;
var d = Math.min(t - prev, 10 * 60 * 1000);
if (d > 0) {
STATE.trialUsedMs += d;
GM_setValue(K.trialUsed, STATE.trialUsedMs);
}
GM_setValue(K.trialTick, t);
if (trialLeftMs() <= 0) {
STATE.stopped = true;
stopLearning();
showPayDialog('试用时间已用完');
}
}, 30000);
}
/* ================== 支付弹窗(全自动:扫码 → 轮询 → 激活 → 继续刷课) ================== */
function showPayDialog(reason) {
if (document.getElementById('gdpx-pay-root')) return;
log('显示支付窗口:' + reason);
var root = document.createElement('div');
root.id = 'gdpx-pay-root';
root.innerHTML =
'
' +
'
' +
'
' +
'
学习助手 · 授权激活
' +
'
扫码付款后自动激活,无需联系任何人
' +
'
×' +
'
' +
'
' + reason + ',付款后立即恢复自动学习
' +
'
' +
'
' +
'
已有激活码?直接粘贴(离线可用)
' +
'
' +
'' +
'
' +
'
' +
'
' +
'设备号:' + STATE.deviceId + '' +
'' +
'
' +
'
';
(document.body || document.documentElement).appendChild(root);
document.getElementById('gdpx-pay-close').onclick = function () { root.remove(); };
// 复制设备号:买家把设备号发给客服,客服据此签发激活码
var copyBtn = document.getElementById('gdpx-copy-dev');
if (copyBtn) copyBtn.onclick = function () {
copyText(STATE.deviceId);
var t = document.getElementById('gdpx-copy-tip');
if (t) {
t.textContent = '已复制';
setTimeout(function () { t.textContent = ''; }, 2000);
}
};
document.getElementById('gdpx-lic-btn').onclick = function () {
var val = document.getElementById('gdpx-lic-input').value.trim();
redeemLicense(val).then(function (r) {
var el = document.getElementById('gdpx-lic-msg');
if (r.ok) {
el.innerHTML = '✓ 激活成功,正在恢复学习…';
setTimeout(function () { root.remove(); location.reload(); }, 1200);
} else {
el.innerHTML = '✗ ' + r.reason + '';
}
});
};
if (hasApi()) createOrderAndRender(root);
else renderOfflineGuide(root);
}
/** 复制文本:优先用脚本管理器的剪贴板 API,失败则回退到临时 textarea */
function copyText(text) {
try {
if (typeof GM_setClipboard === 'function') { GM_setClipboard(text); return; }
} catch (e) { /* 继续走降级 */ }
try {
var ta = document.createElement('textarea');
ta.value = text;
ta.style.cssText = 'position:fixed;top:-9999px;opacity:0';
document.body.appendChild(ta);
ta.select();
document.execCommand('copy');
ta.remove();
} catch (e2) { log('复制失败', e2.message); }
}
/** 是否已配置真正的授权服务端 */
function hasApi() {
return !!CONFIG.API_BASE && CONFIG.API_BASE.indexOf('your-domain.com') < 0;
}
/** 无服务器路线:引导自助购买 → 粘贴卡密(卡密离线验签,永久有效) */
function renderOfflineGuide(root) {
var stage = document.getElementById('gdpx-pay-stage');
var shop = CONFIG.SHOP_URL;
var buyBtn = shop
? '' +
'① 点此自助购买激活码'
: '未配置购买链接(CONFIG.SHOP_URL)
';
stage.innerHTML =
'' +
'激活三步:
' +
'① 点下方按钮到购买页付款(请同时把设备号发给客服,下方「复制」按钮可一键复制)
' +
'② 客服核对收款后,把激活码发给你(通常 30 分钟内)
' +
'③ 把激活码粘贴到下方输入框,点「激活」即可使用' +
'
' +
'' + buyBtn + '
' +
'激活码本地离线校验,激活后无需联网
';
}
function createOrderAndRender(root) {
var stage = document.getElementById('gdpx-pay-stage');
api('create_order', { device_id: STATE.deviceId }).then(function (d) {
if (!d.ok) throw new Error(d.msg || '下单失败');
log('订单已创建', d.order_no, d.amount);
GM_setValue(K.payOrder, d.order_no);
stage.innerHTML =
'应付金额 ' +
'¥' + Number(d.amount).toFixed(2) + ' · 永久授权
' +
'' +
'' +
'' +
'请用支付宝/微信扫码付款,付款后本页面自动激活
' +
'' +
'订单号 ' + d.order_no + '
';
pollOrder(root, d.order_no);
}).catch(function (e) {
log('下单失败', e.message);
stage.innerHTML =
'下单失败:' + e.message + '
' +
'请检查网络,或稍后重试。也可使用下方激活码直接激活。
' +
'';
var b = document.getElementById('gdpx-retry');
if (b) b.onclick = function () { createOrderAndRender(root); };
});
}
function pollOrder(root, orderNo) {
var tries = 0;
var maxTries = 600; // 30 分钟
var timer = setInterval(function () {
if (!document.getElementById('gdpx-pay-root')) { clearInterval(timer); return; }
tries++;
if (tries > maxTries) {
clearInterval(timer);
var tip = document.getElementById('gdpx-pay-tip');
if (tip) tip.innerHTML = '订单已超时,请关闭本窗口重新发起支付';
return;
}
api('query_order', { order_no: orderNo }).then(function (d) {
if (!d.ok) return;
if (d.status === 'paid' && d.license) {
clearInterval(timer);
log('支付成功,收到激活码');
redeemLicense(d.license).then(function (r) {
if (r.ok) {
var tip = document.getElementById('gdpx-pay-tip');
if (tip) tip.innerHTML = '✓ 支付成功,已激活,正在恢复学习…';
setTimeout(function () { root.remove(); location.reload(); }, 1500);
} else {
var tip2 = document.getElementById('gdpx-pay-tip');
if (tip2) tip2.innerHTML = '激活码校验失败:' + r.reason + '';
}
});
}
}).catch(function () { /* 网络抖动,下一轮继续 */ });
}, 3000);
// 收银台页面支付成功时也会主动通知(iframe / 新窗口场景)
window.addEventListener('message', function (e) {
if (e.data && e.data.type === 'gdpx_paid' && e.data.license) {
clearInterval(timer);
redeemLicense(e.data.license).then(function (r) {
if (r.ok) { root.remove(); location.reload(); }
});
}
});
}
async function redeemLicense(license) {
var v = await verifyLicense(license, STATE.deviceId);
if (!v.ok) return { ok: false, reason: v.reason };
GM_setValue(K.license, license);
STATE.activated = true;
STATE.license = license;
STATE.expire = v.expire || 0;
STATE.stopped = false;
log('激活成功');
return { ok: true };
}
/* ================== 学习主流程 ================== */
var currentUrl = window.location.href;
function startStudy() {
if (currentUrl.indexOf('LearningCourse.aspx') >= 0) {
setTimeout(handleCourseListPage, 2500);
} else if (currentUrl.indexOf('StudyCenter.aspx') >= 0) {
setTimeout(handleStudyCenterPage, 2500);
} else if (currentUrl.indexOf('play_pc') >= 0 || currentUrl.indexOf('CourseWare') >= 0) {
setTimeout(handleVideoPage, 2000);
} else if (currentUrl === 'https://wcs1.shawcoder.xyz/gdcecw/play_pc/playdo_pc.html') {
setTimeout(handleCompletionPage, 1500);
} else {
log('当前页面无需处理');
}
}
function stopLearning() {
removeWatermark();
if (window.videoTimer) { clearTimeout(window.videoTimer); window.videoTimer = null; }
if (window.playInterval) { clearInterval(window.playInterval); window.playInterval = null; }
if (window.watchTimer) { clearInterval(window.watchTimer); window.watchTimer = null; }
try {
var v = document.querySelector('video');
if (v) v.pause();
} catch (e) { }
}
function guard() {
if (STATE.stopped) { stopLearning(); return false; }
return true;
}
/* ---------- 课程列表页:挑一门没学完的课 ---------- */
function handleCourseListPage() {
if (!guard()) return;
log('处理课程列表页');
setTimeout(function () {
if (!guard()) return;
var links = document.querySelectorAll('a.courseware-list-reed');
if (!links.length) links = document.querySelectorAll("a[id^='gvList_ctl']");
if (!links.length) { log('未找到课程链接'); return; }
var target = null;
for (var i = 0; i < links.length; i++) {
var t = links[i].title || links[i].textContent || '';
var p = parseProgress(t);
if (p < CONFIG.COURSE_DONE_PERCENT) { target = { el: links[i], idx: i + 1, p: p }; break; }
}
if (target) {
log('开始第 ' + target.idx + ' 门课,已完成 ' + target.p + '%');
setTimeout(function () { if (guard()) target.el.click(); }, 1500);
} else {
log('全部课程已完成 🎉');
}
}, 1200);
// 监听子窗口关闭后刷新,继续下一课
window.addEventListener('message', function (e) {
if (e.data && (e.data.type === 'refreshPage' || e.data.type === 'gdpx_next')) {
log('收到刷新消息,2 秒后刷新');
setTimeout(function () { if (guard()) location.reload(); }, 2000);
}
});
}
/* ---------- 学习中心页 ---------- */
function handleStudyCenterPage() {
if (!guard()) return;
log('检测学习中心进度');
setTimeout(function () {
if (!guard()) return;
var p = getCurrentProgress();
log('当前进度 ' + p + '%');
if (p >= CONFIG.COURSE_DONE_PERCENT) {
location.href = 'https://gbpx.gd.gov.cn/gdceportal/Study/LearningCourse.aspx';
} else {
findAndClickCourse();
}
}, 2000);
}
/* ---------- 视频播放页 ---------- */
function handleVideoPage() {
if (!guard()) return;
log('处理视频播放页');
createWatermark();
// 保险丝:最长播放时间
window.videoTimer = setTimeout(function () {
log('达到单课时长上限,切换下一课');
closeVideoAndNext();
}, CONFIG.MAX_VIDEO_MINUTES * 60 * 1000);
setTimeout(startPlayback, 1500);
startWatchdog();
}
function startPlayback() {
if (!guard()) return;
tryDirectPlay();
setTimeout(clickPlayButtons, 400);
setTimeout(clickPageCenter, 900);
var n = 0;
window.playInterval = setInterval(function () {
if (!guard()) { clearInterval(window.playInterval); return; }
n++;
if (isPlaying()) { clearInterval(window.playInterval); log('视频播放中'); return; }
if (n >= 12) { clearInterval(window.playInterval); log('多次尝试后仍未播放'); return; }
log('第 ' + n + ' 次尝试播放');
tryDirectPlay();
clickPlayButtons();
}, 4000);
}
/** 看门狗:检测播放结束或卡死 */
function startWatchdog() {
var lastTime = -1, stall = 0;
window.watchTimer = setInterval(function () {
if (!guard()) return;
var v = document.querySelector('video');
if (!v) return;
var pct = v.duration ? (v.currentTime / v.duration) * 100 : 0;
if (v.ended || pct >= CONFIG.DONE_PERCENT) {
log('本章已播完(' + pct.toFixed(1) + '%),切换下一课');
closeVideoAndNext();
return;
}
if (Math.abs(v.currentTime - lastTime) < 0.05) {
stall++;
if (stall >= 3) { log('播放停滞,重新触发'); stall = 0; tryDirectPlay(); clickPlayButtons(); }
} else {
stall = 0;
}
lastTime = v.currentTime;
}, 10000);
}
function isPlaying() {
try {
var v = document.querySelector('video');
if (v) return !v.paused && !v.ended && v.currentTime > 0;
var pe = document.querySelector('.vjs-play-progress');
if (pe && pe.style.width && pe.style.width !== '0%' && pe.style.width !== '0px') return true;
} catch (e) { }
return false;
}
function tryDirectPlay() {
try {
var vs = document.querySelectorAll('video');
for (var i = 0; i < vs.length; i++) {
var v = vs[i];
v.muted = true;
try { v.volume = 0; } catch (e) { }
var pr = v.play();
if (pr && pr.then) pr.then(function () { log('video.play() 成功'); }).catch(function () { });
}
} catch (e) { }
}
function clickPlayButtons() {
var sels = ['.vjs-big-play-button', '.vjs-play-control', '.vjs-play-button',
'button[title*="播放"]', 'button[title*="play"]', 'button[aria-label*="播放"]',
'button[aria-label*="play"]', '.play-btn', '.video-play', '.play-button',
'.start-play', '.start-button', '.begin-btn', '.vjs-poster', '.video-poster'];
sels.forEach(function (s) {
try {
var els = document.querySelectorAll(s);
for (var i = 0; i < els.length; i++) {
if (visible(els[i])) { els[i].click(); log('点击 ' + s); }
}
} catch (e) { }
});
}
function clickPageCenter() {
try {
var x = window.innerWidth / 2, y = window.innerHeight / 2;
var el = document.elementFromPoint(x, y);
if (el) el.dispatchEvent(new MouseEvent('click', {
view: window, bubbles: true, cancelable: true, clientX: x, clientY: y
}));
} catch (e) { }
}
function visible(el) {
if (!el) return false;
var r = el.getBoundingClientRect();
return r.width > 0 && r.height > 0 && r.top >= 0 && r.left >= 0 &&
r.bottom <= window.innerHeight && r.right <= window.innerWidth;
}
function closeVideoAndNext() {
removeWatermark();
if (window.videoTimer) { clearTimeout(window.videoTimer); window.videoTimer = null; }
if (window.playInterval) { clearInterval(window.playInterval); window.playInterval = null; }
if (window.watchTimer) { clearInterval(window.watchTimer); window.watchTimer = null; }
try {
if (window.opener && !window.opener.closed) {
var n = 0;
var send = function () {
n++;
window.opener.postMessage({ type: 'refreshPage', source: 'videoWindow', count: n }, '*');
if (n < 3) setTimeout(send, 4000);
};
setTimeout(send, 800);
}
} catch (e) { }
setTimeout(function () {
try {
window.close();
log('窗口已关闭');
} catch (e) {
location.href = 'https://gbpx.gd.gov.cn/gdceportal/Study/LearningCourse.aspx';
}
}, 1800);
}
function handleCompletionPage() {
log('播放完成页');
setTimeout(function () {
try {
var b = document.querySelectorAll('button.instructions-close');
if (b.length) { b[0].click(); log('点击关闭按钮'); }
} catch (e) { }
setTimeout(function () {
try { window.close(); }
catch (e) { location.href = 'https://gbpx.gd.gov.cn/gdceportal/study/StudyCenter.aspx'; }
}, 1200);
}, 1500);
}
/* ================== 工具 ================== */
function parseProgress(text) {
var m = String(text || '').match(/([\d.]+)\s*[%%]/);
if (m) return parseFloat(m[1]);
return 0;
}
function getCurrentProgress() {
var divs = document.querySelectorAll('div');
for (var i = 0; i < divs.length; i++) {
var m = (divs[i].textContent || '').match(/([\d.]+)%/);
if (m) return parseFloat(m[1]);
}
return 0;
}
function findAndClickCourse() {
var a = document.querySelectorAll('a.courseware-list-reed');
if (!a.length) a = document.querySelectorAll("a[id^='gvList_ctl']");
if (a.length) { log('继续学习'); a[0].click(); }
else setTimeout(function () {
location.href = 'https://gbpx.gd.gov.cn/gdceportal/Study/LearningCourse.aspx';
}, 2500);
}
/* ================== 动态水印 ================== */
var wmEl = null, wmTimer = null;
function createWatermark() {
removeWatermark();
wmEl = document.createElement('div');
wmEl.textContent = '视频自动播放中';
wmEl.style.cssText = 'position:fixed;z-index:2147483000;pointer-events:none;background:rgba(0,0,0,.72);' +
'color:#4CAF50;padding:12px 24px;border-radius:30px;font-size:16px;font-weight:700;border:3px solid #4CAF50;' +
'box-shadow:0 0 25px rgba(76,175,80,.6);white-space:nowrap;user-select:none;font-family:Arial,sans-serif;' +
'transition:transform .2s linear;';
document.body.appendChild(wmEl);
var vx = (Math.random() > .5 ? 1 : -1) * (1.5 + Math.random() * 1.5);
var vy = (Math.random() > .5 ? 1 : -1) * (1 + Math.random());
var x = Math.random() * Math.max(10, window.innerWidth - 240);
var y = Math.random() * Math.max(10, window.innerHeight - 80);
wmTimer = setInterval(function () {
if (!wmEl) return;
var w = wmEl.offsetWidth || 200, h = wmEl.offsetHeight || 50;
x += vx; y += vy;
if (x < 10) { x = 10; vx = Math.abs(vx); }
if (y < 10) { y = 10; vy = Math.abs(vy); }
if (x > window.innerWidth - w - 10) { x = window.innerWidth - w - 10; vx = -Math.abs(vx); }
if (y > window.innerHeight - h - 10) { y = window.innerHeight - h - 10; vy = -Math.abs(vy); }
wmEl.style.left = x + 'px';
wmEl.style.top = y + 'px';
wmEl.style.transform = 'rotate(' + (Math.sin(Date.now() / 800) * 2) + 'deg)';
}, 40);
}
function removeWatermark() {
if (wmEl) { wmEl.remove(); wmEl = null; }
if (wmTimer) { clearInterval(wmTimer); wmTimer = null; }
}
/* ================== 脚本菜单(脚本猫/油猴 的扩展菜单里可点) ================== */
function registerMenus() {
if (typeof GM_registerMenuCommand !== 'function') return;
GM_registerMenuCommand('查看授权状态 / 激活', function () {
showPayDialog('手动打开');
});
GM_registerMenuCommand('复制设备号(发给客服)', function () {
copyText(STATE.deviceId);
alert('设备号已复制:' + STATE.deviceId + '\n把它和付款截图一起发给客服即可。');
});
GM_registerMenuCommand('查看剩余试用时间', function () {
var leftMs = Math.max(0, CONFIG.TRIAL_MINUTES * 60000 - STATE.trialUsedMs);
alert(STATE.activated
? '已激活' + (STATE.expire ? ',有效期至 ' + new Date(STATE.expire * 1000).toLocaleString() : '(永久)')
: '未激活,剩余试用约 ' + Math.ceil(leftMs / 60000) + ' 分钟');
});
}
/* ================== 调试入口 ================== */
unsafeWindow.GDPX = {
state: STATE,
resetLicense: function () { GM_deleteValue(K.license); },
resetTrial: function () { GM_deleteValue(K.trialUsed); GM_deleteValue(K.trialTick); },
pay: function () { showPayDialog('手动触发'); },
deviceId: function () { return STATE.deviceId; }
};
})();