// ==UserScript==
// @name 江西职培在线网课助手
// @namespace jiangxizhipeizaixian
// @version 1.0
// @description 职培在线网课助手
// @author Nanako660
// @match https://jiangxi.zhipeizaixian.com/study/video*
// @icon https://www.google.com/s2/favicons?sz=64&domain=zhipeizaixian.com
// @require https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js
// @grant GM_xmlhttpRequest
// @grant GM_log
// @grant GM_notification
// @grant GM_setValue
// @grant GM_getValue
// @connect furina.one
// @license MIT
// @downloadURL none
// ==/UserScript==
(function () {
'use strict';
var isDebug = GM_getValue('isDebug', false);
var isCompleted = false;
// 全局邮件地址
var localEmail = GM_getValue('localEmail', null);
var isVerify = false;
// 获取视频控件
var checkVideoInterval;
// 全局悬浮窗
var shadow;
// 监控视频播放
var monitorVideoInterval;
var updateDebugInfoInterval;
function Main() {
console.log("职培在线网课助手脚本开始运行...");
// 创建信息悬浮窗
createFloatingWindow();
// 判断当前课程是否已完成
checkCurrentCourceIsCompleted(true);
if (isCompleted) {
console.log("当前课程已完成,脚本停止执行...");
return;
}
// 监控视频播放
monitorVideo();
}
checkVideoInterval = setInterval(function () {
if (getVideo()) {
console.log("视频控件已加载,开始运行...");
Main();
clearInterval(checkVideoInterval);
} else {
console.log("视频控件未加载,继续轮询...");
}
}, 500);
function getMoal() {
var moal = document.querySelector('.zhipei-modal-content')
if (moal) {
console.log("获取到人机验证弹窗,请手动完成人机验证!");
return moal;
}
return null;
}
function captureAndSendEmail(item = null) {
captureScreenshot(item).then(dataURL => {
let subject = '职培在线网课助手人机验证弹窗通知';
let lvideo = getVideo();
let message = `
职培在线网课助手 - 人机验证弹窗通知
课程信息:
课程标题:
|
${getTitle()} |
视频时长:
|
${lvideo.duration.toFixed(0)} 秒 |
已看时长:
|
${lvideo.currentTime.toFixed(0)} 秒 |
人机验证弹窗截图:
请尽快处理人机验证弹窗,以免影响课程进度!
此邮件由职培在线网课助手自动发送,请勿直接回复!。
`;
//console.log(dataURL);
sendEmail(localEmail, subject, message);
});
}
function getNextButton() {
var nextButton = document.querySelector('.next_button___YGZWZ');
if (nextButton) {
console.log("成功获取下一页按钮");
nextButton.click();
}
}
function getTitle() {
var title = document.querySelector('.header_box_title___1INxv');
if (title) {
return title.innerText;
}
return null;
}
/**
* 获取页面中的视频元素
* @returns {HTMLVideoElement|null} 返回视频元素,如果未找到视频元素,则返回 null。
*/
function getVideo() {
const videoContainer = document.getElementById('J_prismPlayer');
if (!videoContainer) return null;
const video = videoContainer.querySelector('video');
return video || null;
}
function monitorVideo() {
monitorVideoInterval = setInterval(function () {
if (isVerify) {
console.log("需要手动刷新网页重置脚本...");
return;
}
let moal = getMoal();
if (moal) {
if (localEmail == null) {
console.log("邮件地址未设置,不发送邮件通知!");
return;
}
isVerify = true;
setTimeout(function () {
captureAndSendEmail('.zhipei-modal-content');
}, 3000);
return;
}
let video = getVideo();
if (video.currentTime >= video.duration) {
console.log("视频播放完毕,尝试自动播放下一节...");
getNextButton();
return;
}
if (video.paused) {
console.log("视频暂停,尝试继续播放...");
video.volume = 0;
video.play();
}
}, 1000);
}
function createFloatingWindow() {
var floatingWindow = document.createElement('div');
shadow = floatingWindow.attachShadow({ mode: 'open' });
// 添加样式
var style = document.createElement('style');
style.textContent = `
#debugWindow {
position: fixed;
top: 10px;
right: 10px;
background-color: #f0f0f0;
color: #333;
padding: 20px;
border-radius: 15px;
box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.3);
z-index: 9999;
#cursor: move;
width: 320px;
font-family: Arial, sans-serif;
user-select: none;
}
h4 {
margin-top: 0;
margin-bottom: 10px;
font-size: 18px;
font-weight: bold;
color: #444;
}
p {
margin: 8px 0;
font-size: 14px;
line-height: 1.5;
}
input[type="text"] {
width: calc(100% - 100px);
padding: 5px;
border: 1px solid #ccc;
border-radius: 5px;
}
button {
margin-top: 10px;
padding: 5px 10px;
background-color: #007bff;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 14px;
}
button:hover {
background-color: #0056b3;
}
label {
display: flex;
align-items: center;
margin-bottom: 10px;
font-size: 14px;
}
label input[type="checkbox"] {
margin-right: 10px;
}
#debugContent {
display: none; /* 默认隐藏调试信息内容 */
}
`;
shadow.appendChild(style);
// 添加内容
var content = document.createElement('div');
content.id = 'debugWindow';
content.innerHTML = `
职培在线网课助手
功能列表:
1.自动静音后台播放 ☑
2.自动播放下一节课程☑
3.自动过人机验证☐
3.邮件提醒人机验证☑
使用说明:
1.填写邮箱用于接收人机验证通知邮件
推荐使用QQ邮箱,手机下载QQ邮箱App设置通知优先级为高,以免错过通知
2.手动完成人机验证后需要刷新网页
配置信息
邮箱地址:
调试信息
等待视频信息...
不要乱点
`;
shadow.appendChild(content);
// 将浮动窗口添加到文档中
document.body.appendChild(floatingWindow);
// 初始化复选框状态
shadow.querySelector('#debugMode').checked = isDebug;
// 监听复选框状态变化
shadow.querySelector('#debugMode').addEventListener('change', function () {
isDebug = this.checked;
GM_setValue('isDebug', isDebug);
shadow.querySelector('#debugContent').style.display = isDebug ? 'block' : 'none';
});
// 测试按钮功能
shadow.querySelector('#testButton1').addEventListener('click', function () {
checkCurrentCourceIsCompleted(true);
});
// 获取页面上的元素
var emailInput = shadow.querySelector('#emailInput');
var saveEmailButton = shadow.querySelector('#saveEmail');
// 初始化输入框的值为保存的邮箱
var savedEmail = GM_getValue('localEmail', '');
emailInput.value = savedEmail;
// 点击保存邮箱按钮的事件处理
saveEmailButton.addEventListener('click', function () {
var email = emailInput.value.trim();
if (email) {
GM_setValue('localEmail', email);
alert('邮箱地址已保存!请手动刷新网页生效!');
} else {
alert('请输入有效的邮箱地址。');
}
});
// 初始化调试信息的显示状态
shadow.querySelector('#debugContent').style.display = isDebug ? 'block' : 'none';
// 更新调试信息
setInterval(function () {
updateDebugInfo();
}, 1000);
}
function makeDraggable(element) {
var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
element.onmousedown = dragMouseDown;
function dragMouseDown(e) {
e = e || window.event;
e.preventDefault();
pos3 = e.clientX;
pos4 = e.clientY;
document.onmouseup = closeDragElement;
document.onmousemove = elementDrag;
}
function elementDrag(e) {
e = e || window.event;
e.preventDefault();
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
// 计算新的位置
var newTop = element.offsetTop - pos2;
var newLeft = element.offsetLeft - pos1;
// 限制拖动范围,避免拖出屏幕
var minLeft = 0;
var minTop = 0;
var maxLeft = window.innerWidth - element.offsetWidth;
var maxTop = window.innerHeight - element.offsetHeight;
// 限制 left 和 top 的最小最大值
newLeft = Math.max(minLeft, Math.min(newLeft, maxLeft));
newTop = Math.max(minTop, Math.min(newTop, maxTop));
// 设置窗口的新位置,并保持固定宽度
element.style.top = newTop + "px";
element.style.left = newLeft + "px";
element.style.width = '300px'; // 强制宽度固定,避免拖动时缩小
}
function closeDragElement() {
document.onmouseup = null;
document.onmousemove = null;
}
}
function updateDebugInfo() {
var video = getVideo();
if (video) {
var debugInfo = shadow.getElementById('debugInfo');
var info = `
视频标题: ${getTitle()}
视频时长: ${video.duration.toFixed(0)} 秒
当前播放时间: ${video.currentTime.toFixed(0)} 秒
播放状态: ${video.paused ? '暂停' : '播放中'}
音量: ${Math.round(video.volume * 100)} %
`;
debugInfo.innerHTML = info;
}
}
function getContents() {
var parentElement = document.querySelector('.content_box___1fOQp');
var result = {};
if (parentElement) {
console.log("成功获取父级元素");
// 获取所有子元素
var childElements = parentElement.querySelectorAll('.content_box_wrap___ZdoU3');
if (childElements.length > 0) {
console.log('开始解析子集元素...');
var parsedData = []; // 存储解析后的数据
childElements.forEach(function (childElement, index) {
// 解析每个子元素的信息
var title = childElement.querySelector('.units_title___1Js-7')?.textContent || '未找到标题';
var time = childElement.querySelector('.time_box___1PlPI')?.textContent || '未找到时长';
var link = childElement.querySelector('a.units_wrap_box___1ncip')?.href || '未找到链接';
var completed = childElement.querySelector('.anticon-check-circle') ? '已完成' : '未完成';
// 将解析的信息存储在对象中
var item = {
index: index + 1,
title: title,
time: time,
link: link,
completed: completed
};
// 将对象添加到数组中
parsedData.push(item);
});
// 将解析后的数据存储在结果对象中
result.data = parsedData;
} else {
console.log("未找到子元素");
result.data = [];
}
} else {
console.log("未找到父级元素");
result.data = [];
}
return result;
}
// 查找第一个未完成的课程
function findFirstIncompleteCourse(contents) {
for (let element of contents) {
if (element.completed === '未完成') {
return element; // 返回第一个未完成的课程对象
}
}
return null; // 如果没有未完成的课程,返回 null
}
function checkCurrentCourceIsCompleted(autoNext = false) {
// 获取当前页面的标题
let currentTitle = getTitle();
// 获取内容列表
let contents = getContents().data;
// 查找与当前标题匹配的课程
let currentCourse = contents.find(element => element.title === currentTitle);
if (currentCourse) {
console.log(`${currentCourse.title}:${currentCourse.completed}`);
if (currentCourse.completed === '已完成' && autoNext) {
let nextCourse = findFirstIncompleteCourse(contents);
if (nextCourse) {
console.log(`跳转到未完成的课程:${nextCourse.title}`);
window.location.href = nextCourse.link; // 跳转到第一个未完成课程的链接
}
else {
console.log("所有课程已完成!");
alert("所有课程已完成!");
isCompleted = true;
}
}
return currentCourse.completed === '已完成';
}
return false; // 如果未找到当前课程,返回 false
}
/**
* 封装邮件发送方法
* @param {string} recipient - 收件人邮箱地址
* @param {string} subject - 邮件主题
* @param {string} message - 邮件内容
* @param {string} image - 图片路径、URL或base64编码,可选
*/
function sendEmail(recipient, subject, message, image = null) {
// 检查收件人邮箱地址是否为空
if (!recipient) {
console.log("邮件通知发送失败,邮箱地址不正确!");
alert("邮件通知发送失败,邮箱地址不正确!");
return;
}
// 如果图片存在
if (image) {
// 检查是否是base64编码
if (image.startsWith('data:image/')) {
// 图片是base64编码
message += `
`;
} else if (image.startsWith('http://') || image.startsWith('https://')) {
// 图片是URL
message += `
`;
} else {
console.log("图片路径无效!");
return;
}
}
// 邮件发送数据
const emailData = {
recipient: recipient,
subject: subject || "默认主题", // 如果没有提供主题,使用默认主题
message: message || "默认内容" // 如果没有提供内容,使用默认内容
};
// 将emailData转换为JSON字符串
const jsonData = JSON.stringify(emailData);
// 调用PHP接口的URL
const apiUrl = "https://furina.one/api/email.php"; // 替换为实际PHP接口地址
// 使用GM_xmlhttpRequest发送POST请求
GM_xmlhttpRequest({
method: "POST",
url: apiUrl,
data: jsonData,
headers: {
"Content-Type": "application/json" // 告知服务器我们发送的是JSON数据
},
onload: function (response) {
if (response.status === 200) {
// 处理成功响应
console.log("邮件发送成功: " + response.responseText);
} else {
// 处理失败响应
console.log("邮件通知发送失败: " + response.status + " - " + response.responseText);
alert("邮件通知发送失败: " + response.status + " - " + response.responseText);
}
},
onerror: function (error) {
// 处理错误
console.log("邮件通知发送失败,请求错误: " + error);
alert("邮件通知发送失败,请求错误: " + error);
}
});
}
/**
* 截取网页内容并返回base64编码的图片
* @param {string} [selector] - 要截取的元素选择器,若为空则截取整个网页
* @returns {Promise} - 返回base64编码的图片数据URL
*/
function captureScreenshot(selector = null) {
return new Promise((resolve, reject) => {
let element = document.body; // 默认截取整个网页
// 如果提供了选择器,尝试查找元素
if (selector) {
element = document.querySelector(selector);
if (!element) {
console.log("指定的元素未找到!");
reject("指定的元素未找到!");
return;
}
}
// 使用 html2canvas 捕获截图
html2canvas(element).then(canvas => {
// 将 canvas 转换为 base64 编码的图像
const dataURL = canvas.toDataURL('image/png');
resolve(dataURL);
}).catch(error => {
console.log("截图失败: " + error);
reject(error);
});
});
}
})();