// ==UserScript==
// @name 辽宁省干部在线学习网-课程助手
// @namespace http://tampermonkey.net/zzzzzzys_辽宁省干部在线学习网-课程助手
// @version 1.0.1
// @copyright zzzzzzys.All Rights Reserved.
// @description 辽宁省干部在线学习网-课程助手(https://www.lngbzx.gov.cn/),脚本免费功能有限,可自动静音播放视频,防暂停。更全自动方法,请查看文档介绍!客户端课程助手,可学习所有未完成课程,最高支持2倍速!注意此平台网址!一定要和网页上的网址相对应!
// @author zzzzzzys
// @match https://www.lngbzx.gov.cn/*
// @require https://fastly.jsdelivr.net/npm/crypto-js@4.2.0/crypto-js.min.js
// @resource https://cdn.staticfile.org/limonte-sweetalert2/11.7.1/sweetalert2.min.css
// @require https://fastly.jsdelivr.net/npm/sweetalert2@11.12.2/dist/sweetalert2.all.min.js
// @require https://scriptcat.org/lib/637/1.4.5/ajaxHooker.js#sha256=EGhGTDeet8zLCPnx8+72H15QYRfpTX4MbhyJ4lJZmyg=
// @connect fc-mp-8ba0e2a3-d9c9-45a0-a902-d3bde09f5afd.next.bspapp.com
// @connect mp-8ba0e2a3-d9c9-45a0-a902-d3bde09f5afd.cdn.bspapp.com
// @grant unsafeWindow
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_deleteValue
// @grant GM_xmlhttpRequest
// @grant GM_info
// @grant GM_addStyle
// @run-at document-start
// @antifeature ads 有弹窗广告,介绍完全自动化软件
// @antifeature payment 脚本基础功能使用免费,但需要使用完全自动化软件时,可能收费
// ==/UserScript==
// 延迟函数
const delay = ms => new Promise(r => setTimeout(r, ms));
// 主入口类 - 重构后
class MainEntry {
constructor() {
this.courseHandler = null;
this.setupAjaxInterceptor();
this.checkDocumentReady();
}
setupAjaxInterceptor() {
const that = this;
ajaxHooker.hook(req => {
const url = req.url;
if (url.indexOf('vedioValidQuestions/getQuestions') !== -1) {
req.response = res => {
try {
const parsed = JSON.parse(res.responseText);
window.QuestionInfo = parsed.data;
console.log("题目信息:", window.QuestionInfo);
} catch (ex) {
console.error("解析题目失败:", ex);
}
};
}
else if (url.indexOf('p/play/config') !== -1) {
req.response = res => {
try {
const parsed = JSON.parse(res.responseText);
console.log("播放配置:", parsed);
window.playConfig = parsed.data;
} catch (ex) {
console.error("解析配置失败:", ex);
}
};
}
else if (url.indexOf('learning/learnVerify/checkCode') !== -1) {
req.abort = true;
req.response = res => {
res.responseText = '{"code":0,"msg":null,"data":{"data":"请勿频繁请求","status":9999}}';
};
}
else if (url.indexOf('learning/learnVerify') !== -1) {
req.abort = true;
}
});
console.log("AJAX拦截器已启用");
}
checkDocumentReady() {
if (document.readyState !== 'loading') {
this.boot();
} else {
document.addEventListener('DOMContentLoaded', () => this.boot());
}
}
boot() {
this.courseHandler = new CourseManager("channel-hunau");
}
}
// 课程管理类 - 重构后
class CourseManager {
constructor(channelId = "channel-my") {
this.channelId = channelId;
this.isVipUser = false;
this.isProcessing = false;
this.broadcastCh = new BroadcastChannel(channelId);
this.uiPanel = new ControlPanel({
VIPBtnText: "高级功能-已弃用,请前往软件学习"
});
this.initialize();
}
initialize() {
// 设置验证回调
this.uiPanel.onVerify = async (formData) => {
const apiUrl = await Helper.validateCode(formData);
if (apiUrl) {
this.uiPanel.updateTip(Helper.vipText);
this.isVipUser = true;
return true;
}
return false;
};
// 启动按钮回调
this.uiPanel.onStart = () => {
if (this.isProcessing) return;
this.isProcessing = true;
console.log("开始运行,VIP状态:", this.isVipUser);
this.execute().finally(() => {
this.isProcessing = false;
});
};
// VIP按钮回调
this.uiPanel.onVipClick = async () => {
if (!this.apiEndpoint) {
await this.uiPanel.triggerVerify();
}
await this.runPremiumFeatures();
};
this.restoreVipState();
this.showWelcomeAlert();
}
showWelcomeAlert() {
try {
// 手动关闭的提示弹窗
const alertOptions = {
title: "使用须知",
text: Helper.swFireText,
icon: 'info',
confirmButtonText: '明白,开始使用',
allowOutsideClick: false,
allowEscapeKey: false,
showCloseButton: false,
willClose: () => {
this.uiPanel.triggerStart();
}
};
Swal.fire(alertOptions);
} catch (err) {
console.error("弹窗异常:", err);
this.uiPanel.triggerStart();
}
}
restoreVipState() {
const saved = Helper.loadStatus();
this.isVipUser = saved;
if (saved) {
this.uiPanel.updateTip(Helper.vipText);
} else {
this.uiPanel.updateTip(Helper.baseText);
}
console.log("VIP状态恢复:", this.isVipUser);
}
async runPremiumFeatures() {
try {
Helper.showUpgradeAlert();
} catch (err) {
console.error("高级功能错误:", err);
Swal.fire({
title: "高级功能执行失败!",
text: "若一直失败,请联系进行售后处理!",
icon: 'error',
confirmButtonText: '确定',
allowOutsideClick: false,
willClose: () => {
console.log('用户确认错误,脚本已停止');
}
});
}
}
async execute() {
try {
await this.handleVideoPlayback();
} catch (err) {
console.error("执行错误:", err);
Swal.fire({
title: "失败!",
text: "视频基础播放失败!",
icon: 'error',
confirmButtonColor: "#FF4DAFFF",
confirmButtonText: "确定",
timer: 5000,
timerProgressBar: true,
willClose: () => {}
});
}
}
async handleVideoPlayback() {
const media = document.querySelector('video');
if (!media) {
console.warn("未找到视频元素");
return;
}
// 静音设置
media.volume = 0;
media.muted = true;
// 清理旧监听器
if (this._pauseGuard) {
media.removeEventListener('pause', this._pauseGuard);
}
// 防暂停处理
let debounceTimer = null;
this._pauseGuard = async () => {
if (media.ended) return;
if (debounceTimer) clearTimeout(debounceTimer);
debounceTimer = setTimeout(async () => {
console.log("检测到暂停,尝试恢复...");
media.volume = 0;
media.muted = true;
try {
await media.play();
} catch (ex) {
console.error("恢复播放失败:", ex);
}
}, 500);
};
media.addEventListener('pause', this._pauseGuard);
media.addEventListener('ended', () => {
this.navigateToNext();
}, { once: true });
await media.play();
}
navigateToNext() {
const catalogItems = Array.from(
document.querySelectorAll('ul.lis-content li.lis-inside-content')
);
if (!catalogItems.length) {
console.log("目录列表为空");
return;
}
const activeIdx = catalogItems.findIndex(li => li.querySelector('#top_play'));
console.log("当前位置:", activeIdx);
const startIdx = activeIdx === -1 ? 0 : activeIdx + 1;
for (let i = startIdx; i < catalogItems.length; i++) {
const item = catalogItems[i];
const btn = item.querySelector('button');
const titleEl = item.querySelector('h2');
if (!titleEl) continue;
const status = btn ? btn.textContent.trim() : '';
console.log(`项目[${i}] 状态: ${status}`);
const clickAttr = titleEl.getAttribute('onclick') || '';
const urlMatch = clickAttr.match(/window\.location\.href='([^']+)'/);
if (urlMatch) {
const targetUrl = urlMatch[1];
const lessonName = titleEl.textContent.trim().replace(/\s+/g, ' ');
console.log("准备跳转:", targetUrl);
Swal.fire({
title: "视频已播放完毕",
html: `即将跳转到下一节:
${lessonName}`,
icon: 'success',
timer: 5000,
timerProgressBar: true,
confirmButtonText: '立即跳转',
showCancelButton: true,
cancelButtonText: '取消',
}).then((result) => {
if (result.isConfirmed || result.dismiss === Swal.DismissReason.timer) {
window.location.href = targetUrl;
}
});
return;
}
}
console.log("已是最后一节");
this.complete();
}
broadcast(msg) {
this.broadcastCh.postMessage(msg);
}
complete() {
if (!this.isVipUser) {
Swal.fire({
title: "请升级高级版!",
text: "脚本已停止!基础版只能连播几个视频!",
icon: 'info',
confirmButtonColor: "#FF4DAFFF",
confirmButtonText: "确定",
timer: 0,
willClose: () => {}
});
return;
}
this.broadcast('finish');
Swal.fire({
title: "学习完成!",
text: "学习完成,5s后页面自动关闭!",
icon: 'success',
confirmButtonColor: "#FF4DAFFF",
confirmButtonText: "确定",
timer: 5000,
willClose: () => {
history.back();
setTimeout(() => location.reload(), 1000);
}
});
}
async monitorVideoProgress(videoEl, container) {
return new Promise(resolve => {
const checkId = setInterval(async () => {
try {
const current = document.querySelector('video');
if (!current) {
clearInterval(checkId);
resolve();
return;
}
videoEl.volume = 0;
videoEl.muted = true;
if (videoEl.paused && !videoEl.ended) {
console.log("视频暂停,重新播放");
videoEl.volume = 0;
videoEl.muted = true;
await videoEl.play();
}
if (!videoEl.src) {
console.error("视频源丢失,准备刷新");
setTimeout(() => location.reload(), 5000);
}
// 处理答题弹窗
try {
const quizDialog = document.querySelector('div.el-dialog[aria-label="随机练习"]');
if (quizDialog && window.QuestionInfo) {
const answer = window.QuestionInfo.correctAnswer;
console.log("自动答题:", answer);
const choices = Array.from(quizDialog.querySelectorAll('input'));
const correct = choices.find(opt => opt.value.includes(answer));
if (correct) {
correct.click();
await delay(100);
quizDialog.querySelector('.submit').click();
await delay(500);
const msgBox = document.querySelector('.el-message-box button');
if (msgBox) msgBox.click();
}
}
} catch (quizErr) {}
// 处理温馨提示
try {
const tipDialog = document.querySelector('div.el-dialog[aria-label="温馨提示"]');
if (tipDialog) {
const closeBtn = tipDialog.querySelector('button');
if (closeBtn) closeBtn.click();
}
} catch (tipErr) {}
} catch (monitorErr) {
console.error("监控错误:", monitorErr);
clearInterval(checkId);
}
}, 5000);
videoEl.addEventListener('ended', () => {
clearInterval(checkId);
resolve();
}, { once: true });
});
}
async waitForElement(selector, mode = 'single', root, timeoutMs = 10000) {
return new Promise((resolve, reject) => {
const modes = ['single', 'multiple'];
if (!modes.includes(mode)) {
reject('模式错误,需为 single 或 multiple');
return;
}
const cleanup = (tId, iId) => {
clearTimeout(tId);
clearInterval(iId);
};
const success = (result, tId, iId) => {
console.log(`${selector} 已就绪`);
cleanup(tId, iId);
resolve(result);
};
const fail = (tId, iId) => {
cleanup(tId, iId);
resolve(null);
};
const check = () => {
try {
if (mode === 'single') {
const el = root ? root.querySelector(selector) : document.querySelector(selector);
return el;
} else {
const list = root ? root.querySelectorAll(selector) : document.querySelectorAll(selector);
return list.length > 0 ? list : null;
}
} catch (ex) {
console.error('查询节点错误:', ex);
reject(ex);
}
};
const interval = setInterval(() => {
const found = check();
if (found) {
success(found, timer, interval);
} else {
console.log(`等待中: ${selector}`);
}
}, 1000);
const timer = setTimeout(() => {
console.error(`等待超时: ${selector}`);
fail(timer, interval);
}, timeoutMs);
});
}
checkCompletion(domNode) {
const progressText = domNode.querySelector('.el-progress__text');
return progressText && progressText.innerText.indexOf("100") !== -1;
}
checkCompletionPost(domNode) {
return domNode.querySelector('.xxzt_icon3');
}
detectContentType(domNode) {
if (domNode.querySelector('.font-syllabus-online-video')) {
return 0; // 视频
}
if (domNode.querySelector('.font-syllabus-page')) {
return 1; // 文档
}
if (domNode.querySelector('.font-syllabus-material')) {
return 2; // 材料
}
return -1;
}
}
// 辅助工具类 - 重构后
class Helper {
// 存储键名
static vipKey = 'hnedu123_VIP';
static jsKey = 'hnedu123_jsCode';
static signKey = 'hnedu123_vipSign';
// 网站配置
static siteId = '689ee33b8b0da441dcc4b83d';
// 提示文本 - 保持不变
static swFireText = "请在视频播放页面使用脚本,脚本检测到视频会自动开始,脚本功能有限,也可能页面有所更新,导致脚本不能正常使用,建议下载软件使用!全自动学习所有未完成视频";
static baseText = '建议下载软件使用!全自动学习所有未完成视频';
static vipText = '全自动建议下载客户端使用!辽宁省干部在线学习网';
static vipBtnText = "前往软件使用课程助手,全自动学习所有未完成视频!";
// 功能列表
static scriptFeatures = [
"辅助当前页面视频播放",
"防暂停",
"自动静音播放",
"仅限单页面使用",
];
static softwareFeatures = [
"输入账号密码即可全自动",
"全自动完成所有未完成课程",
"支持批量多账号同时学习",
"最高2倍速(开发时可用)",
];
// 下载链接
static aliYunLink = "https://www.alipan.com/s/wViqbLvgSF8";
static directLink = 'http://112.124.58.51/static/课程助手.exe';
// 购买链接
static purchaseLinks = [
"https://68n.cn/IJ8QB",
"https://68n.cn/RM9ob",
];
// 备用站点
static mirrorSites = [
{ name: "备用地址0", url: "https://www.zzzzzzys.com/" },
{ name: "备用地址1", url: "https://zzzzzzys.lovestoblog.com/" },
{ name: "备用地址2", url: "https://zzzzzzys.us.kg/" },
{ name: "备用地址3", url: "https://zzysdocs.dpdns.org/" },
{ name: "备用地址4", url: "https://zzzzzzys.dpdns.org/" },
{ name: "备用地址5", url: "https://zzzzzzys.kesug.com/" },
{ name: "备用地址6", url: "https://zzysdocs.great-site.net/" },
];
static docUrl = `${Helper.mirrorSites[0].url}?webId=${Helper.siteId}`;
static loadStatus() {
return false;
}
static async validateCode(formData) {
try {
Helper.showUpgradeAlert();
return;
// 以下代码实际不会执行,因上方已return
const response = await new Promise((resolve, reject) => {
const query = new URLSearchParams(formData).toString();
GM_xmlhttpRequest({
url: `https://fc-mp-8ba0e2a3-d9c9-45a0-a902-d3bde09f5afd.next.bspapp.com/utils/validCodeCas?${query}`,
method: 'GET',
onload: (res) => {
if (res.status === 200) {
const data = JSON.parse(res.response);
console.log("验证响应:", data);
resolve(data);
} else {
reject('请求失败: ' + res.response);
}
},
onerror: (err) => {
console.error("请求错误:", err);
reject('网络错误: ' + err.toString());
}
});
});
if (response.code !== 200) {
GM_deleteValue(Helper.vipKey);
GM_deleteValue(Helper.jsKey);
throw new Error('验证失败: ' + response.data);
}
Swal.fire({
title: "高级功能已启用!",
text: "校验成功!",
icon: 'success',
confirmButtonText: '确定',
});
GM_setValue(Helper.vipKey, true);
return response.data;
} catch (ex) {
console.error("验证异常:", ex);
Swal.fire({
title: "验证失败!",
text: ex.toString(),
icon: 'error',
confirmButtonText: '确定',
});
}
}
static async fetchRemoteJs(endpoint) {
try {
let cached = GM_getValue(Helper.jsKey);
if (cached) return cached;
const jsContent = await new Promise((resolve, reject) => {
GM_xmlhttpRequest({
url: endpoint,
method: 'GET',
onload: (res) => {
if (res.status === 200) {
resolve(res.responseText);
} else {
reject('服务器拒绝: ' + res.response);
}
},
onerror: (err) => {
console.error("JS加载错误:", err);
reject('请求失败: ' + err.toString());
}
});
});
const escaped = jsContent
.replace(/\\/g, '\\\\')
.replace(/'/g, '\\\'')
.replace(/"/g, '\\\"');
GM_setValue(Helper.jsKey, escaped);
return escaped;
} catch (ex) {
console.error('远程加载失败:', ex);
throw new Error("远程加载失败");
}
}
static showPurchaseDialog() {
const links = Helper.purchaseLinks;
Swal.fire({
title: ' 高级功能解锁',
html: `
⚠️ 网页脚本有局限性
浏览器脚本运行在沙盒中,无法跨页面、无法自动登录、无法批量管理账号。
若需要 输入账号密码后全自动完成所有视频,推荐使用本地客户端。
📄 当前脚本