// ==UserScript==
// @name 山东青岛专技-课程助手
// @namespace http://tampermonkey.net/zzzzzzys_山东青岛专技-课程助手
// @version 1.0.2
// @copyright zzzzzzys.All Rights Reserved.
// @description 山东青岛专技-课程助手(https://www.qdjxjy.com.cn/),脚本免费功能有限,可自动静音播放视频,防暂停。更全自动方法,建议升级至客户端请查看文档介绍!支持高倍速+考试
// @author zzzzzzys
// @match https://www.qdjxjy.com.cn/*
// @require https://fastly.jsdelivr.net/npm/crypto-js@4.2.0/crypto-js.min.js
// @resource https://cdn.staticfile.org/limonte-sweetalert2/11.7.1/sweetalert2.min.css
// @require https://fastly.jsdelivr.net/npm/sweetalert2@11.12.2/dist/sweetalert2.all.min.js
// @require https://scriptcat.org/lib/637/1.4.5/ajaxHooker.js#sha256=EGhGTDeet8zLCPnx8+72H15QYRfpTX4MbhyJ4lJZmyg=
// @connect fc-mp-8ba0e2a3-d9c9-45a0-a902-d3bde09f5afd.next.bspapp.com
// @connect mp-8ba0e2a3-d9c9-45a0-a902-d3bde09f5afd.cdn.bspapp.com
// @connect zys-api.zzzzzzys.com
// @connect 1.zzzzzzys.com
// @grant unsafeWindow
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_deleteValue
// @grant GM_xmlhttpRequest
// @grant GM_info
// @grant GM_addStyle
// @run-at document-start
// @antifeature ads 有弹窗广告,介绍完全自动化软件
// @antifeature payment 脚本基础功能使用免费,但需要使用完全自动化软件时,可能收费
// ==/UserScript==
class Runner {
constructor() {
this.runner = null
this.initAjaxHooker()
this.waitForDOMLoaded()
}
initAjaxHooker() {
// ajaxHooker.filter([
// // {type: 'xhr', url: 'www.example.com', method: 'GET', async: true},
// {url: "https://gw.dtdjzx.gov.cn/gwapi/us/api/study/progress2"},
// {url: "https://dywlxy.dtdjzx.gov.cn/gwapi/dywlxynet/api/user/configure"},
// ]);
const self=this
ajaxHooker.hook(request => {
if (request.url.includes('vedioValidQuestions/getQuestions')) {
request.response = res => {
window.QuestionInfo=JSON.parse(res.responseText).data
console.log("QuestionInfo:", window.QuestionInfo)
// res.responseText = '{"action": "0","success": true,"verificationDuration": 0}';
};
} else if (request.url.includes('p/play/config')) {
request.response = res => {
const json = JSON.parse(res.responseText)
console.log("play/config':");
console.log(json);
window.playConfig = json.data
// res.responseText += 'test';
};
} else if (request.url.includes('learning/learnVerify/checkCode')) {
request.abort=true
request.response=res =>{
res.responseText='{"code":0,"msg":null,"data":{"data":"请勿频繁请求","status":9999}}'
}
}else if(request.url.includes('learning/learnVerify')) {
request.abort=true
}
});
console.log("hooker:", ajaxHooker)
}
waitForDOMLoaded() {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => this.run());
} else {
// DOM已经就绪,直接执行
this.run();
}
}
run() {
const url = location.href;
this.runner = new Course("channel-hunau")
}
}
class Course {
constructor(channel = "channel-my") {
this.panel = new AuthWindow({
VIPBtnText: "高级功能-已弃用,请前往软件学习"
})
this.channel = new BroadcastChannel(channel)
this.VIP = false
this.running = false
// ===== 新增:前3个视频自动播放相关属性 =====
this.maxFreeVideos = 3 // 免费版最多学3个
this.playedVideoCount = 0 // 已播放视频计数
this.processedVideos = new Set() // 记录已处理的视频URL,防重复
this.isAutoPlaying = false // 是否正在自动播放流程中
// ============================================
this.init()
}
init() {
this.panel.setOnVerifyCallback(async (data) => {
this.url = await Utils.validateCode(data)
if (this.url) {
this.panel.setTip(Utils.vipText)
this.VIP = true
return true
}
})
this.panel.setOnBegin(() => {
if (!this.running) {
this.running = true
console.log("运行时:", this.VIP)
this.run().then(r => {
this.running = false
})
}
})
this.panel.setOnVIP(async () => {
if (!this.url) {
await this.panel.handleVerify()
}
await this.runVIP()
})
this.loadVIPStatus()
try {
Swal.fire({
title: "提示",
text: Utils.swFireText,
icon: 'info',
timer: 5000,
confirmButtonText: '确定',
timerProgressBar: true,
willClose: () => {
// Utils.showUpgradeAlert()
this.panel.startAutomation()
}
});
} catch (e) {
console.error(e)
this.panel.startAutomation()
}
}
verifyCheck(){
const dialogSelector=".layui-layer1"
const self=this
const checker=setInterval(async () => {
const dom = document.querySelector(dialogSelector)
if (dom) {
console.log("检查到验证码窗口")
clearInterval(checker)
const inputSelector = "#captchaInput"
const btnSelector = ".layui-layer-btn0"
const max = 20
for (let i = 0; i < max; i++) {
try{
const input = dom.querySelector(inputSelector)
const button = dom.querySelector(btnSelector)
input.value=i
await sleep(100)
button.click()
await sleep(100)
}catch(err){
console.error(err)
break
}
}
self.verifyCheck()
}
},5000)
}
loadVIPStatus() {
if (Utils.loadStatus()) {
this.panel.setTip(Utils.vipText)
this.VIP = true
} else {
this.panel.setTip(Utils.baseText)
this.VIP = false
}
console.log("VIP:", this.VIP)
}
async runVIP() {
try {
Utils.showUpgradeAlert()
} catch
(error) {
console.error(error)
Swal.fire({
title: "高级功能执行失败!",
text: "若一直失败,请联系进行售后处理!",
icon: 'error',
confirmButtonText: '确定',
allowOutsideClick: false,
willClose: () => {
console.log(' 用户确认错误,脚本已停止');
}
});
}
}
async autoPlay(){
const video = document.querySelector('video')
if (!video) {
console.log('[自动播放] 未找到视频元素')
return
}
// ===== 新增:静音 + 解除暂停 =====
video.volume = 0
video.muted = true
// 解除暂停限制(覆盖pause方法)
const originalPause = video.pause
video.pause = function() {
console.log('[自动播放] 拦截pause(),保持播放')
return Promise.resolve()
}
// 拦截可能导致暂停的事件
const blockEvents = ['pause', 'waiting', 'suspend', 'stalled']
blockEvents.forEach(eventName => {
video.addEventListener(eventName, function(e) {
if (eventName === 'pause' && !video.ended) {
setTimeout(() => video.play().catch(()=>{}), 100)
}
}, true)
})
// 让页面始终认为可见
Object.defineProperty(document, 'hidden', {
get: function() { return false },
configurable: true
})
Object.defineProperty(document, 'visibilityState', {
get: function() { return 'visible' },
configurable: true
})
// 定时检查恢复播放
const keepAlive = setInterval(() => {
if (video.paused && !video.ended && this.isAutoPlaying) {
video.play().catch(()=>{})
}
}, 2000)
// 视频结束时清理并跳转
video.addEventListener('ended', () => {
clearInterval(keepAlive)
this.playedVideoCount++
console.log(`[自动播放] 第${this.playedVideoCount}个视频播放完毕`)
// 判断是继续下一个还是结束
if (this.playedVideoCount >= this.maxFreeVideos) {
this.showFreeLimitReached() // 弹出限制提示
} else {
this.goNextCatalog() // 继续下一个
}
}, { once: true })
// 开始播放
await video.play()
console.log('[自动播放] 视频开始播放,当前第' + (this.playedVideoCount + 1) + '个')
}
/**
* 找到目录中当前播放项(含 #top_play 的 li),跳转到下一个未完成的条目
*/
/**
* 找到目录中当前播放项,跳转到下一个未完成的条目
* 免费版限制:最多跳转 maxFreeVideos 次
*/
goNextCatalog() {
// ===== 新增:检查是否已达免费上限 =====
if (this.playedVideoCount >= this.maxFreeVideos) {
console.log("[自动播放] 已达免费上限" + this.maxFreeVideos + "个")
this.showFreeLimitReached()
return
}
// ====================================
const items = Array.from(document.querySelectorAll('ul.lis-content li.lis-inside-content'))
if (!items.length) {
console.log("未找到目录列表")
return
}
const currentIndex = items.findIndex(li => li.querySelector('#top_play'))
console.log("当前目录索引:", currentIndex)
const startIndex = currentIndex + 1
for (let i = startIndex; i < items.length; i++) {
const li = items[i]
const btn = li.querySelector('button')
const h2 = li.querySelector('h2')
if (!h2) continue
const btnText = btn ? btn.textContent.trim() : ''
console.log(`目录[${i}] 状态: ${btnText}`)
const onclick = h2.getAttribute('onclick') || ''
const match = onclick.match(/window\.location\.href='([^']+)'/)
if (match) {
const nextUrl = match[1]
const nextTitle = h2.textContent.trim().replace(/\s+/g, ' ')
console.log("跳转下一节:", nextUrl)
Swal.fire({
title: "视频已播放完毕",
html: `即将跳转到下一节:
${nextTitle}`,
icon: 'success',
timer: 3000, // 缩短到3秒,更快跳转
timerProgressBar: true,
confirmButtonText: '立即跳转',
showCancelButton: false, // 免费版不允许取消,强制继续
}).then((result) => {
// 无论点确定还是倒计时结束,都跳转
window.location.href = nextUrl
})
return
}
}
console.log("已是最后一节,无下一个目录")
this.finish()
}
async run() {
try {
this.isAutoPlaying = true
await this.autoPlay()
} catch (e) {
console.error(e)
Swal.fire({
title: "失败!",
text: `视频基础播放失败!`,
icon: 'error',
confirmButtonColor: "#FF4DAFFF",
confirmButtonText: "确定",
timer: 5000,
timerProgressBar: true,
})
}
}
sendMsg(msg) {
// 创建 BroadcastChannel
const channel = new BroadcastChannel(this.channel);
channel.postMessage(msg);
}
/**
* 免费版达到3个视频上限后的提示
* 先显示自定义弹窗,关闭后再显示升级弹窗
*/
showFreeLimitReached() {
this.isAutoPlaying = false
// 第1个弹窗:自定义完成提示
Swal.fire({
title: '学习完成',
html: `
免费脚本功能有限,已为您完成前3个视频的学习。
如需体验更佳效果,请升级客户端。
`,
icon: 'success',
confirmButtonText: '确定',
allowOutsideClick: false, // 必须点确定才能关闭
}).then((result) => {
// 用户点击"确定"后,触发你脚本原有的升级弹窗
if (result.isConfirmed) {
console.log('[自动播放] 用户确认完成,显示升级弹窗')
Utils.showUpgradeAlert()
}
})
}
finish() {
// 如果是免费版且是因为达到上限而调用,不再重复弹窗
if (!this.VIP && this.playedVideoCount >= this.maxFreeVideos) {
// 已经由 showFreeLimitReached 处理过了,这里直接返回
return
}
if (!this.VIP) {
Swal.fire({
title: "请升级高级版!",
text: `脚本已停止!基础版只能连播几个视频!`,
icon: 'info',
confirmButtonColor: "#FF4DAFFF",
confirmButtonText: "确定",
timer: 0,
willClose: () => {
// 关闭后显示升级弹窗
Utils.showUpgradeAlert()
}
})
return
}
// VIP用户的完成逻辑(保持不变)
this.sendMsg('finish')
if (Swal) {
this.sendMsg('finish')
Swal.fire({
title: "学习完成!",
text: `学习完成,5s后页面自动关闭!`,
icon: 'success',
confirmButtonColor: "#FF4DAFFF",
confirmButtonText: "确定",
timer: 5000,
willClose: () => {
history.back()
setTimeout(()=>{
location.reload()
},1000)
}
})
}
}
async waitForVideoEnd(video,dom) {
return new Promise(resolve => {
const checkInterval = setInterval(async () => {
try {
const vid=document.querySelector('video')
if(!vid){
clearInterval(checkInterval)
resolve()
}
video.volume = 0
video.muted = true
if (video && video.paused) {
console.log("视频暂停了,重新开始播放...");
video.volume = 0
video.muted = true
await video.play();
}
if (!video.src) {
console.error("视频源未设置,即将重新加载");
setTimeout(() => {
location.reload()
}, 5000)
}
try {
const dialog=document.querySelector('div.el-dialog[aria-label="随机练习"]')
if(dialog){
const answer=window.QuestionInfo.correctAnswer
console.log("答题弹窗:答案:",answer)
const options = Array.from(dialog.querySelectorAll('input'));
const correctOption = options.find(opt => {
return opt.value.includes(answer); // 根据实际页面特征调整
});
correctOption.click()
await sleep(100)
dialog.querySelector('.submit').click()
await sleep(500)
document.querySelector('.el-message-box button').click()
}
}catch (e) {}
try{
const dialog=document.querySelector('div.el-dialog[aria-label="温馨提示"]')
if(dialog) {
dialog.querySelector('button').click()
}
}catch (e) {}
} catch (e) {
console.error("checkInterval error:", e);
clearInterval(checkInterval);
setTimeout(() => {
// location.reload()
}, 2000);
}
}, 5000);
video.addEventListener('ended', () => {
clearInterval(checkInterval);
resolve()
}, {once: true}); // 监听视频结束事件
});
}
getStudyNode(selector, type = 'node', dom, timeout = 10000) {
return new Promise((resolve, reject) => {
if (!['node', 'nodeList'].includes(type)) {
console.error('Invalid type parameter. Expected "node" or "nodeList"');
reject('Invalid type parameter. Expected "node" or "nodeList"');
}
const cleanup = (timeoutId, intervalId) => {
clearTimeout(timeoutId);
clearInterval(intervalId);
};
const handleSuccess = (result, timeoutId, intervalId) => {
console.log(`${selector} ready!`);
cleanup(timeoutId, intervalId);
resolve(result);
};
const handleFailure = (timeoutId, intervalId) => {
cleanup(timeoutId, intervalId);
resolve(null);
};
const checkNode = () => {
try {
let nodes;
if (type === 'node') {
nodes = dom ? dom.querySelector(selector) : document.querySelector(selector);
return nodes
}
nodes = dom ? dom.querySelectorAll(selector) : document.querySelectorAll(selector);
return nodes.length > 0 ? nodes : null;
} catch (error) {
console.error('节点检查错误:', error);
reject('节点检查错误:', error)
}
};
const intervalId = setInterval(() => {
const result = checkNode();
if (result) {
handleSuccess(result, timeoutId, intervalId);
} else {
console.log(`等待节点: ${selector}...`);
}
}, 1000);
const timeoutId = setTimeout(() => {
console.error(`节点获取超时: ${selector}`);
handleFailure(timeoutId, intervalId);
}, timeout);
});
}
checkFinish(dom) {
return dom.querySelector('.el-progress__text').innerText.includes("100")
}
checkFinish_post(dom) {
return dom.querySelector('.xxzt_icon3')
}
/**
*
* @param dom
* @returns {number} 0 视频 |1 文档|2 材料
*/
checkType(dom) {
if (dom.querySelector('.font-syllabus-online-video')) {
// 视频
return 0
} else if (dom.querySelector('.font-syllabus-page')) {
// 文档页面
return 1
} else if (dom.querySelector('.font-syllabus-material')) {
// 材料
return 2
}
}
}
class Utils {
constructor() {
}
// =========================================================
// 【内部标识】GM存储用的 key,一般不需要改
// =========================================================
static flag = 'hnedu123_VIP'
static js_Flag = 'hnedu123_jsCode'
static vipSign = 'hnedu123_vipSign'
// =========================================================
// 【必改配置】每个网站脚本只需修改这一块
// =========================================================
// 网站唯一ID,用于跳转到对应文档页面
static webId = '68099ad1b9fb230b03586910'
// 脚本启动时弹窗提示文字(简短说明脚本怎么用)
static swFireText = "请在视频播放页面使用脚本,脚本检测到视频会自动开始,脚本功能有限,也可能页面有所更新,导致脚本不能正常使用,建议下载软件使用!全自动学习所有未完成视频"
// 控制面板顶部提示文字(基础版状态)
static baseText = '建议下载软件使用!全自动学习所有未完成视频'
// 控制面板顶部提示文字(VIP/已验证状态)
static vipText = '全自动建议下载客户端使用!山东青岛专技(适配网址:https://www.qdjxjy.com.cn/)-客户端'
// 高级功能按钮文字
static vipBtnText = "前往软件使用课程助手,全自动学习所有未完成视频!"
// 【当前脚本】功能列表,显示在功能说明弹窗左侧
static scriptFeatures = [
"辅助当前页面视频播放",
"防暂停",
"自动静音播放",
"仅限单页面使用",
]
// 【客户端软件】功能列表,显示在功能说明弹窗右侧
static softwareFeatures = [
"输入账号密码即可全自动",
"全自动完成所有未完成课程 + 考试",
"支持批量多账号同时学习",
"支持倍速",
]
// =========================================================
// 【下载/链接配置】一般不需要改
// =========================================================
// 阿里云盘下载链接
static aliLink = "https://www.alipan.com/s/wViqbLvgSF8"
// 直链下载地址(备用)
static directLink = 'https://zzzzzzys.lanzouu.com/b00zxor42h'
// 授权码购买链接
static link = [
"https://68n.cn/IJ8QB",
"https://68n.cn/RM9ob",
]
// 备用网址列表(官网打不开时使用,总有一个能用)
static web_list = [
{ name: "备用地址0", url: "https://www.zzzzzzys.com/" },
{ name: "备用地址1", url: "https://zzzzzzys.lovestoblog.com/" },
{ name: "备用地址2", url: "https://zzzzzzys.us.kg/" },
{ name: "备用地址3", url: "https://zzysdocs.dpdns.org/" },
{ name: "备用地址4", url: "https://zzzzzzys.dpdns.org/" },
{ name: "备用地址5", url: "https://zzzzzzys.kesug.com/" },
{ name: "备用地址6", url: "https://zzysdocs.great-site.net/" },
]
// 官网文档链接(会自动拼接 webId)
static docLink = `${Utils.web_list[0].url}?webId=` + Utils.webId
static loadStatus() {
return false
}
static async validateCode(data) {
try {
Utils.showUpgradeAlert()
return
const res = await new Promise((resolve, reject) => {
GM_xmlhttpRequest({
'url': "https://zys-api.zzzzzzys.com/api/v1/external/quota/external/proxy/script-activate?" + new URLSearchParams(data),
method: 'GET',
onload: function (res) {
if (res.status === 200) {
const result = JSON.parse(res.response)
console.log(result)
resolve(result)
}
reject('请求失败:' + res.response)
},
onerror: function (err) {
console.error(err)
reject('请求错误!' + err.toString())
}
})
})
if (res.code !== 200) {
GM_deleteValue(Utils.flag)
GM_deleteValue(Utils.js_Flag)
throw new Error('验证失败:' + res.data)
}
Swal.fire({
title: "高级功能已启用!",
text: "校验成功!",
icon: 'success',
confirmButtonText: '确定',
});
GM_setValue(Utils.flag, true)
return res.data
} catch (e) {
console.error(e)
Swal.fire({
title: "验证失败!",
text: e.toString(),
icon: 'error',
confirmButtonText: '确定',
});
}
}
static async getJsCode(url) {
try {
let code = GM_getValue(Utils.js_Flag)
if (!code) {
const jsUrl = url
//获取js文件,然后在这里执行,然后获得结果
const jsCode = await new Promise((resolve, reject) => {
GM_xmlhttpRequest({
'url': jsUrl,
method: 'GET',
onload: function (res) {
console.log(res)
if (res.status === 200) {
const result = (res.responseText)
// console.log(result)
resolve(result)
} else {
reject('服务器拒绝:' + res.response)
}
},
onerror: function (err) {
console.error(err)
reject('请求错误!' + err.toString())
}
})
})
code = jsCode
.replace(/\\/g, '\\\\')
.replace(/'/g, '\'')
.replace(/"/g, '\"')
GM_setValue(Utils.js_Flag, code)
}
return code
} catch (error) {
console.error('远程加载失败:', error);
throw new Error("远程加载失败")
}
}
static showLinkSwal() {
const link = [
"https://68n.cn/IJ8QB",
"https://68n.cn/RM9ob",
]
Swal.fire({
title: ' 高级功能解锁',
html: `
`,
icon: 'info',
confirmButtonText: '前往激活',
showCloseButton: true,
timer: 30000,
customClass: {
popup: 'vip-alert-popup',
confirmButton: 'vip-confirm-btn'
},
willClose: () => {
// window.open(link[1])
}
});
}
static getStudyNode(selector, type = 'node', timeout = 10000) {
return new Promise((resolve, reject) => {
if (!['node', 'nodeList'].includes(type)) {
console.error('Invalid type parameter. Expected "node" or "nodeList"');
reject('Invalid type parameter. Expected "node" or "nodeList"');
}
const cleanup = (timeoutId, intervalId) => {
clearTimeout(timeoutId);
clearInterval(intervalId);
};
const handleSuccess = (result, timeoutId, intervalId) => {
console.log(`${selector} ready!`);
cleanup(timeoutId, intervalId);
resolve(result);
};
const handleFailure = (timeoutId, intervalId) => {
cleanup(timeoutId, intervalId);
resolve(null);
};
const checkNode = () => {
try {
let nodes;
if (type === 'node') {
nodes = document.querySelector(selector);
return nodes
}
nodes = document.querySelectorAll(selector);
return nodes.length > 0 ? nodes : null;
} catch (error) {
console.error('节点检查错误:', error);
reject('节点检查错误:', error)
}
};
const intervalId = setInterval(() => {
const result = checkNode();
if (result) {
handleSuccess(result, timeoutId, intervalId);
} else {
console.log(`等待节点: ${selector}...`);
}
}, 1000);
const timeoutId = setTimeout(() => {
console.error(`节点获取超时: ${selector}`);
handleFailure(timeoutId, intervalId);
}, timeout);
});
}
static parseChineseTime(timeStr, options = {}) {
// 正则匹配提取时、分、秒数值
const pattern = /(?:(\d+)小时)?(?:(\d+)分)?(?:(\d+)秒)?/;
const matches = timeStr.match(pattern) || [];
const hours = parseInt(matches[1] || 0, 10);
const minutes = parseInt(matches[2] || 0, 10);
const seconds = parseInt(matches[3] || 0, 10);
const totalSeconds = hours * 3600 + minutes * 60 + seconds;
return options.returnObject
? {hours, minutes, seconds}
: totalSeconds;
}
static decodeJWT(token){
try {
const [headerB64, payloadB64] = token.split('.');
const decodeBase64Url = (str) => {
return atob(str.replace(/-/g, '+').replace(/_/g, '/').padEnd(str.length + (4 - str.length % 4) % 4, '='));
};
const header = JSON.parse(decodeBase64Url(headerB64));
const payload = JSON.parse(
decodeURIComponent(
decodeBase64Url(payloadB64)
.split('')
.map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
.join('')
)
);
return { header, payload };
} catch (error) {
console.error('解码失败:', error);
return null;
}
}
static showUpgradeAlert() {
return Swal.fire({
title: '全自动学习功能说明',
html: `
⚠️ 网页脚本有局限性
浏览器脚本运行在沙盒中,无法跨页面、无法自动登录、无法批量管理账号。
若需要 输入账号密码后全自动完成所有视频 + 考试,推荐使用本地客户端。
📄 当前脚本
${Utils.scriptFeatures.map(f => `- ${f}
`).join('')}
推荐
🖥️ 客户端工具
${Utils.softwareFeatures.map((f, i) => `- ${i === 0 ? `${f}` : f}
`).join('')}
▸ 官网无法访问?点击展开备用网址
`,
showCancelButton: true,
confirmButtonText: '前往官网',
cancelButtonText: '关闭',
confirmButtonColor: '#38a169',
cancelButtonColor: '#a0aec0',
width: '620px',
padding: '1.8em',
background: '#ffffff',
backdrop: 'rgba(0,0,0,0.25)',
customClass: { popup: 'shadow-lg' }
}).then((result) => {
if (result.isConfirmed) {
window.open(Utils.docLink, '_blank');
}
});
}
}
class AuthWindow {
constructor({VIPBtnText = "高级功能,极速刷课", VIPInfo = "您正在使用基础版本,功能可能存在限制",}) {
this.storageKey = 'AuthData';
this.injectGlobalStyles();
this.initDOM();
this.loadPersistedData();
this.show();
this.setVIPBtnText(Utils.vipText);
this.setTip(VIPInfo)
// this.startAutomation()
}
injectGlobalStyles() {
GM_addStyle(`
.auth-window { position: fixed; bottom: 10px; right: 10px; z-index: 999999999999; background: white; padding: 24px; border-radius: 12px; box-shadow: 0 6px 30px rgba(0,0,0,0.15); border: 1px solid #e4e7ed; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; min-width: 320px; transform: translateY(20px); opacity: 0; transition: all 0.3s ease; } .auth-window.visible { transform: translateY(0); opacity: 1; } .auth-title { margin: 0 0 16px; font-size: 20px; color: #2c3e50; font-weight: 600; display: flex; align-items: center; gap: 8px; } .auth-version { font-size: 12px; color: #95a5a6; font-weight: normal; } .auth-tip { margin: 0 0 20px; color: #ffbb00; font-size: 14px; font-weight: weight; line-height: 1.5; } .input-group { margin-bottom: 18px; } .input-label { display: block; margin-bottom: 6px; color: #34495e; font-size: 14px; font-weight: 500; } .input-field { width: 80%; padding: 10px 12px; border: 2px solid #e0e0e0; border-radius: 8px; font-size: 14px; transition: border-color 0.2s; } .input-field:focus { outline: none; border-color: #3498db; box-shadow: 0 0 0 3px rgba(52,152,219,0.1); } .auth-button { width: 100%; padding: 12px; background: #3498db; color: white; border: none; border-radius: 8px; font-size: 14px; font-weight: 600; cursor: pointer; transition: all 0.2s ease; display: flex; align-items: center; justify-content: center; gap: 8px; } .auth-button:hover { background: #2980b9; transform: translateY(-1px); } .auth-button:active { transform: translateY(0); } .error-message { color: #e74c3c; font-size: 13px; margin-top: 8px; padding: 8px; background: #fdeded; border-radius: 6px; display: none; animation: shake 0.4s; } @keyframes shake { 0%, 100% { transform: translateX(0); } 25% { transform: translateX(-5px); } 75% { transform: translateX(5px); } } .control-panel { opacity: 1; transform: translateY(10px); transition: all 0.3s ease; } .control-panel.visible { opacity: 1; transform: translateY(0); } .auth-button[disabled] { background: #bdc3c7 !important; cursor: not-allowed; } .auth-window { position: fixed; right: 30px; bottom: 80px; transition: transform 0.3s ease; } .window-toggle:hover .toggle-icon { animation: bounce 0.6s; } .toggle-icon { width: 20px; height: 20px; transition: transform 0.3s ease; } @keyframes bounce { 0%, 100% { transform: translateX(0); } 50% { transform: translateX(4px); } } /* VIP 按钮特效 */ .vip-btn { width: 100%; position: relative; padding: 12px 24px; border: none; border-radius: 8px; background: linear-gradient(135deg, #ffd700 0%, #ffd900 30%, #ffae00 70%, #ff8c00 100%); color: #2c1a00; font-weight: 600; font-family: 'Segoe UI', sans-serif; cursor: pointer; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); overflow: hidden; box-shadow: 0 4px 15px rgba(255, 174, 0, 0.3); } /* 辉光动画效果 */ .glow-effect::after { content: ''; position: absolute; inset: 0; background: radial-gradient(circle at 50% 0%, rgba(255, 255, 255, 0.4) 0%, transparent 70%); opacity: 0; transition: opacity 0.3s; } /* 悬停交互 */ .vip-btn:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(255, 174, 0, 0.5); } .vip-btn:hover::after { opacity: 1; } /* 点击反馈 */ .vip-btn:active { transform: translateY(1px); box-shadow: 0 2px 8px rgba(255, 174, 0, 0.3); } /* 皇冠图标动画 */ .crown-icon { width: 20px; height: 20px; margin-right: 8px; vertical-align: middle; transition: transform 0.3s; } .vip-btn:hover .crown-icon { transform: rotate(10deg) scale(1.1); } /* 文字渐变特效 */ .vip-text { background: linear-gradient(45deg, #2c1a00, #5a3a00); -webkit-background-clip: text; background-clip: text; color: transparent; display: inline-block; } * 弹窗容器 */ .vip-alert-popup { border: 2px solid #ffd700; border-radius: 12px; background: linear-gradient(145deg, #1a1a1a, #2d2d2d); } /* 标题区域 */ .alert-header { border-bottom: 1px solid #404040; padding-bottom: 12px; margin-bottom: 15px; } .swal-vip-icon { color: #ffd700; font-size: 2.2em; margin-right: 8px; } /* 需求列表 */ .requirements-box { background: rgba(255,215,0,0.1); border-radius: 8px; padding: 15px; margin: 15px 0; } .requirement-item { display: flex; align-items: center; margin: 10px 0; } .number-badge { background: #ffd700; color: #000; width: 24px; height: 24px; border-radius: 50%; text-align: center; margin-right: 12px; font-weight: bold; } /* 状态标签 */ .status-tag { padding: 4px 8px; border-radius: 4px; font-size: 0.9em; } .free-status { background: #ff4444; color: white; } /* 操作引导 */ .action-guide { background: rgba(255,255,255,0.05); padding: 15px; border-radius: 8px; } .step-list li { margin: 8px 0; padding-left: 8px; } .pricing-link { color: #00ff9d !important; text-decoration: underline dotted; transition: all 0.3s; } .pricing-link:hover { color: #00cc7a !important; text-decoration: underline; } /* 确认按钮 */ .vip-confirm-btn { background: linear-gradient(135deg, #ffd700 0%, #ff9900 100%) !important; border: none !important; font-weight: bold !important; transition: transform 0.2s !important; } .vip-confirm-btn:hover { transform: scale(1.05); }
`)
GM_addStyle(` div.swal2-container { all: initial !important; /* 重置所有继承样式 */ position: fixed !important; z-index: 999999 !important; inset: 0 !important; display: flex !important; align-items: center !important; justify-content: center !important; background: rgba(0,0,0,0.4) !important; } .swal2-popup { all: initial !important; max-width: 600px !important; width: 90vw !important; min-width: 300px !important; position: relative !important; box-sizing: border-box !important; padding: 20px !important; background: white !important; border-radius: 8px !important; font-family: Arial !important; animation: none !important; } @keyframes swal2-show { 0% { transform: scale(0.9); opacity: 0 } 100% { transform: scale(1); opacity: 1 } } `);
GM_addStyle(` /* 实验功能容器 */ .beta-container { margin: 18px 0; border-radius: 10px; background: linear-gradient(145deg, #2d2d2d, #1a1a1a); border: 1px solid rgba(255, 215, 0, 0.2); box-shadow: 0 4px 20px rgba(0,0,0,0.2); } .beta-card { padding: 16px; } /* 标题区域 */ .beta-header { display: flex; align-items: center; gap: 12px; margin-bottom: 18px; } .beta-icon { width: 28px; height: 28px; fill: #ffd700; filter: drop-shadow(0 0 4px rgba(255,215,0,0.3)); } .beta-title { margin: 0; color: #ffd700; font-size: 16px; font-weight: 600; text-shadow: 0 2px 4px rgba(0,0,0,0.2); } /* 开关组件 */ .beta-toggle { display: flex; align-items: center; gap: 12px; cursor: pointer; padding: 10px; border-radius: 8px; transition: background 0.3s; } .beta-toggle:hover { background: rgba(255,215,0,0.05); } .beta-checkbox { display: none; } /* 自定义轨道 */ .beta-track { position: relative; width: 50px; height: 28px; border-radius: 14px; background: rgba(255,215,0,0.1); border: 1px solid rgba(255,215,0,0.3); transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); } /* 滑块 */ .beta-thumb { position: absolute; left: 2px; top: 2px; width: 24px; height: 24px; background: linear-gradient(145deg, #ffd700, #ffae00); border-radius: 50%; transform: translateX(0); transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1); box-shadow: 0 2px 4px rgba(0,0,0,0.2); } /* 选中状态 */ .beta-checkbox:checked + .beta-track { background: rgba(255,215,0,0.2); border-color: #ffd700; } .beta-checkbox:checked + .beta-track .beta-thumb { transform: translateX(22px); } /* 光效 */ .beta-sparkles { position: absolute; width: 100%; height: 100%; background: radial-gradient(circle at 50% 50%, rgba(255,255,255,0.8) 10%, transparent 60%); opacity: 0; transition: opacity 0.3s; } .beta-checkbox:checked + .beta-track .beta-sparkles { opacity: 0.3; } /* 文字样式 */ .beta-label { color: #fff; font-size: 14px; font-weight: 500; letter-spacing: 0.5px; background: linear-gradient(90deg, #ffd700, #ffae00); -webkit-background-clip: text; background-clip: text; color: transparent; } .beta-tip { margin: 12px 0 0; color: rgba(255,215,0,0.6); font-size: 12px; line-height: 1.4; padding-left: 8px; border-left: 3px solid rgba(255,215,0,0.3); } /* 新增进度条样式 */ .progress-overlay { position: fixed; bottom: 0; left: 30%; transform: translate(0 -50%); background: rgba(0,0,0,0.8); padding: 24px; border-radius: 12px; color: white; z-index: 9999999999; display: none; min-width: 300px; height:100px; box-shadow: 0 4px 20px rgba(0,0,0,0.3); backdrop-filter: blur(8px); } .progress-header { margin-bottom: 12px; display: flex; justify-content: space-between; align-items: center; } .progress-title { margin: 0; font-size: 16px; color: #fff; } .progress-bar { display:block; width: 100%; height: 8px; background: rgba(255,255,255,0.1); border-radius: 4px; overflow: hidden; } .progress-fill { height: 100%; background: linear-gradient(90deg, #00ff88, #00ccff); transition: width 0.3s ease; } .progress-info { margin-top: 15px; text-align: center; /* 整体内容水平居中 */ gap: 20px; /* 添加元素间距 */ font-size: 12px; color: rgba(255,255,255,0.8); }`);
}
initDOM() {
this.container = document.createElement('div');
this.container.className = 'auth-window';
// 标题区域
const title = document.createElement('h3');
title.className = 'auth-title';
title.innerHTML = `
脚本控制台v${GM_info.script.version}
`;
// 提示信息
const tip = document.createElement('p');
tip.className = 'auth-tip';
tip.textContent = '您正在使用基础版本,功能可能存在限制';
this.tip = tip
// 输入框组
// this.phoneInput = this.createInput(' 手机/QQ号', 'text', '#phone');
this.authInput = this.createInput(' 授权密钥', 'password', '#auth');
// 授权链接
const link =Utils.link
const authLink1 = this.createLink('authLink1', link[0], '获取授权链接1');
const authLink2 = this.createLink('authLink2', link[1], '获取授权链接2');
// 验证按钮
this.verifyBtn = document.createElement('button');
this.verifyBtn.className = 'auth-button';
this.verifyBtn.innerHTML = `
验证授权码
`;
this.verifyBtn.onclick = () => this.handleVerify();
// 启动控制面板
this.controlPanel = document.createElement('div');
this.controlPanel.className = 'control-panel';
this.controlPanel.style.cssText = `
margin-top: 20px;
border-top: 1px solid #eee;
padding-top: 16px;
`;
this.vipBtn = document.createElement('button');
this.vipBtn.className = 'vip-btn glow-effect';
this.vipBtn.innerHTML = `
高级功能-全自动挂机
`;
this.vipBtn.addEventListener('click', () => {
this.handleVIPClick()
})
// 计时器
this.timerDisplay = document.createElement('div');
this.timerDisplay.className = 'timer';
this.timerDisplay.textContent = '运行时间: 00:00:00';
this.timerDisplay.style.cssText = `
color: #2ecc71;
font-size: 13px;
margin-bottom: 12px;
`;
// 开始按钮
this.startBtn = document.createElement('button');
this.startBtn.className = 'auth-button';
this.startBtn.style.backgroundColor = '#2ecc71';
this.startBtn.innerHTML = `
开始运行-自动化挂机
`;
this.startBtn.onclick = () => this.startAutomation();
// 错误提示
this.errorBox = document.createElement('div');
this.errorBox.className = 'error-message';
this.vipGroup = document.createElement('div');
this.vipGroup.className = 'beta-container';
this.vipGroup.innerHTML = `
`;
this.betaCheckbox = this.vipGroup.querySelector('#beta-speed');
this.betaCheckbox.checked = GM_getValue(Utils.vipSign, false);
this.betaCheckbox.onchange = (e) => {
GM_setValue(Utils.vipSign, e.target.checked);
};
// 组装结构
this.controlPanel.append(
this.vipBtn,
// this.vipGroup,
this.timerDisplay,
this.startBtn
);
this.container.append(
title,
tip,
// this.phoneInput.container,
this.authInput.container,
authLink1,
authLink2,
this.verifyBtn,
this.controlPanel,
this.errorBox
);
document.body.appendChild(this.container);
this.initControlBtn()
}
initControlBtn() {
// 创建控制按钮
this.toggleBtn = document.createElement('button');
this.toggleBtn.className = 'window-toggle';
this.toggleBtn.innerHTML = `
展开面板
`;
this.toggleBtn.style.cssText = `
position: fixed;
right: 30px;
bottom: 30px;
padding: 12px 20px;
background: #fff;
border: none;
border-radius: 30px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
cursor: pointer;
display: flex;
align-items: center;
gap: 8px;
transition: all 0.3s ease;
z-index: 9999999;
`;
// 添加交互效果
this.toggleBtn.addEventListener('mouseenter', () => {
this.toggleBtn.style.transform = 'translateY(-2px)';
this.toggleBtn.style.boxShadow = '0 6px 16px rgba(0,0,0,0.2)';
});
this.toggleBtn.addEventListener('mouseleave', () => {
this.toggleBtn.style.transform = 'none';
this.toggleBtn.style.boxShadow = '0 4px 12px rgba(0,0,0,0.15)';
});
// 点击事件处理
this.toggleBtn.onclick = () => {
const isVisible = this.container.style.display !== 'none';
this.container.style.display = isVisible ? 'none' : 'block';
// 更新按钮状态
this.toggleBtn.querySelector('.toggle-icon').style.transform =
isVisible ? 'rotate(180deg)' : 'none';
this.toggleBtn.querySelector('.toggle-text').textContent =
isVisible ? '展开面板' : '收起面板';
// 添加动画效果
if (!isVisible) {
this.container.animate([
{opacity: 0, transform: 'translateY(20px)'},
{opacity: 1, transform: 'none'}
], {duration: 300, easing: 'ease-out'});
}
};
document.body.appendChild(this.toggleBtn);
}
startAutomation(callback) {
if (!this.isRunning) {
this.startTime = Date.now();
this.isRunning = true;
this.startBtn.innerHTML = `
运行中...
`;
this.startBtn.style.backgroundColor = '#e67e22';
this.startBtn.disabled = true;
// 启动计时器
this.timer = setInterval(() => {
const elapsed = Date.now() - this.startTime;
const hours = Math.floor(elapsed / 3600000);
const minutes = Math.floor((elapsed % 3600000) / 60000);
const seconds = Math.floor((elapsed % 60000) / 1000);
this.timerDisplay.textContent =
`运行时间: ${hours.toString().padStart(2, '0')}:` +
`${minutes.toString().padStart(2, '0')}:` +
`${seconds.toString().padStart(2, '0')}`;
}, 1000);
// 触发自动化任务
if (this.begin && typeof this.begin === 'function') {
this.begin()
}
if (typeof callback === 'function') {
callback()
}
}
}
createInput(labelText, type, id) {
const container = document.createElement('div');
container.className = 'input-group';
const label = document.createElement('label');
label.className = 'input-label';
label.textContent = labelText;
label.htmlFor = id;
const input = document.createElement('input');
input.className = 'input-field';
input.type = type;
input.id = id;
input.maxLength = 16
container.appendChild(label);
container.appendChild(input);
return {container, input};
}
createLink(id, link, name) {
const authLink = document.createElement('a');
authLink.id = id;
authLink.className = 'auth-link';
authLink.href = link;
authLink.target = '_blank';
authLink.textContent = name;
authLink.style.cssText = `
display: block; margin: 12px 0; color: #3498db; text-decoration: none; font-size: 13px; transition: opacity 0.2s; `;
authLink.addEventListener('mouseenter', () => {
authLink.style.opacity = '0.8';
authLink.style.textDecoration = 'underline';
});
authLink.addEventListener('mouseleave', () => {
authLink.style.opacity = '1';
authLink.style.textDecoration = 'none';
});
return authLink
}
show() {
setTimeout(() => {
this.container.classList.add('visible');
}, 100);
}
showError(message) {
this.errorBox.textContent = message;
this.errorBox.style.display = 'block';
setTimeout(() => {
this.errorBox.style.display = 'none';
}, 5000);
}
async handleVerify() {
const data = {
// phone: this.phoneInput.input.value,
key: this.authInput.input.value
};
console.log(data);
if (!data.key || !(/^[A-Z0-9]{16}$/).test(data.key)) {
Swal.fire({
title: "授权码不正确,应为16位",
text: "请正确输入!",
icon: 'info',
confirmButtonText: '确定',
});
return
}
// 触发验证回调
if (this.onVerify) {
if (await this.onVerify(data)) {
GM_setValue(this.storageKey, JSON.stringify(data))
} else {
}
}
}
handleVIPClick() {
if (this.vipCallback) {
this.vipCallback()
} else {
Swal.fire({
title: "提示",
text: "请在视频播放页面使用!",
icon: 'info',
confirmButtonText: '确定',
willClose: () => {
console.log(' 用户确认错误,脚本已停止');
}
});
}
}
loadPersistedData() {
let saved = GM_getValue(this.storageKey);
if (saved) {
saved = JSON.parse(saved)
// this.phoneInput.input.value = saved.phone || '';
this.authInput.input.value = saved.key || '';
}
}
hide() {
this.container.style.display = 'none';
}
// get phone() {
// return this.phoneInput.input.value;
// }
// set phone(value) {
// this.phoneInput.input.value = value;
// }
get key() {
return this.authInput.input.value;
}
set key(value) {
// this.authInput.input.value = value;
}
setTip(text) {
this.tip.innerText = text
}
// 验证回调函数
setOnVerifyCallback(callback) {
this.onVerify = callback;
}
setOnBegin(callback) {
this.begin = callback;
}
setOnVIP(callback) {
this.vipCallback = callback;
}
setVIPBtnText(text) {
this.vipBtn.innerHTML = `
${text}
`;
}
}
const sleep = function (time) {
return new Promise(resolve => setTimeout(resolve, time));
}
new Runner()