// ==UserScript==
// @name 燕京理工英华学堂自动看网课
// @version 1.4
// @description 英华学堂自动看网课,自动识别验证码,界面美观,功能丰富。支持用户注册登录、签到、金币经验系统。
// @author Sheng
// @email
// @match *://mooc.yit.edu.cn/user/node*
// @match *://mooc.yinghuaonline.com/user/node*
// @match *://zxshixun.yit.edu.cn/user/node*
// @match *://gyxy.yit.edu.cn/user/node*
// @match *://mooc.yit.edu.cn/user/index*
// @match *://mooc.yinghuaonline.com/user/index*
// @match *://zxshixun.yit.edu.cn/user/index*
// @match *://gyxy.yit.edu.cn/user/index*
// @iconURL
// @grant GM_xmlhttpRequest
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_deleteValue
// @license MIT
// @connect 119.8.102.43
// @connect 119.8.102.43:5000
// @connect api-app.api5dao.eu.org
// @require https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js
// @require https://cdn.bootcdn.net/ajax/libs/font-awesome/6.4.0/js/all.min.js
// ==/UserScript==
// 视频元素
let videoElement = null;
// 监视器
let checkCaptchaTimer = null;
// 容器文本
let containerTextElement = null;
// 验证码弹窗
let layuiLayerContent = null;
// 课程表
let links = null;
// 当前课程位置
let current = 0;
// 计时
let timerCnt = 0;
// 版本
let version = "1.4"
// 日志记录
let logs = [];
// 是否静音
let isMuted = GM_getValue('isMuted', true);
// 悬浮窗可见性
let isVisible = GM_getValue('isVisible', true);
// 是否启用自动播放
let autoPlayEnabled = GM_getValue('autoPlayEnabled', true);
// 当前进度
let currentProgress = 0;
// 总进度
let totalProgress = 0;
// 主题颜色
let themeColor = GM_getValue('themeColor', 'red');
// API配置
const API = {
BASE_URL: 'https://api-app.api5dao.eu.org/api',
APP_ID: '10007', // 应用ID
DEVICE_ID: generateDeviceId(), // 生成设备ID
};
// 用户信息
let userInfo = {
loggedIn: false,
id: GM_getValue('userId', ''),
username: GM_getValue('username', ''),
userToken: GM_getValue('userToken', ''),
nickname: GM_getValue('nickname', ''),
avatar: GM_getValue('avatar', ''),
money: GM_getValue('money', 0),
exp: GM_getValue('exp', 0),
lastSignTime: GM_getValue('lastSignTime', ''),
isSigned: GM_getValue('isSigned', false),
continuityDays: GM_getValue('continuityDays', 0),
seriesDays: GM_getValue('seriesDays', 0),
inviteCode: GM_getValue('inviteCode', ''),
lastUpdateTime: GM_getValue('lastUpdateTime', 0)
};
// 学习统计
let studyStats = {
todayStudyTime: GM_getValue('todayStudyTime', 0),
totalStudyTime: GM_getValue('totalStudyTime', 0),
earnedCoins: GM_getValue('earnedCoins', 0),
earnedExp: GM_getValue('earnedExp', 0),
lastStudyDate: GM_getValue('lastStudyDate', ''),
studyStartTime: 0
};
// 常量
const CONSTANTS = {
COINS_PER_HOUR: 0, // 每小时获得金币数
COST_PER_HOUR: 5, // 每小时消耗金币数
DAILY_SIGN_COINS: 15, // 每日签到金币数
INVITE_COINS: 180, // 邀请好友金币数
STUDY_TIME_UNIT: 60 * 60, // 1小时(秒)
STUDY_EXP_PER_HOUR: 10, // 每小时获得经验值
UPDATE_INTERVAL: 60 * 1000, // 用户信息更新间隔(毫秒)
MAX_UNREAD_MESSAGES: 99, // 最大未读消息数显示
AUTO_RECORD_STUDY_HOURS: true // 是否自动累计听课时长(默认开启)
};
// 生成设备ID
function generateDeviceId() {
const storedDeviceId = GM_getValue('deviceId', '');
if (storedDeviceId) {
return storedDeviceId;
}
// 生成随机设备ID
const randomId = Math.random().toString(36).substring(2, 15) +
Math.random().toString(36).substring(2, 15);
GM_setValue('deviceId', randomId);
return randomId;
}
// 检查当前日期
function checkCurrentDate() {
const today = new Date().toLocaleDateString();
// 如果是新的一天,重置每日学习时间
if (today !== studyStats.lastStudyDate) {
studyStats.todayStudyTime = 0;
studyStats.lastStudyDate = today;
userInfo.isSigned = false;
saveStudyStats();
GM_setValue('isSigned', false);
}
}
// 修改学习统计的初始化方法,绑定到学号
function loadStudyStats() {
const studentId = GM_getValue('studentId', '');
if (studentId) {
// 如果有学号,使用学号作为键
studyStats.todayStudyTime = GM_getValue(`${studentId}_todayStudyTime`, 0);
studyStats.totalStudyTime = GM_getValue(`${studentId}_totalStudyTime`, 0);
studyStats.earnedCoins = GM_getValue(`${studentId}_earnedCoins`, 0);
studyStats.earnedExp = GM_getValue(`${studentId}_earnedExp`, 0);
studyStats.lastStudyDate = GM_getValue(`${studentId}_lastStudyDate`, '');
// 同步总学习时间和累计听课时长
const localStudyHours = GM_getValue(`${studentId}_localStudyHours`, 0);
// 将小时转换为秒
const totalStudyTimeInSeconds = localStudyHours * 3600;
// 如果本地记录的听课时长大于总学习时间,则同步总学习时间
if (totalStudyTimeInSeconds > studyStats.totalStudyTime) {
studyStats.totalStudyTime = totalStudyTimeInSeconds;
GM_setValue(`${studentId}_totalStudyTime`, totalStudyTimeInSeconds);
}
} else {
// 没有学号,使用默认值
studyStats.todayStudyTime = GM_getValue('todayStudyTime', 0);
studyStats.totalStudyTime = GM_getValue('totalStudyTime', 0);
studyStats.earnedCoins = GM_getValue('earnedCoins', 0);
studyStats.earnedExp = GM_getValue('earnedExp', 0);
studyStats.lastStudyDate = GM_getValue('lastStudyDate', '');
}
studyStats.studyStartTime = 0;
}
// 修改保存学习统计的方法,绑定到学号
function saveStudyStats() {
const studentId = GM_getValue('studentId', '');
if (studentId) {
// 如果有学号,使用学号作为键
GM_setValue(`${studentId}_todayStudyTime`, studyStats.todayStudyTime);
GM_setValue(`${studentId}_totalStudyTime`, studyStats.totalStudyTime);
GM_setValue(`${studentId}_earnedCoins`, studyStats.earnedCoins);
GM_setValue(`${studentId}_earnedExp`, studyStats.earnedExp);
GM_setValue(`${studentId}_lastStudyDate`, studyStats.lastStudyDate);
// 同步累计听课时长(小时)
const totalStudyHours = Math.floor(studyStats.totalStudyTime / 3600);
GM_setValue(`${studentId}_localStudyHours`, totalStudyHours);
} else {
// 没有学号,使用通用键
GM_setValue('todayStudyTime', studyStats.todayStudyTime);
GM_setValue('totalStudyTime', studyStats.totalStudyTime);
GM_setValue('earnedCoins', studyStats.earnedCoins);
GM_setValue('earnedExp', studyStats.earnedExp);
GM_setValue('lastStudyDate', studyStats.lastStudyDate);
// 同步通用累计听课时长
const totalStudyHours = Math.floor(studyStats.totalStudyTime / 3600);
GM_setValue('localStudyHours', totalStudyHours);
}
}
// 保存用户信息
function saveUserInfo() {
GM_setValue('userId', userInfo.id);
GM_setValue('username', userInfo.username);
GM_setValue('userToken', userInfo.userToken);
GM_setValue('nickname', userInfo.nickname);
GM_setValue('avatar', userInfo.avatar);
GM_setValue('money', userInfo.money);
GM_setValue('exp', userInfo.exp);
GM_setValue('lastSignTime', userInfo.lastSignTime);
GM_setValue('isSigned', userInfo.isSigned);
GM_setValue('continuityDays', userInfo.continuityDays);
GM_setValue('seriesDays', userInfo.seriesDays);
GM_setValue('inviteCode', userInfo.inviteCode);
GM_setValue('lastUpdateTime', userInfo.lastUpdateTime);
}
// 格式化日期时间
function formatDateTime(date) {
const d = date ? new Date(date) : new Date();
return d.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
}).replace(/\//g, '-');
}
// 检查登录状态
function checkLoginStatus() {
userInfo.loggedIn = !!(userInfo.id && userInfo.username && userInfo.userToken);
if (userInfo.loggedIn) {
// 如果登录信息存在,检查是否需要更新用户信息
const now = Date.now();
if (now - userInfo.lastUpdateTime > CONSTANTS.UPDATE_INTERVAL) {
updateUserInfo();
}
}
return userInfo.loggedIn;
}
// API请求基本函数
function apiRequest(endpoint, data, successCallback, errorCallback) {
const formData = new FormData();
// 添加公共参数
formData.append('appid', API.APP_ID);
formData.append('device', API.DEVICE_ID);
// 添加请求数据
for (const key in data) {
if (data.hasOwnProperty(key) && data[key] !== undefined) {
formData.append(key, data[key]);
}
}
GM_xmlhttpRequest({
method: 'POST',
url: `${API.BASE_URL}/${endpoint}`,
data: formData,
responseType: 'json',
timeout: 10000,
onload: function(response) {
if (response.status === 200) {
const result = response.response;
if (result.code === 200) {
successCallback && successCallback(result);
} else {
showNotification('请求失败', result.msg || '未知错误');
errorCallback && errorCallback(result);
}
} else {
showNotification('网络错误', `状态码: ${response.status}`);
errorCallback && errorCallback({code: response.status, msg: '网络错误'});
}
},
onerror: function(error) {
showNotification('请求错误', error.message || '未知错误');
errorCallback && errorCallback({code: 500, msg: error.message || '请求错误'});
},
ontimeout: function() {
showNotification('请求超时', '服务器响应超时');
errorCallback && errorCallback({code: 408, msg: '请求超时'});
}
});
}
// 用户登录
function userLogin(username, password, callback) {
apiRequest(
'Login',
{
username: username,
password: password
},
function(result) {
// 登录成功,保存用户信息
userInfo.loggedIn = true;
userInfo.id = result.data.id;
userInfo.username = result.data.username;
userInfo.userToken = result.data.usertoken;
userInfo.lastUpdateTime = Date.now();
saveUserInfo();
showNotification('登录成功', `欢迎回来,${userInfo.username}!`);
// 获取完整用户信息
updateUserInfo();
callback && callback(true, result);
},
function(error) {
callback && callback(false, error);
}
);
}
// 用户注册
function userRegister(username, password, email, phone, callback) {
const now = Math.floor(Date.now() / 1000);
apiRequest(
'Register',
{
username: username,
password: password,
useremail: email,
phone: phone,
regcodetime: now
},
function(result) {
showNotification('注册成功', '请登录您的账号');
callback && callback(true, result);
},
function(error) {
callback && callback(false, error);
}
);
}
// 获取用户信息
function updateUserInfo(callback) {
if (!userInfo.id && !userInfo.username) {
callback && callback(false, {code: 401, msg: '未登录'});
return;
}
apiRequest(
'getUserInfo',
{
id: userInfo.id,
username: userInfo.username
},
function(result) {
// 更新用户信息
userInfo.nickname = result.data.nickname;
userInfo.avatar = result.data.usertx;
userInfo.money = result.data.money;
userInfo.exp = result.data.exp;
userInfo.lastSignTime = result.data.signlasttime;
userInfo.isSigned = result.data.sign;
userInfo.continuityDays = result.data.continuity_days;
userInfo.seriesDays = result.data.series_days;
userInfo.inviteCode = result.data.invitecode;
userInfo.lastUpdateTime = Date.now();
saveUserInfo();
updateUserPanel();
callback && callback(true, result);
},
function(error) {
callback && callback(false, error);
}
);
}
// 用户签到
function userSignIn(callback) {
if (!checkLoginStatus()) {
showLoginModal();
callback && callback(false, {code: 401, msg: '请先登录'});
return;
}
apiRequest(
'UserSign',
{
id: userInfo.id,
username: userInfo.username
},
function(result) {
userInfo.isSigned = true;
userInfo.lastSignTime = formatDateTime();
userInfo.continuityDays++;
userInfo.seriesDays++;
userInfo.money += CONSTANTS.DAILY_SIGN_COINS;
userInfo.lastUpdateTime = Date.now();
saveUserInfo();
updateUserPanel();
showNotification('签到成功', `恭喜获得${CONSTANTS.DAILY_SIGN_COINS}金币!`);
addSystemMessage('签到成功', `您已成功签到,获得${CONSTANTS.DAILY_SIGN_COINS}金币,当前连续签到${userInfo.continuityDays}天`);
callback && callback(true, result);
},
function(error) {
callback && callback(false, error);
}
);
}
// 生成邀请码
function generateInviteCode(callback) {
if (!checkLoginStatus()) {
// 直接跳转到用户页面获取信息
const host = window.location.host;
const userPageUrl = `https://${host}/user/index`;
// 保存当前页面URL作为返回地址
const currentUrl = window.location.href;
localStorage.setItem('returnUrl', currentUrl);
// 显示通知
showNotification('请先登录', '跳转到用户页面获取信息...');
// 延迟跳转
setTimeout(() => {
window.location.href = userPageUrl;
}, 1000);
callback && callback(false, {code: 401, msg: '请先登录'});
return;
}
apiRequest(
'Getinvitecode',
{
id: userInfo.id,
username: userInfo.username,
usertoken: userInfo.userToken
},
function(result) {
userInfo.inviteCode = result.data;
saveUserInfo();
showNotification('生成成功', `您的邀请码是:${userInfo.inviteCode}`);
callback && callback(true, result);
},
function(error) {
callback && callback(false, error);
}
);
}
// 使用邀请码
function useInviteCode(inviteCode, callback) {
if (!checkLoginStatus()) {
// 直接跳转到用户页面获取信息
const host = window.location.host;
const userPageUrl = `https://${host}/user/index`;
// 保存当前页面URL作为返回地址
const currentUrl = window.location.href;
localStorage.setItem('returnUrl', currentUrl);
// 显示通知
showNotification('请先登录', '跳转到用户页面获取信息...');
// 延迟跳转
setTimeout(() => {
window.location.href = userPageUrl;
}, 1000);
callback && callback(false, {code: 401, msg: '请先登录'});
return;
}
apiRequest(
'Invitation',
{
id: userInfo.id,
username: userInfo.username,
usertoken: userInfo.userToken,
invitecode: inviteCode
},
function(result) {
userInfo.money += CONSTANTS.INVITE_COINS;
saveUserInfo();
updateUserPanel();
showNotification('邀请成功', `使用邀请码成功,获得${CONSTANTS.INVITE_COINS}金币!`);
callback && callback(true, result);
},
function(error) {
callback && callback(false, error);
}
);
}
// 获取邀请排行榜
function getInviteRanking(callback) {
apiRequest(
'GetinviterList',
{
limit: 10,
sortOrder: 'desc'
},
function(result) {
callback && callback(true, result);
},
function(error) {
callback && callback(false, error);
}
);
}
// 获取未读消息
function getUnreadMessages(callback) {
if (!checkLoginStatus()) {
callback && callback(false, {code: 401, msg: '请先登录'});
return;
}
apiRequest(
'getunreadMessage',
{
userid: userInfo.id,
username: userInfo.username
},
function(result) {
callback && callback(true, result);
},
function(error) {
callback && callback(false, error);
}
);
}
// 获取消息列表
function getMessageList(page, type, callback) {
if (!checkLoginStatus()) {
callback && callback(false, {code: 401, msg: '请先登录'});
return;
}
apiRequest(
'getmessagelist',
{
userid: userInfo.id,
username: userInfo.username,
type: type,
limit: 10,
page: page
},
function(result) {
callback && callback(true, result);
},
function(error) {
callback && callback(false, error);
}
);
}
// 标记消息已读
function markMessagesAsRead(type, callback) {
if (!checkLoginStatus()) {
callback && callback(false, {code: 401, msg: '请先登录'});
return;
}
apiRequest(
'updatemsgstate',
{
userid: userInfo.id,
username: userInfo.username,
type: type
},
function(result) {
callback && callback(true, result);
},
function(error) {
callback && callback(false, error);
}
);
}
// 添加系统消息(本地)
function addSystemMessage(title, content) {
// 将系统消息添加到本地存储
const messages = GM_getValue('localMessages', []);
messages.unshift({
title: title,
content: content,
time: formatDateTime(),
read: false
});
// 限制本地消息数量
if (messages.length > 50) {
messages.pop();
}
GM_setValue('localMessages', messages);
}
// 获取本地消息
function getLocalMessages() {
return GM_getValue('localMessages', []);
}
// 标记本地消息已读
function markLocalMessagesAsRead() {
const messages = GM_getValue('localMessages', []);
messages.forEach(message => {
message.read = true;
});
GM_setValue('localMessages', messages);
}
// 修改记录学习时间函数,同步累计听课时长
function recordStudyTime() {
if (!videoElement || videoElement.paused) return;
const now = Math.floor(Date.now() / 1000);
if (studyStats.studyStartTime === 0) {
studyStats.studyStartTime = now;
return;
}
const elapsed = now - studyStats.studyStartTime;
if (elapsed <= 0) return;
// 更新学习时间
studyStats.todayStudyTime += elapsed;
studyStats.totalStudyTime += elapsed;
// 计算获得的金币和经验
const earnedCoins = Math.floor(elapsed / CONSTANTS.STUDY_TIME_UNIT * CONSTANTS.COINS_PER_HOUR);
const earnedExp = Math.floor(elapsed / CONSTANTS.STUDY_TIME_UNIT * CONSTANTS.STUDY_EXP_PER_HOUR);
if (earnedCoins > 0 && checkLoginStatus()) {
// 增加金币和经验
userInfo.money += earnedCoins;
userInfo.exp += earnedExp;
saveUserInfo();
// 更新统计
studyStats.earnedCoins += earnedCoins;
studyStats.earnedExp += earnedExp;
// 调用听课API,每小时调用一次,但仅当开启了自动累计听课时长时
// 当总学习时间新增了一个小时整数部分时触发
const newTotalHours = Math.floor(studyStats.totalStudyTime / CONSTANTS.STUDY_TIME_UNIT);
const oldTotalHours = Math.floor((studyStats.totalStudyTime - elapsed) / CONSTANTS.STUDY_TIME_UNIT);
if (newTotalHours > oldTotalHours && CONSTANTS.AUTO_RECORD_STUDY_HOURS) {
listenClass();
} else {
// 即使没有触发listenClass,也要保存学习统计,以同步听课时长
saveStudyStats();
}
// 显示通知
if (earnedCoins >= CONSTANTS.COINS_PER_HOUR) {
showNotification('获得奖励', `学习奖励:${earnedCoins}金币,${earnedExp}经验值`);
addSystemMessage('学习奖励', `您通过学习获得了${earnedCoins}金币和${earnedExp}经验值`);
}
} else {
// 即使没有获得金币,也要保存学习统计,以同步听课时长
saveStudyStats();
}
// 重置学习开始时间
studyStats.studyStartTime = now;
updateUserPanel();
}
// 修改获取听课时长函数,使用学号关联的本地存储
function getStudyHours(callback) {
try {
const studentId = GM_getValue('studentId', '');
let localStudyHours;
if (studentId) {
// 从学号关联的本地存储获取累计听课时长
localStudyHours = GM_getValue(`${studentId}_localStudyHours`, 0);
} else {
// 如果没有学号,则使用通用的存储
localStudyHours = GM_getValue('localStudyHours', 0);
}
// 同步本地记录的总学习时间(秒)与听课时长(小时)
if (studentId && studyStats.totalStudyTime > 0) {
const totalStudyHours = Math.floor(studyStats.totalStudyTime / 3600);
if (totalStudyHours > localStudyHours) {
localStudyHours = totalStudyHours;
GM_setValue(`${studentId}_localStudyHours`, localStudyHours);
}
}
// 模拟API返回格式
const result = {
code: 0,
msg: '获取成功',
data: {
study: localStudyHours
}
};
// 调用回调函数,返回成功状态和结果
callback && callback(true, result);
} catch (error) {
console.error('获取听课时长失败:', error);
callback && callback(false, {code: 500, msg: '读取本地存储失败'});
}
}
// 修改听课API函数,更新与学号关联的累计时长
function listenClass() {
// 如果关闭了自动累计听课时长,则跳过此功能
if (!CONSTANTS.AUTO_RECORD_STUDY_HOURS) {
addText("已关闭听课时长累计功能,继续观看课程...");
return;
}
if (!checkLoginStatus()) {
return;
}
// 检查用户金币是否足够
if (userInfo.money < CONSTANTS.COST_PER_HOUR) {
showNotification('金币不足', '您的金币不足,请签到或邀请好友获取更多金币');
// 添加带有按钮的提示信息
const lowCoinsMsg = `
您的金币不足${CONSTANTS.COST_PER_HOUR}个,无法继续累计听课时长
${!userInfo.isSigned ?
'' :
'今日已签到'}
`;
addText(lowCoinsMsg);
// 添加快捷按钮事件
$('#quickSignBtn').on('click', function() {
userSignIn();
});
$('#quickInviteBtn').on('click', function() {
showSettingsModal();
// 切换到邀请标签
setTimeout(() => {
$('.tab-btn[data-tab="invite"]').click();
}, 100);
});
// 添加关闭时长累计的按钮事件
$('#disableRecordBtn').on('click', function() {
CONSTANTS.AUTO_RECORD_STUDY_HOURS = false;
GM_setValue('AUTO_RECORD_STUDY_HOURS', false);
showNotification('已关闭时长累计', '您可以继续观看课程,但不会累计听课时长');
addText("已关闭听课时长累计功能,您可以继续观看课程");
$(this).prop('disabled', true).text('已关闭');
});
return;
}
addText(`正在提交听课时长并扣除${CONSTANTS.COST_PER_HOUR}金币...`);
// 准备API请求参数
const studentId = GM_getValue('studentId', '');
// 实际调用API
apiRequest(
'listenclass',
{
userid: userInfo.id,
username: userInfo.username,
usertoken: userInfo.userToken,
studentid: studentId,
coin: CONSTANTS.COST_PER_HOUR
},
function(result) {
// API请求成功
// 扣除本地金币记录
userInfo.money -= CONSTANTS.COST_PER_HOUR;
saveUserInfo();
updateUserPanel();
try {
// 从本地存储获取当前累计听课时长
let currentStudyHours;
if (studentId) {
currentStudyHours = GM_getValue(`${studentId}_localStudyHours`, 0);
} else {
currentStudyHours = GM_getValue('localStudyHours', 0);
}
// 增加1小时听课时长
currentStudyHours += 1;
// 保存更新后的累计听课时长到本地存储
if (studentId) {
GM_setValue(`${studentId}_localStudyHours`, currentStudyHours);
} else {
GM_setValue('localStudyHours', currentStudyHours);
}
// 同步更新总学习时间(秒)
studyStats.totalStudyTime = currentStudyHours * 3600;
saveStudyStats();
// 成功消息
addText(`成功调用API累计听课时长并消耗${CONSTANTS.COST_PER_HOUR}金币,获得1小时听课时长,当前累计听课时长:${currentStudyHours}小时,剩余${userInfo.money}金币`);
showNotification('累计听课成功', `获得1小时听课时长,当前累计时长:${currentStudyHours}小时`);
// 添加系统消息
addSystemMessage('听课时长累计', `已成功累计1小时听课时长,消耗${CONSTANTS.COST_PER_HOUR}金币`);
} catch (error) {
console.error('更新本地听课时长记录失败:', error);
addText(`API调用成功但更新本地听课时长记录失败: ${error.message}`);
}
},
function(error) {
// API请求失败
console.error('听课API请求失败:', error);
addText(`调用听课API失败: ${error.msg || '未知错误'}`);
showNotification('听课失败', '无法连接到服务器,请检查网络连接');
}
);
}
// 开始记录学习时间
function startRecordingStudyTime() {
if (studyStats.studyStartTime === 0) {
studyStats.studyStartTime = Math.floor(Date.now() / 1000);
// 每分钟记录一次学习时间
setInterval(recordStudyTime, 60 * 1000);
}
}
// 获取当前课程
function getCurrent() {
links = $('a[target="_self"]');
links.each((index, item) => {
if ($(item).hasClass("on")) {
current = index;
return false;
}
});
totalProgress = links.length;
currentProgress = current + 1;
updateProgressBar();
}
// 下一个视频
async function playNext() {
clearInterval(checkCaptchaTimer);
if (current === links.length - 1) {
addText("最后一个已看完!");
showNotification("课程完成", "所有视频已观看完毕!");
} else {
addText("准备播放下一个视频...");
await pause(3);
links[current + 1].click();
}
}
// 输入验证码
async function inputCaptcha() {
if (layuiLayerContent.length && layuiLayerContent.is(':visible')) {
addText("验证码弹窗出现,准备填写验证码...");
await pause(2, 5)
// 获取图片
let imgs = layuiLayerContent.find("img")
let img = imgs[0].style.opacity === '0' ? imgs[1] : imgs[0]
// 图片转base64
let canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0, img.width, img.height);
let code = canvas.toDataURL("image/png").split("base64,")[1];
// 调用接口,识别验证码
let ans = await getCode(code)
// 获取input,填入验证码
let inputs = layuiLayerContent.find("input")
let input = inputs[0].style.display === 'none' ? inputs[1] : inputs[0]
$(input).mousedown()
input.value = ans
// 点击开始播放按钮
await pause(2, 5)
// 尝试点击开始播放按钮
try {
const playButton = $('.layui-layer-btn0');
if (playButton.length) {
// 方法1:直接点击DOM元素
if (playButton[0] && typeof playButton[0].click === 'function') {
playButton[0].click();
}
// 方法2:使用jQuery点击
playButton.trigger('click');
addText("已尝试点击开始播放按钮");
// 等待验证码弹窗消失
let waitCount = 0;
while (layuiLayerContent.is(':visible') && waitCount < 10) {
await pause(1);
waitCount++;
}
// 重新开始视频检测
checkCaptchaTimer = setInterval(playVideo, 1000);
// 如果验证码弹窗依然可见,刷新页面
if (layuiLayerContent.is(':visible')) {
addText("验证码弹窗未关闭,刷新页面...");
await pause(2);
location.reload();
return;
}
// 如果视频存在,尝试直接播放
if (videoElement) {
try {
videoElement.play();
} catch (e) {
addText("视频播放失败:" + e.message);
}
}
} else {
addText("未找到开始播放按钮,尝试刷新页面...");
await pause(2);
location.reload(); // 刷新当前页面
}
} catch (error) {
addText("点击按钮时出错:" + error.message);
await pause(2);
location.reload();
}
}
}
// 使用sw1128的接口,油猴链接:https://greasyfork.org/zh-CN/scripts/459260
function getCode(code) {
return new Promise((resolve, reject) => {
const datas = {
"ImageBase64": String(code),
}
GM_xmlhttpRequest({
method: "POST",
url: "http://119.8.102.43:5000/get_captcha",
data: JSON.stringify(datas),
headers: {
"Content-Type": "application/json",
},
responseType: "json",
timeout: 10000,
ontimeout: async function (e) {
addText("验证码获取超时,刷新页面...");
await pause(3)
location.reload(); // 刷新当前页面
},
onload: function (response) {
if (response.status == 200) {
let result = response.response["message"];
addText("识别结果:" + result);
return resolve(result);
} else {
let result = JSON.parse(response.response)["message"];
addText(result);
}
},
onerror: function (error) {
addText(`${error.statusText} ${error.status} - 出错了`)
},
});
});
}
// 格式化时间
function formatTime(seconds) {
const mins = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
}
// 播放视频,同时检测验证码
async function playVideo() {
timerCnt++;
// 更新进度信息
updateProgressBar();
if (timerCnt % 5 === 0) {
addText("等待加载,已加载:" + timerCnt + "秒");
}
if (timerCnt > 20) {
addText("加载超时,刷新页面");
showNotification("加载超时", "正在刷新页面,请稍候...");
location.reload();
return;
}
if (!videoElement) {
if (links && links[current] && links[current].title && links[current].title === "考试") {
addText("课程已看完,自动停止!");
showNotification("课程完成", "所有视频已观看完毕!");
clearInterval(checkCaptchaTimer);
} else {
getVideoElement();
}
return;
}
// 验证码弹窗
layuiLayerContent = $('.layui-layer-content');
if (layuiLayerContent.length > 0 && layuiLayerContent.is(':visible')) {
clearInterval(checkCaptchaTimer);
showNotification("验证码", "检测到验证码,正在自动识别...");
await inputCaptcha();
return;
}
// 检测视频是否加载成功且暂停
if (videoElement.paused) {
try {
await videoElement.play();
if (videoElement.readyState === 4) {
const message = containerTextElement.text().includes("视频加载完成")
? "请将浏览器置于前台运行。(若学时会增加可忽略)" : "视频加载完成,准备播放";
addText(message);
}
} catch (e) {
addText("视频播放失败:" + e.message);
// 如果播放失败,尝试重新获取视频元素
getVideoElement();
}
} else {
timerCnt = 0;
}
}
// 暂停函数
function pause(start, end = undefined) {
let delay = start;
if (end) {
delay = Math.floor(Math.random() * (end - start)) + start;
addText(`等待 ${delay} 秒后继续...`);
}
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, delay * 1000); // 将秒转换为毫秒
});
}
// 添加交互显示
const addContainer = () => {
const container = $('');
// 构建标题栏
const header = $(`
`);
// 构建主体内容
const content = $('');
// 用户信息面板(默认隐藏,登录后显示)
const userPanel = $(`
`);
// 登录注册按钮(未登录时显示)
const authButtons = $(`
`);
// 构建进度条
const progressSection = $(`
`);
// 构建控制按钮
const controlsSection = $(`
`);
// 构建日志区域
const logSection = $(`
`);
// 构建提示信息
const tipSection = $(`
如果开启了系统代理,请先关闭
请允许跨域资源请求以识别验证码
如需累计学时,请保持浏览器在前台运行
登录账号可获取金币奖励、签到福利
`);
// 构建底部信息
const footerSection = $(`
`);
// 组装所有部分 - 移除themeSection
content.append(userPanel, authButtons, progressSection, controlsSection, logSection, tipSection, footerSection);
container.append(header, content);
// 将容器添加到页面
$("body").append(container);
// 设置容器的可拖动性
header.on("mousedown", function(event) {
if ($(event.target).closest('.panel-controls').length === 0) {
const initialX = event.clientX;
const initialY = event.clientY;
const initialLeft = container.offset().left;
const initialTop = container.offset().top;
function onMouseMove(event) {
container.css({
left: initialLeft + (event.clientX - initialX) + 'px',
top: initialTop + (event.clientY - initialY) + 'px'
});
}
function onMouseUp() {
$(document).off('mousemove', onMouseMove);
$(document).off('mouseup', onMouseUp);
}
$(document).on('mousemove', onMouseMove);
$(document).on('mouseup', onMouseUp);
}
});
// 设置容器的可缩放性
const resizer = $('');
container.append(resizer);
resizer.on("mousedown", function(event) {
const initialX = event.clientX;
const initialWidth = container.width();
function onMouseMove(event) {
const newWidth = initialWidth + (event.clientX - initialX);
if (newWidth >= 300) {
container.css('width', newWidth + 'px');
}
}
function onMouseUp() {
$(document).off('mousemove', onMouseMove);
$(document).off('mouseup', onMouseUp);
}
$(document).on('mousemove', onMouseMove);
$(document).on('mouseup', onMouseUp);
});
// 储存日志容器元素
containerTextElement = $('#logContainer');
// 设置事件监听器
$('#toggleVisibilityButton').on('click', togglePanelVisibility);
$('#closeButton').on('click', minimizePanel);
$('#autoPlayButton').on('click', toggleAutoPlay);
$('#muteButton').on('click', toggleMute);
$('#skipButton').on('click', playNext);
$('#restartButton').on('click', restartVideo);
$('#clearLogButton').on('click', clearLogs);
$('#settingsButton').on('click', showSettingsModal);
// 用户相关按钮事件
$('#loginButton').on('click', function() {
// 直接跳转到用户页面获取信息
const host = window.location.host;
const userPageUrl = `https://${host}/user/index`;
// 保存当前页面URL作为返回地址
const currentUrl = window.location.href;
localStorage.setItem('returnUrl', currentUrl);
// 显示通知
showNotification('正在授权', '跳转到用户页面获取信息...');
// 延迟跳转
setTimeout(() => {
window.location.href = userPageUrl;
}, 1000);
});
$('#registerButton').on('click', function() {
// 直接跳转到用户页面获取信息
const host = window.location.host;
const userPageUrl = `https://${host}/user/index`;
// 保存当前页面URL作为返回地址
const currentUrl = window.location.href;
localStorage.setItem('returnUrl', currentUrl);
// 显示通知
showNotification('正在授权', '跳转到用户页面获取信息...');
// 延迟跳转
setTimeout(() => {
window.location.href = userPageUrl;
}, 1000);
});
$('#signInButton').on('click', userSignIn);
$('#messageButton').on('click', showMessageCenter);
// 初始化UI状态
updateMuteButton();
changeTheme(themeColor);
if (!isVisible) {
togglePanelVisibility();
}
// 检查登录状态并更新UI
updateUserPanel(); // 调用更新用户面板,根据登录状态显示或隐藏按钮
}
// 最小化面板
function minimizePanel() {
$('.yjlt-auto-player').addClass('minimized');
showMinimizedIndicator();
}
// 显示最小化指示器
function showMinimizedIndicator() {
const indicator = $(`
`);
$('body').append(indicator);
indicator.on('click', function() {
$('.yjlt-auto-player').removeClass('minimized');
$(this).remove();
});
}
// 重启当前视频
function restartVideo() {
if (videoElement) {
videoElement.currentTime = 0;
videoElement.play();
addText("已重新开始当前视频");
}
}
// 清空日志
function clearLogs() {
containerTextElement.empty();
logs = [];
addText("日志已清空");
}
// 添加交互文本
const addText = text => {
const timestamp = new Date().toLocaleTimeString();
const logEntry = `[${timestamp}] ${text}
`;
containerTextElement.append(logEntry);
containerTextElement.scrollTop(containerTextElement[0].scrollHeight);
// 保存日志
logs.push({ time: timestamp, text: text });
// 限制日志数量
if (logs.length > 100) {
logs.shift();
}
}
// 添加样式
const addStyle = () => {
const style = $("");
style.prop('type', 'text/css');
style.html(`
:root {
--primary-color: #1e88e5;
--secondary-color: #64b5f6;
--background-gradient: linear-gradient(135deg, #42a5f5 0%, #1976d2 100%);
--text-color: #333;
--light-text: #fff;
--border-radius: 8px;
--shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
--panel-bg: #fff;
--section-bg: #f5f7fa;
--success-color: #4caf50;
--warning-color: #ff9800;
--danger-color: #f44336;
}
.yjlt-auto-player {
position: fixed;
top: 50px;
right: 20px;
width: 380px;
font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
z-index: 9999999;
background-color: var(--panel-bg);
box-shadow: var(--shadow);
border-radius: var(--border-radius);
transition: all 0.3s ease;
overflow: hidden;
}
.yjlt-auto-player.minimized {
transform: translateX(400px);
}
.panel-header {
background: var(--background-gradient);
color: var(--light-text);
padding: 12px 15px;
display: flex;
justify-content: space-between;
align-items: center;
cursor: move;
user-select: none;
}
.panel-title {
font-weight: 600;
font-size: 16px;
display: flex;
align-items: center;
gap: 8px;
}
.version-tag {
background: rgba(255, 255, 255, 0.2);
padding: 2px 6px;
border-radius: 12px;
font-size: 12px;
font-weight: normal;
}
.panel-controls {
display: flex;
gap: 5px;
}
.header-btn {
background: none;
border: none;
color: var(--light-text);
cursor: pointer;
width: 24px;
height: 24px;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.2s;
}
.header-btn:hover {
background: rgba(255, 255, 255, 0.2);
}
.panel-content {
padding: 15px;
}
/* 用户面板 */
.user-panel {
margin-bottom: 15px;
border-radius: var(--border-radius);
overflow: hidden;
background-color: var(--section-bg);
}
.user-info {
padding: 12px;
display: flex;
align-items: center;
}
.user-avatar {
width: 50px;
height: 50px;
border-radius: 50%;
overflow: hidden;
}
.user-avatar img {
width: 100%;
height: 100%;
object-fit: cover;
}
.user-details {
margin-left: 12px;
flex: 1;
}
.user-name {
font-weight: 600;
font-size: 16px;
margin-bottom: 4px;
}
.user-stats {
display: flex;
gap: 12px;
font-size: 13px;
color: #555;
}
.user-actions {
display: flex;
gap: 8px;
}
.user-btn {
background-color: var(--primary-color);
color: white;
border: none;
border-radius: 4px;
padding: 5px 10px;
font-size: 12px;
cursor: pointer;
display: flex;
align-items: center;
gap: 4px;
transition: all 0.2s;
}
.user-btn:hover {
background-color: var(--secondary-color);
}
.user-btn:disabled {
background-color: #ccc;
cursor: not-allowed;
}
.badge {
position: absolute;
top: -5px;
right: -5px;
background-color: var(--danger-color);
color: white;
border-radius: 10px;
padding: 0px 5px;
font-size: 10px;
min-width: 15px;
min-height: 15px;
display: flex;
align-items: center;
justify-content: center;
}
/* 登录注册按钮 */
.auth-buttons {
display: flex;
gap: 10px;
margin-bottom: 15px;
}
.auth-btn {
flex: 1;
background-color: var(--section-bg);
color: var(--text-color);
border: none;
border-radius: var(--border-radius);
padding: 10px;
font-size: 14px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
transition: all 0.2s;
}
.auth-btn:hover {
background-color: var(--secondary-color);
color: white;
}
/* 进度条 */
.progress-section {
margin-bottom: 15px;
}
.progress-container {
height: 8px;
background-color: #e0e0e0;
border-radius: 4px;
overflow: hidden;
margin-bottom: 8px;
}
.progress-bar {
height: 100%;
background-color: var(--primary-color);
transition: width 0.3s ease;
width: 0%;
}
.progress-text {
font-size: 14px;
color: var(--text-color);
text-align: center;
}
/* 视频进度 */
.video-progress-info {
margin-bottom: 15px;
background-color: var(--section-bg);
border-radius: var(--border-radius);
padding: 12px;
}
.video-progress-container {
height: 8px;
background-color: #e0e0e0;
border-radius: 4px;
overflow: hidden;
margin-bottom: 8px;
}
.video-progress-bar {
height: 100%;
background-color: var(--primary-color);
transition: width 0.3s ease;
width: 0%;
}
.video-time {
font-size: 13px;
color: var(--text-color);
text-align: center;
}
/* 控制按钮 */
.controls-section {
display: flex;
gap: 10px;
margin-bottom: 15px;
justify-content: center;
}
.control-btn {
border: none;
background-color: var(--section-bg);
color: var(--text-color);
padding: 8px 12px;
border-radius: 4px;
cursor: pointer;
font-weight: 500;
transition: all 0.2s;
min-width: 40px;
display: flex;
align-items: center;
justify-content: center;
}
.control-btn:hover {
background-color: var(--secondary-color);
color: var(--light-text);
}
.btn-success {
background-color: var(--success-color);
color: var(--light-text);
}
.btn-danger {
background-color: var(--danger-color);
color: var(--light-text);
}
/* 日志区域 */
.log-section {
background-color: var(--section-bg);
border-radius: var(--border-radius);
padding: 12px;
margin-bottom: 15px;
}
.log-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.log-title {
font-size: 14px;
font-weight: 500;
color: var(--text-color);
display: flex;
align-items: center;
gap: 5px;
}
.log-btn {
background: none;
border: none;
color: var(--text-color);
cursor: pointer;
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
transition: all 0.2s;
}
.log-btn:hover {
background-color: rgba(0, 0, 0, 0.1);
}
.log-container {
max-height: 150px;
overflow-y: auto;
padding: 10px;
background-color: #fff;
border-radius: 4px;
border: 1px solid #e0e0e0;
font-size: 13px;
line-height: 1.5;
}
.log-entry {
margin-bottom: 6px;
border-bottom: 1px dashed #eee;
padding-bottom: 6px;
}
.log-time {
color: var(--primary-color);
font-weight: 500;
margin-right: 6px;
}
/* 提示区域 */
.tip-section {
background-color: var(--section-bg);
border-radius: var(--border-radius);
padding: 12px;
margin-bottom: 15px;
}
.tip-item {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 8px;
font-size: 13px;
color: var(--text-color);
}
.tip-item i {
color: var(--primary-color);
}
/* 底部信息 */
.footer-section {
font-size: 12px;
color: #888;
text-align: center;
margin-top: 10px;
display: flex;
justify-content: space-between;
}
/* 缩放手柄 */
.resizer {
position: absolute;
bottom: 0;
right: 0;
width: 15px;
height: 15px;
cursor: nwse-resize;
background: transparent;
}
.resizer::after {
content: '';
position: absolute;
right: 3px;
bottom: 3px;
width: 5px;
height: 5px;
border-right: 2px solid var(--primary-color);
border-bottom: 2px solid var(--primary-color);
}
/* 最小化指示器 */
.minimized-indicator {
position: fixed;
right: 20px;
top: 50px;
width: 40px;
height: 40px;
border-radius: 50%;
background: var(--background-gradient);
display: flex;
align-items: center;
justify-content: center;
color: #fff;
cursor: pointer;
box-shadow: var(--shadow);
z-index: 9999999;
animation: pulse 2s infinite;
}
@keyframes pulse {
0% {
transform: scale(1);
}
50% {
transform: scale(1.1);
}
100% {
transform: scale(1);
}
}
/* 通知 */
.custom-notification {
position: fixed;
top: 20px;
right: 20px;
max-width: 300px;
background-color: var(--panel-bg);
border-left: 4px solid var(--primary-color);
border-radius: 4px;
padding: 15px;
box-shadow: var(--shadow);
z-index: 9999999;
transform: translateX(350px);
opacity: 0;
transition: all 0.3s ease;
}
.custom-notification.show {
transform: translateX(0);
opacity: 1;
}
.notification-title {
font-weight: 600;
margin-bottom: 5px;
color: var(--primary-color);
}
.notification-message {
font-size: 14px;
color: var(--text-color);
}
/* 模态框 */
.yjlt-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 10000000;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
visibility: hidden;
transition: all 0.3s ease;
}
.yjlt-modal.show {
opacity: 1;
visibility: visible;
}
.modal-content {
width: 90%;
max-width: 500px;
max-height: 80vh;
background-color: var(--panel-bg);
border-radius: var(--border-radius);
box-shadow: var(--shadow);
overflow: hidden;
transform: scale(0.9);
transition: transform 0.3s ease;
}
.yjlt-modal.show .modal-content {
transform: scale(1);
}
.modal-header {
padding: 15px;
background: var(--background-gradient);
color: var(--light-text);
display: flex;
justify-content: space-between;
align-items: center;
}
.modal-header h3 {
margin: 0;
font-size: 18px;
font-weight: 600;
}
.modal-close-btn {
background: none;
border: none;
color: var(--light-text);
font-size: 24px;
cursor: pointer;
line-height: 1;
}
.modal-body {
padding: 20px;
max-height: calc(80vh - 120px);
overflow-y: auto;
}
.modal-footer {
padding: 15px;
display: flex;
justify-content: flex-end;
gap: 10px;
border-top: 1px solid #eee;
}
.modal-btn {
padding: 8px 16px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
}
.btn-primary {
background-color: var(--primary-color);
color: white;
}
.btn-text {
background: none;
color: var(--primary-color);
}
/* 表单 */
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: 500;
}
.form-group input {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
}
.form-error {
color: var(--danger-color);
font-size: 14px;
margin-top: 10px;
}
/* 开关 */
.switch {
position: relative;
display: inline-block;
width: 40px;
height: 20px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 16px;
width: 16px;
left: 2px;
bottom: 2px;
background-color: white;
transition: .4s;
}
input:checked + .slider {
background-color: var(--primary-color);
}
input:checked + .slider:before {
transform: translateX(20px);
}
.slider.round {
border-radius: 20px;
}
.slider.round:before {
border-radius: 50%;
}
/* 设置 */
.tabs {
display: flex;
flex-direction: column;
}
.tab-header {
display: flex;
overflow-x: auto;
border-bottom: 1px solid #eee;
margin-bottom: 15px;
}
.tab-btn {
padding: 10px 15px;
border: none;
background: none;
cursor: pointer;
font-size: 14px;
font-weight: 500;
color: #666;
border-bottom: 2px solid transparent;
transition: all 0.2s;
}
.tab-btn.active {
color: var(--primary-color);
border-bottom-color: var(--primary-color);
}
.tab-pane {
display: none;
}
.tab-pane.active {
display: block;
}
.setting-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 0;
border-bottom: 1px solid #eee;
}
.setting-item:last-child {
border-bottom: none;
}
/* 主题选择按钮样式 */
.theme-options {
display: flex;
gap: 10px;
}
.theme-btn {
width: 24px;
height: 24px;
border-radius: 50%;
border: 2px solid transparent;
cursor: pointer;
transition: all 0.2s;
}
.theme-btn[data-theme="blue"] {
background: linear-gradient(135deg, #42a5f5 0%, #1976d2 100%);
}
.theme-btn[data-theme="green"] {
background: linear-gradient(135deg, #66bb6a 0%, #2e7d32 100%);
}
.theme-btn[data-theme="purple"] {
background: linear-gradient(135deg, #ab47bc 0%, #6a1b9a 100%);
}
.theme-btn[data-theme="orange"] {
background: linear-gradient(135deg, #ffa726 0%, #ef6c00 100%);
}
.theme-btn[data-theme="red"] {
background: linear-gradient(135deg, #ef5350 0%, #c62828 100%);
}
.theme-btn.active {
border-color: #333;
transform: scale(1.1);
}
.stats-info {
margin-top: 10px;
font-size: 14px;
color: #666;
}
.stats-info p {
margin: 5px 0;
}
.account-info {
text-align: center;
}
.user-profile {
margin-bottom: 20px;
}
.profile-avatar {
width: 80px;
height: 80px;
border-radius: 50%;
margin-bottom: 10px;
}
.account-details {
text-align: left;
background-color: var(--section-bg);
padding: 15px;
border-radius: var(--border-radius);
margin-bottom: 20px;
}
.account-details p {
margin: 8px 0;
display: flex;
justify-content: space-between;
}
.setting-btn {
padding: 8px 16px;
border: none;
background-color: var(--primary-color);
color: white;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: all 0.2s;
}
.setting-btn:hover {
background-color: var(--secondary-color);
}
.not-logged {
text-align: center;
padding: 20px;
}
.auth-actions {
margin-top: 20px;
display: flex;
justify-content: center;
gap: 15px;
}
/* 邀请 */
.invite-section {
margin-bottom: 30px;
}
.invite-section h3 {
margin-top: 0;
}
.invite-code {
background-color: var(--section-bg);
padding: 15px;
border-radius: var(--border-radius);
margin: 15px 0;
display: flex;
align-items: center;
justify-content: space-between;
}
.generate-invite {
text-align: center;
margin: 15px 0;
}
.use-invite {
margin-top: 20px;
}
.use-invite .form-group {
display: flex;
gap: 10px;
}
.use-invite input {
flex: 1;
}
.invite-ranking h3 {
margin-top: 0;
}
.ranking-list {
display: flex;
flex-direction: column;
gap: 10px;
}
.ranking-item {
display: flex;
align-items: center;
padding: 10px;
background-color: var(--section-bg);
border-radius: var(--border-radius);
}
.rank-num {
width: 30px;
height: 30px;
border-radius: 50%;
background-color: #ddd;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
margin-right: 10px;
}
.rank-1 .rank-num {
background-color: gold;
color: #333;
}
.rank-2 .rank-num {
background-color: silver;
color: #333;
}
.rank-3 .rank-num {
background-color: #cd7f32;
color: white;
}
.rank-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
overflow: hidden;
margin-right: 10px;
}
.rank-avatar img {
width: 100%;
height: 100%;
object-fit: cover;
}
.rank-info {
flex: 1;
}
.rank-name {
font-weight: 500;
}
.rank-count {
font-size: 12px;
color: #666;
}
.loading-text, .empty-text {
text-align: center;
padding: 20px;
color: #666;
}
/* 消息中心 */
.message-center {
display: flex;
flex-direction: column;
height: 100%;
}
.message-tabs {
display: flex;
overflow-x: auto;
border-bottom: 1px solid #eee;
margin-bottom: 15px;
}
.message-tab {
padding: 10px 15px;
border: none;
background: none;
cursor: pointer;
font-size: 14px;
white-space: nowrap;
color: #666;
border-bottom: 2px solid transparent;
transition: all 0.2s;
}
.message-tab.active {
color: var(--primary-color);
border-bottom-color: var(--primary-color);
}
.message-list {
flex: 1;
overflow-y: auto;
margin-bottom: 15px;
}
.message-item {
padding: 15px;
border-bottom: 1px solid #eee;
background-color: #fff;
}
.message-item.unread {
background-color: rgba(30, 136, 229, 0.05);
border-left: 3px solid var(--primary-color);
}
.message-header {
display: flex;
justify-content: space-between;
margin-bottom: 10px;
}
.message-title {
font-weight: 500;
display: flex;
align-items: center;
gap: 8px;
}
.message-type {
padding: 2px 6px;
background-color: var(--section-bg);
border-radius: 4px;
font-size: 12px;
font-weight: normal;
}
.message-time {
font-size: 12px;
color: #999;
}
.message-content {
font-size: 14px;
line-height: 1.5;
color: #333;
}
.message-pagination {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 0;
border-top: 1px solid #eee;
}
.pagination-btn {
padding: 5px 10px;
border: none;
background-color: var(--section-bg);
color: var(--text-color);
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: all 0.2s;
}
.pagination-btn:disabled {
background-color: #eee;
color: #999;
cursor: not-allowed;
}
/* 关于 */
.about-section {
text-align: center;
}
.about-section h3 {
margin-top: 0;
}
.feature-list {
text-align: left;
margin-top: 20px;
}
.feature-list ul {
padding-left: 20px;
}
.feature-list li {
margin-bottom: 5px;
}
/* 警告消息样式 */
.warn-message {
background-color: #fff8e1;
border-left: 3px solid #ffc107;
padding: 10px;
margin: 5px 0;
border-radius: 4px;
}
.warn-actions {
display: flex;
gap: 10px;
margin-top: 8px;
}
.warn-btn {
background-color: var(--primary-color);
color: white;
border: none;
border-radius: 4px;
padding: 5px 10px;
font-size: 12px;
cursor: pointer;
transition: all 0.2s;
}
.warn-btn:hover {
background-color: var(--secondary-color);
}
/* 信息提示框 */
.info-box {
background-color: #e3f2fd;
border-left: 3px solid var(--primary-color);
padding: 10px;
margin: 5px 0;
border-radius: 4px;
font-size: 14px;
}
.info-box p {
margin: 5px 0;
}
.info-box .note {
font-size: 12px;
color: #666;
font-style: italic;
}
/* 按钮样式 */
.btn-outline {
background-color: transparent !important;
border: 1px solid var(--primary-color) !important;
color: var(--primary-color) !important;
}
.btn-outline:hover {
background-color: var(--primary-color) !important;
color: white !important;
}
.user-panel .remaining-time {
margin-top: 2px;
font-size: 12px;
color: #555;
}
.user-panel .remaining-time.warning {
color: #f44336;
}
.user-panel .remaining-time i {
margin-right: 4px;
}
`);
$('body').append(style);
}
// 切换视频静音状态
function toggleMute() {
if (videoElement) {
isMuted = !isMuted;
videoElement.muted = isMuted;
GM_setValue('isMuted', isMuted);
updateMuteButton();
}
}
// 更新静音按钮状态
function updateMuteButton() {
const muteIcon = isMuted ? 'fa-volume-mute' : 'fa-volume-up';
$('#muteButton i').attr('class', 'fas ' + muteIcon);
}
// 切换自动播放
function toggleAutoPlay(forceEnable) {
// 如果提供了forceEnable参数,则强制设置自动播放状态
if (forceEnable !== undefined) {
autoPlayEnabled = forceEnable;
} else {
// 否则切换状态
autoPlayEnabled = !autoPlayEnabled;
}
// 保存状态到GM存储
GM_setValue('autoPlayEnabled', autoPlayEnabled);
if (autoPlayEnabled) {
checkCaptchaTimer = setInterval(playVideo, 1000);
$('#autoPlayButton').removeClass('btn-danger').addClass('btn-success');
$('#autoPlayButton i').removeClass('fa-pause').addClass('fa-play');
addText("自动播放已启用");
// 如果视频存在且暂停,则开始播放
if (videoElement && videoElement.paused) {
videoElement.play();
}
} else {
clearInterval(checkCaptchaTimer);
$('#autoPlayButton').removeClass('btn-success').addClass('btn-danger');
$('#autoPlayButton i').removeClass('fa-play').addClass('fa-pause');
addText("自动播放已暂停");
// 如果视频存在且正在播放,则暂停
if (videoElement && !videoElement.paused) {
videoElement.pause();
}
}
}
// 更新进度条
function updateProgressBar() {
const percentage = Math.floor((currentProgress / totalProgress) * 100);
$('#progressBar').css('width', percentage + '%');
$('#progressText').text(`进度: ${currentProgress}/${totalProgress} (${percentage}%)`);
}
// 显示通知
function showNotification(title, message) {
const notification = $('');
notification.html(`
${title}
${message}
`);
$('body').append(notification);
setTimeout(() => {
notification.addClass('show');
setTimeout(() => {
notification.removeClass('show');
setTimeout(() => notification.remove(), 500);
}, 3000);
}, 100);
}
// 切换主题颜色
function changeTheme(color) {
themeColor = color;
GM_setValue('themeColor', color);
const cssVars = {
'blue': {
'primary': '#1e88e5',
'secondary': '#64b5f6',
'background': 'linear-gradient(135deg, #42a5f5 0%, #1976d2 100%)'
},
'green': {
'primary': '#43a047',
'secondary': '#81c784',
'background': 'linear-gradient(135deg, #66bb6a 0%, #2e7d32 100%)'
},
'purple': {
'primary': '#8e24aa',
'secondary': '#ba68c8',
'background': 'linear-gradient(135deg, #ab47bc 0%, #6a1b9a 100%)'
},
'orange': {
'primary': '#fb8c00',
'secondary': '#ffb74d',
'background': 'linear-gradient(135deg, #ffa726 0%, #ef6c00 100%)'
},
'red': {
'primary': '#e53935',
'secondary': '#ef5350',
'background': 'linear-gradient(135deg, #ef5350 0%, #c62828 100%)'
}
};
const root = document.documentElement;
root.style.setProperty('--primary-color', cssVars[color].primary);
root.style.setProperty('--secondary-color', cssVars[color].secondary);
root.style.setProperty('--background-gradient', cssVars[color].background);
$('.theme-btn').removeClass('active');
$(`.theme-btn[data-theme="${color}"]`).addClass('active');
}
// 切换面板可见性
function togglePanelVisibility() {
isVisible = !isVisible;
GM_setValue('isVisible', isVisible);
if (isVisible) {
$('.panel-content').slideDown(300);
$('#toggleVisibilityButton i').removeClass('fa-chevron-down').addClass('fa-chevron-up');
} else {
$('.panel-content').slideUp(300);
$('#toggleVisibilityButton i').removeClass('fa-chevron-up').addClass('fa-chevron-down');
}
}
// 添加防止跳转循环的检测函数
function isRedirectLocked() {
const now = Date.now();
const lastRedirectTime = parseInt(localStorage.getItem('lastRedirectTime') || '0');
const redirectCount = parseInt(localStorage.getItem('redirectCount') || '0');
// 如果5秒内跳转超过3次,则锁定跳转
if (now - lastRedirectTime < 5000) {
if (redirectCount >= 2) {
return true;
} else {
localStorage.setItem('redirectCount', (redirectCount + 1).toString());
}
} else {
// 重置计数
localStorage.setItem('redirectCount', '1');
}
localStorage.setItem('lastRedirectTime', now.toString());
return false;
}
// 解锁跳转
function unlockRedirect() {
localStorage.removeItem('lastRedirectTime');
localStorage.removeItem('redirectCount');
}
// 添加防检测和防封号设置
const ANTI_DETECTION = {
// 是否启用防检测功能
ENABLED: true,
// 视频播放前的随机延迟(秒)
PLAY_DELAY_MIN: 2,
PLAY_DELAY_MAX: 8,
// 视频播放中的随机暂停概率(百分比) - 降低到1%
RANDOM_PAUSE_CHANCE: 1,
// 随机暂停的持续时间(秒)
RANDOM_PAUSE_MIN: 3,
RANDOM_PAUSE_MAX: 10,
// 鼠标随机移动的时间间隔(毫秒)
MOUSE_MOVE_INTERVAL: 30000,
// 页面滚动的概率(百分比)
SCROLL_CHANCE: 20,
// 播放速度随机变化的概率(百分比) - 设为0禁用倍速播放
SPEED_CHANGE_CHANCE: 0,
// 随机更新用户活跃状态的时间间隔(毫秒)
ACTIVITY_UPDATE_INTERVAL: 60000,
// 防检测日志输出
LOG_ACTIONS: true
};
// 添加人工操作模拟功能
let humanActivityTimer = null;
let lastActivityTime = Date.now();
let behaviorPatterns = [];
// 模拟人类行为的函数
function simulateHumanBehavior() {
if (!ANTI_DETECTION.ENABLED) return;
// 修改为0-3的随机行为类型,去掉播放速度调整
const actionType = Math.floor(Math.random() * 4);
switch (actionType) {
case 0: // 随机鼠标移动
simulateMouseMovement();
break;
case 1: // 随机页面滚动
if (Math.random() * 100 < ANTI_DETECTION.SCROLL_CHANCE) {
simulateScroll();
}
break;
case 2: // 随机暂停/播放 (概率已在配置中降低)
if (videoElement && Math.random() * 100 < ANTI_DETECTION.RANDOM_PAUSE_CHANCE) {
simulateVideoPause();
}
break;
case 3: // 随机聚焦视频元素
if (videoElement) {
videoElement.focus();
logAntiDetectionAction("模拟用户聚焦视频");
}
break;
}
// 更新最后活跃时间
lastActivityTime = Date.now();
// 记录行为模式
recordBehaviorPattern(actionType);
}
// 模拟鼠标移动
function simulateMouseMovement() {
// 生成随机坐标
const x = Math.floor(Math.random() * window.innerWidth);
const y = Math.floor(Math.random() * window.innerHeight);
// 创建和分发鼠标移动事件
try {
const mouseEvent = new MouseEvent('mousemove', {
view: window,
bubbles: true,
cancelable: true,
clientX: x,
clientY: y
});
document.dispatchEvent(mouseEvent);
logAntiDetectionAction(`模拟鼠标移动到坐标 (${x}, ${y})`);
} catch (e) {
console.error("模拟鼠标移动失败:", e);
}
}
// 模拟页面滚动
function simulateScroll() {
const scrollAmount = Math.floor(Math.random() * 300) - 150; // -150到150的随机滚动量
try {
window.scrollBy({
top: scrollAmount,
behavior: 'smooth'
});
logAntiDetectionAction(`模拟页面滚动 ${scrollAmount > 0 ? '向下' : '向上'} ${Math.abs(scrollAmount)}px`);
} catch (e) {
console.error("模拟页面滚动失败:", e);
}
}
// 模拟视频暂停/播放
async function simulateVideoPause() {
if (!videoElement) return;
try {
if (!videoElement.paused) {
videoElement.pause();
logAntiDetectionAction("模拟用户暂停视频");
// 随机暂停时长
const pauseDuration = Math.floor(Math.random() *
(ANTI_DETECTION.RANDOM_PAUSE_MAX - ANTI_DETECTION.RANDOM_PAUSE_MIN)) +
ANTI_DETECTION.RANDOM_PAUSE_MIN;
await new Promise(resolve => setTimeout(resolve, pauseDuration * 1000));
videoElement.play();
logAntiDetectionAction(`暂停${pauseDuration}秒后继续播放`);
}
} catch (e) {
console.error("模拟视频暂停/播放失败:", e);
}
}
// 记录行为模式以分析规律
function recordBehaviorPattern(actionType) {
const currentTime = Date.now();
behaviorPatterns.push({
type: actionType,
time: currentTime
});
// 只保留最近100条行为记录
if (behaviorPatterns.length > 100) {
behaviorPatterns.shift();
}
// 检查是否存在规律性行为,如果存在则打乱
if (behaviorPatterns.length >= 10) {
breakPatternIfNeeded();
}
}
// 检查并打破可能被检测的规律性行为
function breakPatternIfNeeded() {
// 检查最近的行为是否有明显的规律(简单实现)
let patternDetected = false;
// 检查最近5个行为的时间间隔是否过于规律
const recentActions = behaviorPatterns.slice(-5);
const intervals = [];
for (let i = 1; i < recentActions.length; i++) {
intervals.push(recentActions[i].time - recentActions[i-1].time);
}
// 计算时间间隔的标准差,如果太小说明太规律
const mean = intervals.reduce((sum, val) => sum + val, 0) / intervals.length;
const variance = intervals.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / intervals.length;
const stdDev = Math.sqrt(variance);
// 如果标准差小于平均值的10%,认为过于规律
if (stdDev < mean * 0.1) {
patternDetected = true;
}
// 检查行为类型是否过于规律
const actionTypes = recentActions.map(a => a.type);
const uniqueTypes = new Set(actionTypes).size;
// 如果行为类型过于单一,也认为可能被检测
if (uniqueTypes <= 1) {
patternDetected = true;
}
// 如果检测到规律,则执行一些随机行为来打破规律
if (patternDetected) {
logAntiDetectionAction("检测到行为过于规律,执行随机操作打破规律");
// 随机执行2-4次不同的操作
const breakCount = Math.floor(Math.random() * 3) + 2;
for (let i = 0; i < breakCount; i++) {
simulateHumanBehavior();
}
}
}
// 记录防检测操作的日志
function logAntiDetectionAction(message) {
if (ANTI_DETECTION.LOG_ACTIONS) {
console.log(`[防检测] ${message}`);
// 不在界面上显示防检测日志,避免引起注意
}
}
// 启动防检测系统
function startAntiDetectionSystem() {
if (!ANTI_DETECTION.ENABLED) return;
// 清除可能存在的旧计时器
if (humanActivityTimer) {
clearInterval(humanActivityTimer);
}
// 设置随机的人工操作模拟定时器
const randomInterval = () => Math.floor(Math.random() * 20000) + ANTI_DETECTION.MOUSE_MOVE_INTERVAL;
// 初始化最后活跃时间
lastActivityTime = Date.now();
// 设置第一次触发的时间
setTimeout(() => {
simulateHumanBehavior();
// 后续使用不规则的时间间隔
humanActivityTimer = setInterval(() => {
// 清除当前定时器
clearInterval(humanActivityTimer);
// 模拟人工操作
simulateHumanBehavior();
// 设置新的随机间隔定时器
humanActivityTimer = setInterval(simulateHumanBehavior, randomInterval());
}, randomInterval());
}, Math.floor(Math.random() * 10000) + 5000); // 5-15秒后开始第一次模拟
logAntiDetectionAction("防检测系统已启动");
}
// 关闭防检测系统
function stopAntiDetectionSystem() {
if (humanActivityTimer) {
clearInterval(humanActivityTimer);
humanActivityTimer = null;
logAntiDetectionAction("防检测系统已关闭");
}
}
// 修改获取视频元素函数以支持防检测
const getVideoElement = () => {
videoElement = document.querySelector("video");
if (!videoElement) return;
videoElement.muted = true;
// 始终使用1.0倍速,不再随机调整
videoElement.playbackRate = 1.0;
logAntiDetectionAction(`播放速度固定为 1.0x`);
videoElement.volume = 0;
// 添加视频事件监听
videoElement.onended = async function () {
if (ANTI_DETECTION.ENABLED) {
logAntiDetectionAction("视频播放完毕,随机延迟后播放下一个");
// 视频结束后延迟随机时间再播放下一个
const delayBeforeNext = Math.floor(Math.random() *
(ANTI_DETECTION.PLAY_DELAY_MAX - ANTI_DETECTION.PLAY_DELAY_MIN)) +
ANTI_DETECTION.PLAY_DELAY_MIN;
await pause(delayBeforeNext);
}
await playNext();
};
videoElement.ontimeupdate = function() {
if (videoElement && videoElement.duration) {
const percent = (videoElement.currentTime / videoElement.duration) * 100;
$('#videoProgressBar').css('width', percent + '%');
$('#videoTimeText').text(formatTime(videoElement.currentTime) + ' / ' + formatTime(videoElement.duration));
}
};
// 创建视频进度信息块
if ($('.video-progress-info').length === 0) {
const videoProgressInfo = $(`
`);
$('.panel-content').prepend(videoProgressInfo);
}
}
// 修改播放视频函数,添加防检测机制
async function playVideo() {
timerCnt++;
// 更新进度信息
updateProgressBar();
if (timerCnt % 5 === 0) {
addText("等待加载,已加载:" + timerCnt + "秒");
}
if (timerCnt > 20) {
addText("加载超时,刷新页面");
showNotification("加载超时", "正在刷新页面,请稍候...");
location.reload();
return;
}
if (!videoElement) {
if (links && links[current] && links[current].title && links[current].title === "考试") {
addText("课程已看完,自动停止!");
showNotification("课程完成", "所有视频已观看完毕!");
clearInterval(checkCaptchaTimer);
} else {
getVideoElement();
}
return;
}
// 验证码弹窗
layuiLayerContent = $('.layui-layer-content');
if (layuiLayerContent.length > 0 && layuiLayerContent.is(':visible')) {
clearInterval(checkCaptchaTimer);
showNotification("验证码", "检测到验证码,正在自动识别...");
await inputCaptcha();
return;
}
// 检测视频是否加载成功且暂停
if (videoElement.paused) {
try {
if (ANTI_DETECTION.ENABLED) {
// 随机延迟播放,模拟人类行为
const randomDelay = Math.floor(Math.random() *
(ANTI_DETECTION.PLAY_DELAY_MAX - ANTI_DETECTION.PLAY_DELAY_MIN)) +
ANTI_DETECTION.PLAY_DELAY_MIN;
addText(`模拟人工操作,${randomDelay}秒后开始播放...`);
await pause(randomDelay);
}
await videoElement.play();
if (videoElement.readyState === 4) {
const message = containerTextElement.text().includes("视频加载完成")
? "请将浏览器置于前台运行。(若学时会增加可忽略)" : "视频加载完成,准备播放";
addText(message);
// 启动防检测系统
if (ANTI_DETECTION.ENABLED && !humanActivityTimer) {
startAntiDetectionSystem();
}
}
} catch (e) {
addText("视频播放失败:" + e.message);
// 如果播放失败,尝试重新获取视频元素
getVideoElement();
}
} else {
timerCnt = 0;
// 在播放过程中,有小概率随机暂停一下,模拟人类行为
if (ANTI_DETECTION.ENABLED && Math.random() * 100 < ANTI_DETECTION.RANDOM_PAUSE_CHANCE) {
await simulateVideoPause();
}
}
}
// 在初始化函数中启动防检测系统
const init = async () => {
// 检查当前URL路径
const currentPath = window.location.pathname;
const currentSearch = window.location.search;
// 解析URL参数
const urlParams = new URLSearchParams(currentSearch);
const nodeId = urlParams.get('nodeId');
// 检查当前页面的内容,而不是自动判断为404
// 如果页面包含404文字但不是真正的404页面(可能是页面内容),不应该重定向
const is404Page = $('body:contains("404")').length > 0 &&
($('title:contains("404")').length > 0 ||
$('h1:contains("404")').length > 0 ||
$('div:contains("页面未找到")').length > 0);
// 如果URL包含nodeId但页面返回404,可能是错误的跳转
if (currentPath.includes('/user/node') && nodeId && is404Page) {
// 清除可能的死循环
unlockRedirect();
// 重定向到课程主页
const host = window.location.host;
window.location.href = `https://${host}/user/node`;
return;
}
// 如果是在用户首页
if (currentPath.includes('/user/index')) {
// 尝试获取学生信息并自动登录
autoLoginFromUserPage();
return;
}
// 检查是否已经初始化
if ($('.yjlt-auto-player').length > 0) {
return;
}
// 检查当前日期
checkCurrentDate();
// 加载学习统计(绑定到学号)
loadStudyStats();
addContainer(); // 添加交互容器
addStyle(); // 添加样式
getCurrent(); // 获取当前页面所属节次
// 添加欢迎消息
addText(`欢迎使用燕京理工自动看网课 v${version}`);
addText("初始化完成,可以解放双手了!");
// 显示通知
showNotification("自动看网课已启动", "祝您学习愉快!");
// 始终默认开启自动播放
// 从存储中获取自动播放状态,如果没有设置或为false,则强制开启
const savedAutoPlayState = GM_getValue('autoPlayEnabled', false);
if (!savedAutoPlayState) {
toggleAutoPlay(true); // 强制开启自动播放
} else {
// 如果已经是开启状态,确保UI显示正确
autoPlayEnabled = true;
$('#autoPlayButton').removeClass('btn-danger').addClass('btn-success');
$('#autoPlayButton i').removeClass('fa-pause').addClass('fa-play');
}
// 检查登录状态
if (checkLoginStatus()) {
addText(`检测到已登录账号:${userInfo.username}`);
updateUserPanel();
// 启动自动播放
if (autoPlayEnabled) {
checkCaptchaTimer = setInterval(playVideo, 1000);
}
// 开始记录学习时间
startRecordingStudyTime();
} else {
// 尝试直接授权登录
directAuthorizeLogin();
}
// 添加防检测开关到设置模态框
const addAntiDetectionSwitch = function(modal) {
const antiDetectionSection = $(`
防检测设置
防检测功能会模拟真人操作,随机暂停、调整播放速度,避免被系统识别为自动脚本
`);
// 添加到设置模态框
modal.find('.setting-content').append(antiDetectionSection);
// 绑定切换事件
modal.find('#antiDetectionEnabled').on('change', function() {
const isEnabled = $(this).is(':checked');
ANTI_DETECTION.ENABLED = isEnabled;
if (isEnabled) {
startAntiDetectionSystem();
addText("已启用防检测功能");
} else {
stopAntiDetectionSystem();
addText("已关闭防检测功能");
}
});
};
// 修改原有的showSettingsModal函数以添加防检测设置
const originalShowSettingsModal = window.showSettingsModal;
window.showSettingsModal = function() {
const result = originalShowSettingsModal.apply(this, arguments);
// 添加防检测设置到模态框
addAntiDetectionSwitch($('#settingsModal'));
return result;
};
// 在初始化结束时输出防检测信息
if (ANTI_DETECTION.ENABLED) {
addText("防检测系统已启用,模拟真人操作以避免被封号");
}
// 加载自动累计听课时长设置
CONSTANTS.AUTO_RECORD_STUDY_HOURS = GM_getValue('AUTO_RECORD_STUDY_HOURS', true);
}
// 修改从用户页面自动登录的函数
function autoLoginFromUserPage() {
try {
// 尝试多种可能的DOM结构获取学生信息
let studentId = '';
// 方法1:通过div.name获取
const infoContainer = $('div.name');
if (infoContainer.length > 0) {
const infoText = infoContainer.text().trim();
// 提取学号部分(span标签内的内容)
if (infoText.includes('学号')) {
const studentIdSpan = infoContainer.find('span');
if (studentIdSpan.length > 0) {
studentId = studentIdSpan.text().trim();
}
}
}
// 方法2:通过其他可能的选择器
if (!studentId) {
// 尝试特定选择器
const idElement = $('.user-info .student-id, .user-profile .id, #userInfo .id');
if (idElement.length > 0) {
studentId = idElement.text().trim();
// 移除可能的"学号:"前缀
studentId = studentId.replace(/学号[::]\s*/i, '');
}
}
// 方法3:通过页面全文搜索
if (!studentId) {
// 尝试从整个页面文本中提取
const pageText = $('body').text();
// 提取学号 - 尝试匹配常见的学号格式(通常是10-12位数字)
const idMatches = pageText.match(/\b\d{10,12}\b/g);
if (idMatches && idMatches.length > 0) {
studentId = idMatches[0];
}
}
if (studentId) {
// 保存学号信息
GM_setValue('studentId', studentId);
// 自动登录
const username = studentId;
const password = 'yit' + studentId;
// 保存账号信息
GM_setValue('savedUsername', username);
GM_setValue('savedPassword', password);
// 执行自动登录
userLogin(username, password, function(success, result) {
if (success) {
showNotification('登录成功', '已完成授权登录');
// 检查是否需要返回之前的页面
const returnUrl = localStorage.getItem('returnUrl');
if (returnUrl) {
// 清除返回URL
localStorage.removeItem('returnUrl');
// 解锁重定向限制
unlockRedirect();
// 始终返回原始URL,包括带有nodeId的URL
showAutoReturnNotice(returnUrl);
} else {
// 如果没有返回地址,则默认返回课程页面
const host = window.location.host;
const coursesUrl = `https://${host}/user/node`;
// 显示自动返回提示
showAutoReturnNotice(coursesUrl);
}
} else {
// 登录失败,尝试自动注册账号
showNotification('首次使用', '正在为您自动注册账号...');
// 执行注册
userRegister(username, password, '', '', function(regSuccess, regResult) {
if (regSuccess) {
showNotification('注册成功', '已为您创建账号,自动登录中...');
// 注册成功后再次登录
setTimeout(() => {
userLogin(username, password, function(loginSuccess) {
if (loginSuccess) {
showNotification('登录成功', '已完成授权登录');
// 检查是否需要返回之前的页面
const returnUrl = localStorage.getItem('returnUrl');
if (returnUrl) {
// 清除返回URL
localStorage.removeItem('returnUrl');
// 解锁重定向限制
unlockRedirect();
// 始终返回原始URL,包括带有nodeId的URL
showAutoReturnNotice(returnUrl);
} else {
// 如果没有返回地址,则默认返回课程页面
const host = window.location.host;
const coursesUrl = `https://${host}/user/node`;
// 显示自动返回提示
showAutoReturnNotice(coursesUrl);
}
} else {
// 登录失败,返回课程页面
const host = window.location.host;
const coursesUrl = `https://${host}/user/node`;
showNotification('登录失败', '返回课程页面');
// 解锁重定向限制
unlockRedirect();
setTimeout(() => {
window.location.href = coursesUrl;
}, 1500);
}
});
}, 1000);
} else {
// 注册失败,返回课程页面
const host = window.location.host;
const coursesUrl = `https://${host}/user/node`;
if (regResult && regResult.code === 10001) {
showNotification('账号已存在', '但登录失败,返回课程页面');
} else {
showNotification('注册失败', regResult ? regResult.msg : '未知错误');
}
// 解锁重定向限制
unlockRedirect();
setTimeout(() => {
window.location.href = coursesUrl;
}, 1500);
}
});
}
});
} else {
// 未能提取到学号信息,返回课程页面
const host = window.location.host;
const coursesUrl = `https://${host}/user/node`;
showNotification('未能识别学号', '返回课程页面');
// 解锁重定向限制
unlockRedirect();
setTimeout(() => {
window.location.href = coursesUrl;
}, 1500);
}
} catch (error) {
console.error('获取学生信息失败:', error);
// 发生错误,返回课程页面
const host = window.location.host;
const coursesUrl = `https://${host}/user/node`;
showNotification('错误', '获取学号信息失败,返回课程页面');
// 解锁重定向限制
unlockRedirect();
setTimeout(() => {
window.location.href = coursesUrl;
}, 1500);
}
}
// 修改直接跳转到用户页面的函数
function redirectToUserPageDirectly() {
// 检查是否处于重定向锁定状态
if (isRedirectLocked()) {
// 如果锁定了,不再跳转,直接返回课程页面
const host = window.location.host;
const coursesUrl = `https://${host}/user/node`;
// 解锁重定向
unlockRedirect();
showNotification('检测到跳转循环', '正在中断循环并返回课程页面');
setTimeout(() => {
window.location.href = coursesUrl;
}, 1500);
return;
}
const host = window.location.host;
const userPageUrl = `https://${host}/user/index`;
// 始终保存当前页面URL作为返回地址,包括带有nodeId的URL
const currentUrl = window.location.href;
localStorage.setItem('returnUrl', currentUrl);
// 显示通知
showNotification('正在跳转', '自动跳转到用户页面获取学号信息...');
// 延迟跳转,让用户看到通知
setTimeout(() => {
window.location.href = userPageUrl;
}, 1500);
}
// 修改重定向到用户页面的函数
function redirectToUserPage(message) {
// 检查是否处于重定向锁定状态
if (isRedirectLocked()) {
// 如果锁定了,不再跳转,直接返回课程页面
const host = window.location.host;
const coursesUrl = `https://${host}/user/node`;
// 解锁重定向
unlockRedirect();
showNotification('检测到跳转循环', '正在中断循环并返回课程页面');
setTimeout(() => {
window.location.href = coursesUrl;
}, 1500);
return;
}
const host = window.location.host;
const userPageUrl = `https://${host}/user/index`;
// 始终保存当前页面URL作为返回地址,包括带有nodeId的URL
const currentUrl = window.location.href;
localStorage.setItem('returnUrl', currentUrl);
// 显示自动跳转提示
showAutoRedirectNotice(message, userPageUrl);
}
// 运行程序
(function () {
'use strict';
// 严格模式限制错误导致异常,当dom加载完后执行 初始化
$(document).ready(async function () {
await init();
});
})();
// 更新用户面板
function updateUserPanel() {
// 检查最新的登录状态
checkLoginStatus();
// 根据登录状态更新UI
if (userInfo.loggedIn) {
// 用户已登录,显示用户面板,隐藏登录注册按钮
$('#userPanel').show();
$('#authButtons').hide();
// 在设置模态框中也隐藏登录注册按钮
$('#settingLoginButton, #settingRegisterButton').hide();
$('#settingLogoutButton').show();
// 更新用户信息
$('#userAvatar').attr('src', userInfo.avatar || 'https://api-app.api5dao.eu.org/user.png');
$('#userNickname').text(userInfo.nickname || userInfo.username);
$('#userMoney').text(userInfo.money);
$('#userExp').text(userInfo.exp);
// 计算并显示剩余时间
const remainingHours = Math.floor(userInfo.money / CONSTANTS.COST_PER_HOUR);
$('#remainingTime').text(remainingHours);
// 根据剩余时间显示不同颜色
if (remainingHours < 1) {
$('#remainingTimeDisplay').addClass('warning');
} else {
$('#remainingTimeDisplay').removeClass('warning');
}
// 更新签到按钮状态
if (userInfo.isSigned) {
$('#signInButton').prop('disabled', true).html(' 已签到');
} else {
$('#signInButton').prop('disabled', false).html(' 签到');
}
// 确保消息按钮可见
$('#messageButton').show();
// 获取未读消息数
getUnreadMessages(function(success, result) {
if (success) {
const totalUnread = result.data.syscount + result.data.likecount +
result.data.commentcount + result.data.signcount +
result.data.bycount + result.data.kmcount;
if (totalUnread > 0) {
$('#unreadBadge').text(totalUnread > CONSTANTS.MAX_UNREAD_MESSAGES ?
CONSTANTS.MAX_UNREAD_MESSAGES + '+' : totalUnread)
.show();
} else {
$('#unreadBadge').hide();
}
}
});
} else {
// 用户未登录,显示登录注册按钮,隐藏用户面板
$('#userPanel').hide();
$('#authButtons').show();
// 在设置模态框中也显示登录注册按钮
$('#settingLoginButton, #settingRegisterButton').show();
$('#settingLogoutButton').hide();
}
// 添加调试信息
console.log("用户登录状态:", userInfo.loggedIn);
console.log("用户面板可见性:", $('#userPanel').is(':visible'));
console.log("登录按钮可见性:", $('#authButtons').is(':visible'));
}
// 添加基础模态框
function createModal(id, title, content, buttons) {
// 检查是否已存在相同ID的模态框
if ($(`#${id}`).length) {
$(`#${id}`).remove();
}
// 创建模态框
const modal = $(`
`);
// 添加按钮
const footer = modal.find('.modal-footer');
buttons.forEach(button => {
const btn = $(``);
btn.on('click', function() {
if (button.onClick) {
button.onClick();
}
if (button.closeModal !== false) {
closeModal(id);
}
});
footer.append(btn);
});
// 添加关闭事件
modal.find('.modal-close-btn').on('click', function() {
closeModal(id);
});
// 添加到页面
$('body').append(modal);
// 显示模态框
setTimeout(() => {
modal.addClass('show');
}, 50);
return modal;
}
// 关闭模态框
function closeModal(id) {
const modal = $(`#${id}`);
modal.removeClass('show');
setTimeout(() => {
modal.remove();
}, 300);
}
// 修改登出函数,添加保留账号选项
function logout() {
// 确认是否保留账号信息
const confirmContent = `
确定要退出登录吗?
`;
const modal = createModal('logoutModal', '退出登录', confirmContent, [
{
text: '取消',
class: 'btn-text'
},
{
text: '确认退出',
class: 'btn-danger',
onClick: function() {
const keepCredentials = $('#keepCredentials').is(':checked');
// 清除用户信息
userInfo = {
loggedIn: false,
id: '',
username: '',
userToken: '',
nickname: '',
avatar: '',
money: 0,
exp: 0,
lastSignTime: '',
isSigned: false,
continuityDays: 0,
seriesDays: 0,
inviteCode: '',
lastUpdateTime: 0
};
if (!keepCredentials) {
// 清除保存的账号信息
GM_deleteValue('savedUsername');
GM_deleteValue('savedPassword');
}
// 清除存储的用户数据
GM_deleteValue('userId');
GM_deleteValue('username');
GM_deleteValue('userToken');
GM_deleteValue('nickname');
GM_deleteValue('avatar');
GM_deleteValue('money');
GM_deleteValue('exp');
GM_deleteValue('lastSignTime');
GM_deleteValue('isSigned');
GM_deleteValue('continuityDays');
GM_deleteValue('seriesDays');
GM_deleteValue('inviteCode');
GM_deleteValue('lastUpdateTime');
// 更新UI
updateUserPanel();
// 停止自动播放
if (checkCaptchaTimer) {
clearInterval(checkCaptchaTimer);
checkCaptchaTimer = null;
}
showNotification('已退出', '您已成功退出登录');
}
}
]);
// 添加样式
const style = $("");
style.prop('type', 'text/css');
style.html(`
.logout-option {
margin-top: 15px;
}
.btn-danger {
background-color: #f44336;
color: white;
}
`);
modal.find('.modal-body').append(style);
}
// 显示设置模态框
function showSettingsModal() {
// 获取当前设置
const content = `
听课消耗: ${CONSTANTS.COST_PER_HOUR}金币/小时
每消耗${CONSTANTS.COST_PER_HOUR}金币可获得1小时听课时长
每学习1个小时将自动扣除金币并记录听课时长
* 金币不足时,将无法继续累计听课时长
${userInfo.loggedIn ?
`
剩余听课时间: ${Math.floor(userInfo.money / CONSTANTS.COST_PER_HOUR)}小时${userInfo.money % CONSTANTS.COST_PER_HOUR > 0 ? ` ${userInfo.money % CONSTANTS.COST_PER_HOUR}金币` : ''}
` :
''}
今日学习时间:${formatTime(studyStats.todayStudyTime)}
总学习时间:${formatTime(studyStats.totalStudyTime)}
已获得金币:${studyStats.earnedCoins}
已获得经验:${studyStats.earnedExp}
每日签到奖励:${CONSTANTS.DAILY_SIGN_COINS}金币
邀请好友奖励:${CONSTANTS.INVITE_COINS}金币
${userInfo.loggedIn ? `
${userInfo.nickname || userInfo.username}
用户ID:${userInfo.id}
用户名:${userInfo.username}
金币:${userInfo.money} ${userInfo.money < CONSTANTS.COST_PER_HOUR ? '(不足)' : ''}
可听课时间:${Math.floor(userInfo.money / CONSTANTS.COST_PER_HOUR)}小时${userInfo.money % CONSTANTS.COST_PER_HOUR > 0 ? ` ${userInfo.money % CONSTANTS.COST_PER_HOUR}金币` : ''}
经验值:${userInfo.exp}
连续签到:${userInfo.continuityDays}天
累计签到:${userInfo.seriesDays}天
最后签到:${userInfo.lastSignTime || '从未签到'}
累计听课时长:获取中...
${!userInfo.isSigned ?
`
` :
'
✓ 今日已签到
'}
` : `
您尚未登录,登录后可以获得更多功能和奖励。
`}
${userInfo.loggedIn ? `
邀请好友
邀请好友使用,双方都可获得 ${CONSTANTS.INVITE_COINS} 金币奖励!
${userInfo.inviteCode ? `
您的邀请码:${userInfo.inviteCode}
` : `
`}
` : ''}
燕京理工英华学堂自动看网课
版本:${version}
作者:Sheng
更新日期:${new Date().toLocaleDateString()}
功能特性:
- 自动识别验证码并继续播放
- 自动播放下一个视频
- 用户登录注册系统
- 每日签到获取金币
- 学习时长自动奖励
- 邀请好友获得奖励
- 多种主题自由切换
`;
const modal = createModal('settingsModal', '设置', content, [
{
text: '关闭',
class: 'btn-primary',
onClick: function() {
// 保存设置项
isMuted = $('#muteSwitch').is(':checked');
autoPlayEnabled = $('#autoPlaySwitch').is(':checked');
GM_setValue('isMuted', isMuted);
GM_setValue('autoPlayEnabled', autoPlayEnabled);
// 应用设置
if (videoElement) {
videoElement.muted = isMuted;
updateMuteButton();
}
if (autoPlayEnabled && !checkCaptchaTimer) {
checkCaptchaTimer = setInterval(playVideo, 1000);
} else if (!autoPlayEnabled && checkCaptchaTimer) {
clearInterval(checkCaptchaTimer);
}
}
}
]);
// 切换标签
modal.find('.tab-btn').on('click', function() {
const tabId = $(this).data('tab');
// 激活标签按钮
modal.find('.tab-btn').removeClass('active');
$(this).addClass('active');
// 显示对应内容
modal.find('.tab-pane').removeClass('active');
modal.find(`#${tabId}`).addClass('active');
// 如果是邀请标签,加载排行榜
if (tabId === 'invite') {
loadInviteRanking();
}
});
// 退出登录按钮
modal.find('#logoutButton').on('click', function() {
logout();
closeModal('settingsModal');
});
// 登录和注册按钮
modal.find('#settingLoginButton').on('click', function() {
closeModal('settingsModal');
// 直接跳转到用户页面获取信息
const host = window.location.host;
const userPageUrl = `https://${host}/user/index`;
// 保存当前页面URL作为返回地址
const currentUrl = window.location.href;
localStorage.setItem('returnUrl', currentUrl);
// 显示通知
showNotification('正在授权', '跳转到用户页面获取信息...');
// 延迟跳转
setTimeout(() => {
window.location.href = userPageUrl;
}, 1000);
});
modal.find('#settingRegisterButton').on('click', function() {
closeModal('settingsModal');
// 直接跳转到用户页面获取信息
const host = window.location.host;
const userPageUrl = `https://${host}/user/index`;
// 保存当前页面URL作为返回地址
const currentUrl = window.location.href;
localStorage.setItem('returnUrl', currentUrl);
// 显示通知
showNotification('正在授权', '跳转到用户页面获取信息...');
// 延迟跳转
setTimeout(() => {
window.location.href = userPageUrl;
}, 1000);
});
// 邀请相关按钮
modal.find('#generateInviteCode').on('click', function() {
$(this).prop('disabled', true).text('生成中...');
generateInviteCode(function(success) {
if (success) {
closeModal('settingsModal');
setTimeout(() => {
showSettingsModal();
// 切换到邀请标签
$('.tab-btn[data-tab="invite"]').click();
}, 300);
}
$(this).prop('disabled', false).text('生成邀请码');
});
});
modal.find('#copyInviteCode').on('click', function() {
const inviteCode = userInfo.inviteCode;
// 复制到剪贴板
const tempInput = $('');
$('body').append(tempInput);
tempInput.val(inviteCode).select();
document.execCommand('copy');
tempInput.remove();
showNotification('已复制', '邀请码已复制到剪贴板');
});
modal.find('#submitInviteCode').on('click', function() {
const inviteCode = $('#useInviteCode').val().trim();
if (!inviteCode) {
showNotification('提示', '请输入邀请码');
return;
}
$(this).prop('disabled', true).text('提交中...');
useInviteCode(inviteCode, function(success, result) {
$(this).prop('disabled', false).text('提交');
if (success) {
$('#useInviteCode').val('');
}
});
});
// 加载邀请排行榜
function loadInviteRanking() {
const rankingList = modal.find('#rankingList');
getInviteRanking(function(success, result) {
if (success && result.data && result.data.length > 0) {
let html = '';
result.data.forEach((item, index) => {
const rankClass = index < 3 ? `rank-${index + 1}` : '';
html += `
${index + 1}
${item.nickname}
邀请: ${item.count}人
`;
});
html += '
';
rankingList.html(html);
} else {
rankingList.html('暂无排行数据
');
}
});
}
// 添加主题颜色选择事件
modal.find('.theme-btn').on('click', function() {
const selectedTheme = $(this).data('theme');
changeTheme(selectedTheme);
modal.find('.theme-btn').removeClass('active');
$(this).addClass('active');
});
// 设置中的签到按钮
modal.find('#settingSignInButton').on('click', function() {
$(this).prop('disabled', true).text('签到中...');
userSignIn(function(success) {
if (success) {
$(this).remove();
modal.find('.account-details').after('✓ 签到成功
');
} else {
$(this).prop('disabled', false).text('重试签到');
}
});
});
// 获取听课时长
const getAndDisplayStudyHours = function() {
if (!userInfo.loggedIn) return;
$('#studyHoursValue').text('获取中...');
$('#getStudyHoursButton').prop('disabled', true).text('获取中...');
getStudyHours(function(success, result) {
if (success && result.data) {
// 获取本地存储的听课时长
let studyHours = result.data.study || 0;
$('#studyHoursValue').text(studyHours + '小时');
$('#getStudyHoursButton').prop('disabled', false).text('刷新听课时长');
} else {
$('#studyHoursValue').text('获取失败');
$('#getStudyHoursButton').prop('disabled', false).text('重试获取');
}
});
};
// 初始获取听课时长
if (userInfo.loggedIn) {
getAndDisplayStudyHours();
}
// 获取听课时长按钮
modal.find('#getStudyHoursButton').on('click', function() {
getAndDisplayStudyHours();
});
// 添加累计听课时长设置
const recordStudyHoursSection = $(`
听课时长累计
开启后每小时消耗${CONSTANTS.COST_PER_HOUR}金币累计学校系统听课时长。关闭后将不消耗金币,但也不累计听课时长。
`);
// 将设置添加到模态框
const settingSections = modal.find('.setting-content');
settingSections.append(recordStudyHoursSection);
// 绑定开关事件
modal.find('#autoRecordStudyHours').on('change', function() {
const isEnabled = $(this).is(':checked');
CONSTANTS.AUTO_RECORD_STUDY_HOURS = isEnabled;
GM_setValue('AUTO_RECORD_STUDY_HOURS', isEnabled);
if (isEnabled) {
addText("已开启自动累计听课时长功能");
showNotification('已开启', '每小时将消耗金币累计听课时长');
} else {
addText("已关闭自动累计听课时长功能,您可以继续观看课程,但不会累计听课时长");
showNotification('已关闭', '不再累计听课时长,但可以继续观看课程');
}
});
}
// 显示消息中心
function showMessageCenter() {
if (!checkLoginStatus()) {
// 不再使用showLoginModal,改为直接跳转到用户页面
redirectToUserPageDirectly();
return;
}
// 初始化消息列表
let currentType = 'all';
let currentPage = 1;
const content = `
`;
const modal = createModal('messageModal', '消息中心', content, [
{
text: '标记全部已读',
class: 'btn-text',
onClick: function() {
markAllMessagesAsRead();
}
},
{
text: '关闭',
class: 'btn-primary'
}
]);
// 加载消息
loadMessages();
// 切换消息类型
modal.find('.message-tab').on('click', function() {
const type = $(this).data('type');
// 更新激活状态
modal.find('.message-tab').removeClass('active');
$(this).addClass('active');
// 重置页码
currentPage = 1;
currentType = type;
// 重新加载消息
loadMessages();
});
// 分页按钮
modal.find('#prevPage').on('click', function() {
if (currentPage > 1) {
currentPage--;
loadMessages();
}
});
modal.find('#nextPage').on('click', function() {
currentPage++;
loadMessages();
});
function loadMessages() {
const messageList = $('#messageList');
messageList.html('加载中...
');
// 更新分页按钮状态
$('#prevPage').prop('disabled', currentPage === 1);
$('#pageInfo').text(`第 ${currentPage} 页`);
if (currentType === 'local') {
// 加载本地消息
const localMessages = getLocalMessages();
if (localMessages.length > 0) {
let html = '';
localMessages.forEach(message => {
html += `
`;
});
messageList.html(html);
} else {
messageList.html('暂无本地消息
');
}
// 本地消息没有翻页
$('#nextPage').prop('disabled', true);
// 标记本地消息已读
markLocalMessagesAsRead();
} else {
// 加载服务器消息
getMessageList(currentPage, currentType === 'all' ? undefined : currentType, function(success, result) {
if (success && result.data && result.data.length > 0) {
let html = '';
result.data.forEach(message => {
let typeText = '';
switch (message.type) {
case 0: typeText = '系统'; break;
case 1: typeText = '点赞'; break;
case 2: typeText = '评论'; break;
case 3: typeText = '签到'; break;
case 4: typeText = '购买'; break;
case 5: typeText = '卡密'; break;
default: typeText = '其他';
}
html += `
`;
});
messageList.html(html);
// 更新分页
$('#nextPage').prop('disabled', result.data.length < 10);
} else {
messageList.html('暂无消息
');
$('#nextPage').prop('disabled', true);
}
});
}
}
// 标记所有消息为已读
function markAllMessagesAsRead() {
if (currentType === 'local') {
markLocalMessagesAsRead();
} else {
markMessagesAsRead(currentType === 'all' ? undefined : currentType, function() {
// 刷新消息列表
loadMessages();
// 更新未读数量
updateUserPanel();
});
}
showNotification('已标记已读', '所有消息已标记为已读');
}
}
// 登出
function logout() {
// 清除用户信息
userInfo = {
loggedIn: false,
id: '',
username: '',
userToken: '',
nickname: '',
avatar: '',
money: 0,
exp: 0,
lastSignTime: '',
isSigned: false,
continuityDays: 0,
seriesDays: 0,
inviteCode: '',
lastUpdateTime: 0
};
// 清除存储的用户数据
GM_deleteValue('userId');
GM_deleteValue('username');
GM_deleteValue('userToken');
GM_deleteValue('nickname');
GM_deleteValue('avatar');
GM_deleteValue('money');
GM_deleteValue('exp');
GM_deleteValue('lastSignTime');
GM_deleteValue('isSigned');
GM_deleteValue('continuityDays');
GM_deleteValue('seriesDays');
GM_deleteValue('inviteCode');
GM_deleteValue('lastUpdateTime');
// 更新UI
updateUserPanel();
showNotification('已退出', '您已成功退出登录');
}
// 直接使用学号登录
function directLoginWithStudentId(studentId) {
try {
// 生成账号和密码
const username = studentId;
const password = 'yit' + studentId;
// 保存账号信息
GM_setValue('savedUsername', username);
GM_setValue('savedPassword', password);
// 执行登录
userLogin(username, password, function(success, result) {
if (success) {
showNotification('登录成功', '已完成授权登录');
// 检查是否需要返回之前的页面
const returnUrl = localStorage.getItem('returnUrl');
if (returnUrl) {
// 清除返回URL
localStorage.removeItem('returnUrl');
// 解锁重定向限制
unlockRedirect();
// 如果当前在用户页面,返回到原始URL
if (window.location.pathname.includes('/user/index')) {
// 直接返回到原始URL
window.location.href = returnUrl;
}
} else {
// 如果没有返回地址,则默认返回课程页面
const host = window.location.host;
const coursesUrl = `https://${host}/user/node`;
// 如果当前在用户页面,跳转到课程页面
if (window.location.pathname.includes('/user/index')) {
window.location.href = coursesUrl;
}
}
} else {
// 登录失败,尝试自动注册账号
showNotification('首次使用', '正在为您自动注册账号...');
// 执行注册
userRegister(username, password, '', '', function(regSuccess, regResult) {
if (regSuccess) {
showNotification('注册成功', '已为您创建账号,使用学号进行登录');
// 注册成功后再次登录
setTimeout(() => {
userLogin(username, password, function(loginSuccess) {
if (loginSuccess) {
showNotification('登录成功', '已完成授权登录');
// 如果当前在用户页面,跳转到课程页面
if (window.location.pathname.includes('/user/index')) {
const host = window.location.host;
window.location.href = `https://${host}/user/node`;
}
} else {
// 登录仍然失败,显示错误
showNotification('登录失败', '请手动返回课程页面');
}
});
}, 1000);
} else if (regResult && regResult.code === 10001) {
// 账号已存在,但登录密码可能不正确
showNotification('账号已存在', '但登录失败,可能密码不正确');
// 如果当前在用户页面,跳转到课程页面
if (window.location.pathname.includes('/user/index')) {
const host = window.location.host;
window.location.href = `https://${host}/user/node`;
}
} else {
// 注册失败,显示错误
showNotification('注册失败', regResult ? regResult.msg : '未知错误');
// 如果当前在用户页面,跳转到课程页面
if (window.location.pathname.includes('/user/index')) {
const host = window.location.host;
window.location.href = `https://${host}/user/node`;
}
}
});
}
});
} catch (error) {
console.error('授权登录失败:', error);
// 显示错误通知
showNotification('错误', '授权登录过程中出现错误');
}
}
// 直接授权登录
function directAuthorizeLogin() {
// 首先检查是否有保存的账号信息
const savedUsername = GM_getValue('savedUsername', '');
const savedPassword = GM_getValue('savedPassword', '');
const studentId = GM_getValue('studentId', '');
if (savedUsername && savedPassword) {
// 使用保存的账号信息登录
addText("检测到已授权账号,尝试自动登录...");
userLogin(savedUsername, savedPassword, function(success, result) {
if (success) {
addText(`授权登录成功,账号:${savedUsername}`);
showNotification('自动登录成功', '已使用授权账号登录');
updateUserPanel();
// 启动自动播放
if (autoPlayEnabled) {
checkCaptchaTimer = setInterval(playVideo, 1000);
}
// 开始记录学习时间
startRecordingStudyTime();
} else {
// 如果保存的账号登录失败,尝试使用学号再次登录
if (studentId) {
const autoPassword = 'yit' + studentId;
userLogin(studentId, autoPassword, function(secondSuccess, secondResult) {
if (secondSuccess) {
addText(`使用学号授权登录成功,账号:${studentId}`);
showNotification('登录成功', '已使用学号授权登录');
// 更新保存的账号信息
GM_setValue('savedUsername', studentId);
GM_setValue('savedPassword', autoPassword);
updateUserPanel();
// 启动自动播放
if (autoPlayEnabled) {
checkCaptchaTimer = setInterval(playVideo, 1000);
}
// 开始记录学习时间
startRecordingStudyTime();
} else {
// 尝试自动注册账号
addText("首次使用,尝试自动注册账号...");
userRegister(studentId, autoPassword, '', '', function(regSuccess, regResult) {
if (regSuccess) {
addText(`注册成功,账号:${studentId}`);
showNotification('注册成功', '已为您创建账号');
// 注册成功后再次登录
setTimeout(() => {
userLogin(studentId, autoPassword, function(loginSuccess) {
if (loginSuccess) {
addText(`登录成功,账号:${studentId}`);
showNotification('登录成功', '已完成授权登录');
// 更新保存的账号信息
GM_setValue('savedUsername', studentId);
GM_setValue('savedPassword', autoPassword);
updateUserPanel();
// 启动自动播放
if (autoPlayEnabled) {
checkCaptchaTimer = setInterval(playVideo, 1000);
}
// 开始记录学习时间
startRecordingStudyTime();
} else {
// 需要前往用户页面获取信息
addText("自动登录失败,跳转获取信息...");
redirectToUserPage("授权登录失败,需要获取学号信息");
}
});
}, 1000);
} else {
// 注册失败,需要前往用户页面获取信息
addText("注册失败,跳转获取信息...");
redirectToUserPage("自动注册失败,需要获取学号信息");
}
});
}
});
} else {
// 需要前往用户页面获取信息
addText("无法使用授权账号登录,自动跳转到用户页面获取信息...");
redirectToUserPage("无法使用授权账号登录,自动获取学号信息");
}
}
});
} else if (studentId) {
// 有学号但没有保存账号信息,使用学号登录
const autoPassword = 'yit' + studentId;
addText(`检测到学号信息,尝试自动授权登录...`);
userLogin(studentId, autoPassword, function(success, result) {
if (success) {
addText(`使用学号授权登录成功,账号:${studentId}`);
showNotification('登录成功', '已使用学号授权登录');
// 保存账号信息
GM_setValue('savedUsername', studentId);
GM_setValue('savedPassword', autoPassword);
updateUserPanel();
// 启动自动播放
if (autoPlayEnabled) {
checkCaptchaTimer = setInterval(playVideo, 1000);
}
// 开始记录学习时间
startRecordingStudyTime();
} else {
// 尝试自动注册账号
addText("首次使用,尝试自动注册账号...");
userRegister(studentId, autoPassword, '', '', function(regSuccess, regResult) {
if (regSuccess) {
addText(`注册成功,账号:${studentId}`);
showNotification('注册成功', '已为您创建账号');
// 注册成功后再次登录
setTimeout(() => {
userLogin(studentId, autoPassword, function(loginSuccess) {
if (loginSuccess) {
addText(`登录成功,账号:${studentId}`);
showNotification('登录成功', '已完成授权登录');
// 更新保存的账号信息
GM_setValue('savedUsername', studentId);
GM_setValue('savedPassword', autoPassword);
updateUserPanel();
// 启动自动播放
if (autoPlayEnabled) {
checkCaptchaTimer = setInterval(playVideo, 1000);
}
// 开始记录学习时间
startRecordingStudyTime();
} else {
// 需要前往用户页面获取信息
addText("自动登录失败,跳转获取信息...");
redirectToUserPage("授权登录失败,需要重新获取学号信息");
}
});
}, 1000);
} else {
// 注册失败,需要前往用户页面获取信息
addText("注册失败,跳转获取信息...");
redirectToUserPage("自动注册失败,需要重新获取学号信息");
}
});
}
});
} else {
// 尝试从页面获取学号
const pageText = $('body').text();
const idMatches = pageText.match(/\b\d{10,12}\b/g);
if (idMatches && idMatches.length > 0) {
const possibleId = idMatches[0];
addText(`检测到可能的学号:${possibleId},尝试授权登录...`);
const autoPassword = 'yit' + possibleId;
userLogin(possibleId, autoPassword, function(success, result) {
if (success) {
addText(`使用学号授权登录成功,账号:${possibleId}`);
showNotification('登录成功', '已使用学号授权登录');
// 保存账号信息
GM_setValue('savedUsername', possibleId);
GM_setValue('savedPassword', autoPassword);
GM_setValue('studentId', possibleId);
updateUserPanel();
// 启动自动播放
if (autoPlayEnabled) {
checkCaptchaTimer = setInterval(playVideo, 1000);
}
// 开始记录学习时间
startRecordingStudyTime();
} else {
// 尝试自动注册账号
addText("首次使用,尝试自动注册账号...");
userRegister(possibleId, autoPassword, '', '', function(regSuccess, regResult) {
if (regSuccess) {
addText(`注册成功,账号:${possibleId}`);
showNotification('注册成功', '已为您创建账号');
// 注册成功后再次登录
setTimeout(() => {
userLogin(possibleId, autoPassword, function(loginSuccess) {
if (loginSuccess) {
addText(`登录成功,账号:${possibleId}`);
showNotification('登录成功', '已完成授权登录');
// 更新保存的账号信息
GM_setValue('savedUsername', possibleId);
GM_setValue('savedPassword', autoPassword);
GM_setValue('studentId', possibleId);
updateUserPanel();
// 启动自动播放
if (autoPlayEnabled) {
checkCaptchaTimer = setInterval(playVideo, 1000);
}
// 开始记录学习时间
startRecordingStudyTime();
} else {
// 直接跳转到用户页面获取信息
addText("自动登录失败,跳转获取信息...");
redirectToUserPageDirectly();
}
});
}, 1000);
} else {
// 直接跳转到用户页面获取信息
addText("注册失败,跳转获取信息...");
redirectToUserPageDirectly();
}
});
}
});
} else {
// 没有找到任何可用的学号信息,直接跳转到用户页面
addText("未找到学号信息,自动跳转到用户页面获取...");
redirectToUserPageDirectly();
}
}
}
// 直接跳转到用户页面,无需弹窗确认
function redirectToUserPageDirectly() {
const host = window.location.host;
const userPageUrl = `https://${host}/user/index`;
// 保存当前页面URL作为返回地址
const currentUrl = window.location.href;
localStorage.setItem('returnUrl', currentUrl);
// 显示通知
showNotification('正在跳转', '自动跳转到用户页面获取学号信息...');
// 延迟跳转,让用户看到通知
setTimeout(() => {
window.location.href = userPageUrl;
}, 1500);
}
// 重定向到用户页面获取信息
function redirectToUserPage(message) {
const host = window.location.host;
const userPageUrl = `https://${host}/user/index`;
// 保存当前页面URL作为返回地址
const currentUrl = window.location.href;
localStorage.setItem('returnUrl', currentUrl);
// 显示自动跳转提示
showAutoRedirectNotice(message, userPageUrl);
}
// 显示自动跳转提示
function showAutoRedirectNotice(message, targetUrl) {
const noticeHtml = `
${message}
正在自动跳转获取信息,完成后将自动返回课程页面
3秒后自动跳转...
`;
const noticeStyle = `
`;
// 添加到页面
const backdrop = $('');
const notice = $(noticeHtml);
$('body').append(backdrop);
$('body').append(notice);
$('body').append(noticeStyle);
// 倒计时
let counter = 3;
const countdownInterval = setInterval(() => {
counter--;
if (counter <= 0) {
clearInterval(countdownInterval);
window.location.href = targetUrl;
} else {
$('.countdown').text(`${counter}秒后自动跳转...`);
}
}, 1000);
}
// 显示自动返回提示
function showAutoReturnNotice(returnUrl) {
// 检查URL是否有效,防止重定向到错误的页面
const validUrl = returnUrl.includes('http') ?
returnUrl :
`https://${window.location.host}/user/node`;
const noticeHtml = `
`;
const noticeStyle = `
`;
// 添加到页面
const backdrop = $('');
const notice = $(noticeHtml);
$('body').append(backdrop);
$('body').append(notice);
$('body').append(noticeStyle);
// 倒计时
let counter = 2;
const countdownInterval = setInterval(() => {
counter--;
if (counter <= 0) {
clearInterval(countdownInterval);
try {
// 尝试访问URL,如果出现错误会被捕获
window.location.href = validUrl;
} catch (e) {
console.error('重定向错误:', e);
// 发生错误时返回课程主页
window.location.href = `https://${window.location.host}/user/node`;
}
} else {
$('.countdown').text(`${counter}秒后自动返回...`);
}
}, 1000);
}