// ==UserScript==
// @name QFZ网大自动学习机
// @namespace http://tampermonkey.net/
// @version 0.2
// @description 支持功能:自动切换章节、自动切换视频、自动提交随机选项以记录答案、根据记录答案自动完成考试
// @author yuyang
// @match https://kc.zhixueyun.com/
// @icon https://www.google.com/s2/favicons?sz=64&domain=zhixueyun.com
// @grant none
// @license MIT
// ==/UserScript==
const modal = document.createElement("div")
const modalCss = "overflow:hidden;z-index:999999;position:absolute;width:400px;height:250px;background-color:white;bottom:5%;right:5%;font-size: 18px;padding: 4px;color: black;border: 4px red solid;"
modal.style.cssText = modalCss
modal.innerHTML += "小助手日志<br/>"
document.body.appendChild(modal)
function modalMessage(message) {
modal.innerHTML += message + "<br/>"
modal.scrollTop = modal.scrollHeight
}
function logWithColor(text) {
console.log(`%c${text}`, 'color: #fff; background: #f40; font-size: 15px;border-radius:0 6px 6px 0;padding:6px;');
modalMessage(text)
}
function waitForElementReady(parent, selector, checker) {
return new Promise((resolve) => {
setTimeout(resolve, 5000)
})
}
window.addEventListener("beforeunload", (event) => {
// Cancel the event as stated by the standard.
event.preventDefault();
// Chrome requires returnValue to be set.
event.returnValue = "";
});
async function pageSubjectDetail() {
// 课程详情
logWithColor("课程详情页面")
await waitForElementReady()
logWithColor(`记录当前学习课程链接 ${window.location.href}`)
localStorage.setItem("subject-detail", window.location.href)
logWithColor("获取章节完成进度:")
let chapterNodes = [...document.querySelectorAll("[id^='D294studyBtn']")]
// 名字和完成状态
const status = chapterNodes.map(node => ({
title: node.querySelector(".name-des").innerText,
isComplete: node.querySelector(".operation").querySelector(".small").innerText.includes("重新学习"),
node: node
}))
console.table(status)
const inComplete = status.find(s => !s.isComplete)
if (!inComplete) {
logWithColor("所有课程已完成")
return
}
logWithColor(`找到最近的未完成课程: 「${inComplete.title}」 正在跳转...`)
inComplete.node.querySelector(".name-des").click()
setTimeout(() => window.close(), 3000)
}
async function pageStudyCourse() {
// 获得当前学习是 视频 还是 考试
// 获取侧边栏信息以当前需要处理的任务
logWithColor("学习任务页面")
if (!document.querySelector("#D195container")) {
logWithColor("等待标识容器D195加载")
await waitForElementReady(document, "#D195container", () => true)
logWithColor("标识容器D195加载完成 开始执行")
}
const fetchCurrentInfo = () => {
const sideContainerId = "#D196course-side-catalog"
const sideItemNodes = [...document.querySelector(sideContainerId).querySelectorAll(".chapter-list-box")]
const sideItemInfo = sideItemNodes.map(node => ({
id: node.id,
name: node.querySelector(`div[title]:not([title=""])`).innerText.replaceAll(/第.*: /g, ""),
type: node.querySelector(".section-item").querySelector(".item").innerHTML,
isComplete: (node.querySelector(".section-item").querySelector(".item").innerHTML.includes("视频")
&& node.querySelector(".section-item").querySelector("span").innerText.includes("重新学习")) ||
(node.querySelector(".section-item").querySelector(".item").innerHTML.includes("考试")
&& node.querySelector(".section-item").querySelector("span").innerText.includes("成绩100")) ||
(node.querySelector(".section-item").querySelector(".item").innerHTML.includes("文档")
&& node.querySelector(".section-item").querySelector("span").innerText.includes("重新学习"))
}))
logWithColor("当前的学习进度")
console.table(sideItemInfo)
const incItem = sideItemInfo.find(info => !info.isComplete)
if (!incItem) {
logWithColor("本章已学完")
} else {
logWithColor("当前学习任务:" + incItem.name)
}
return incItem
}
let currentStudyInfo
while (true) {
// 更新当前任务
const res = fetchCurrentInfo()
if (!res) {
logWithColor("结束")
// 没有未完成的任务了
break
} else {
currentStudyInfo = res
}
// 处理学习任务
// 切换对应任务
document.getElementById(currentStudyInfo.id).click()
const totalParentSelector = ".player-content"
await new Promise(resolve => setTimeout(resolve, 5000))
if (currentStudyInfo.type === "视频") {
const video = document.querySelector("video")
video.play()
// video.setAttribute("autoplay", "true")
// video.setAttribute("muted", "true")
video.muted = true
if (!document.querySelector("#D195container")) {
logWithColor("视频任务 等待视频加载")
// await waitForElementReady(document.querySelector(totalParentSelector), "div.vjs-current-time-display", () => true)
// await waitForElementReady(document.querySelector(totalParentSelector), "div.vjs-control-text", (el) => el.innerHTML.includes("播放"))
await waitForElementReady(document.querySelector(totalParentSelector), "div", () => !!document.querySelector(totalParentSelector).querySelector(".videojs-referse-btn"))
}
logWithColor("视频任务 加载完成")
// 重新播放按钮
const refersebtn = document.querySelector(totalParentSelector).querySelector(".vjs-paused")
if (refersebtn) {
logWithColor("视频任务 点击恢复播放")
// document.querySelector(".vjs-play-control").click()
video.play()
// refersebtn.click()
}
logWithColor("视频任务 等待视频播放中")
await waitForElementReady(document.querySelector(".catalog-control"), {childList: true}, 0)
}
if (currentStudyInfo.type === "考试") {
if (!document.querySelector(".demand-table")) {
logWithColor("考试任务 等待考试记录加载")
await waitForElementReady(document.querySelector(".player-content"), "div.demand-table", (element) => {
return element.querySelector(".repeat-exam");
})
}
logWithColor("考试任务 加载完成 检查完成状态")
const statusNode = document.querySelector(totalParentSelector).querySelector(".neer-status")
if (!statusNode) {
logWithColor("考试任务 没有考试记录 点进去开始考试")
let joinBtn = document.querySelector(totalParentSelector).querySelector(".new-exam")
if (joinBtn == null) {
joinBtn = document.querySelector(totalParentSelector).querySelector(".repeat-exam")
}
joinBtn.firstElementChild.click()
setTimeout(() => window.close(), 3000)
} else {
logWithColor(`考试答案 ${getAnswer(currentStudyInfo.name)}`)
if (getAnswer(currentStudyInfo.name) == null) {
logWithColor("考试任务 有记录 进入收集答案")
document.querySelector(totalParentSelector).querySelector(".new-exam").click()
setTimeout(() => window.close(), 3000)
} else {
logWithColor("考试任务 有答案 进入开始考试")
document.querySelector(totalParentSelector).querySelector(".repeat-exam").firstElementChild.click()
setTimeout(() => window.close(), 3000)
}
}
return
}
if (currentStudyInfo.type === "文档") {
logWithColor("文档任务 等待文档加载")
await waitForElementReady()
}
}
logWithColor(`跳转到上次打开的课程详情页面 = ${localStorage.getItem("subject-detail")}`)
window.open(localStorage.getItem("subject-detail"), "_blank")
setTimeout(() => window.close(), 3000)
}
async function scoreDetail() {
logWithColor("考试结果页面")
const textSelector = "div[data-current='exam/exam/question/types/answer/choise:content']"
const optionSelect = "div[data-current='exam/exam/question/types/answer/choise:options']"
if (!document.querySelector(".preview-content")) {
logWithColor("等待答案加载中")
await waitForElementReady(document, textSelector, (el) => !!document.querySelector(".show-answer").querySelector(".custom-color-4"))
logWithColor("答案加载完成")
}
logWithColor("正在收集答案")
const optionList = []
while (true) {
// 获取选项信息
const text = document.querySelector(".preview-content").querySelector(".show-answer").querySelector(".custom-color-4")
const op = text.innerHTML.split(":")[1].trim()
optionList.push(op)
logWithColor(`题目${optionList.length} 选项: ${op} 已记录`)
const bottomBtn = document.querySelector(".preview-content").querySelector(".m-bottom").firstElementChild
if (bottomBtn.innerHTML.includes("上一题")) {
logWithColor("已获取全部题目答案")
break
}
if (bottomBtn.innerHTML.includes("下一题")) {
logWithColor("加载下个题目")
bottomBtn.click()
await waitForElementReady(document, textSelector, (el) => !!document.querySelector(".show-answer").querySelector(".custom-color-4"))
}
}
const answerData = {
examTitle: document.querySelector(".achievement-head").querySelector(".title").getAttribute("title"),
optionList
}
saveAnswer(answerData)
logWithColor(`跳转到上次打开的课程详情页面 = ${localStorage.getItem("subject-detail")}`)
window.open(localStorage.getItem("subject-detail"), "_blank")
setTimeout(() => window.close(), 3000)
}
const KEY_ANSWER = "QFZ.answer"
function saveAnswer(answer) {
let pre = localStorage.getItem(KEY_ANSWER)
let arr = []
if (pre == null || pre === "") {
pre = "[]"
localStorage.setItem(KEY_ANSWER, pre)
} else {
arr = JSON.parse(pre)
}
answer.examTitle = answer.examTitle.trim()
if (arr.find(a => a.examTitle === answer.examTitle)) return
arr.push(answer)
localStorage.setItem(KEY_ANSWER, JSON.stringify(arr))
}
function getAnswer(title) {
let pre = localStorage.getItem(KEY_ANSWER)
if (pre == null) {
return null
}
const arr = JSON.parse(pre)
const ol = arr.find(a => a.examTitle === title.trim())
if (ol != null) {
return ol.optionList
} else {
return null
}
}
async function pageExam() {
logWithColor("考试页面")
const textSelector = "div[data-current='exam/exam/question/types/answer/choise:content']"
if (!document.querySelector(".preview-content")) {
logWithColor("等待题目加载中")
await waitForElementReady(document, textSelector, (el) => !!document.querySelector(".question").querySelector("span"))
logWithColor("题目加载完成")
}
const examTitle = document.querySelector(".achievement-head").querySelector(".title").getAttribute("title")
logWithColor(`当前考试标题: ${examTitle} 尝试获取答案`)
const answer = getAnswer(examTitle)
let opList;
if (answer !== null) {
logWithColor("找到答案 开始答题")
opList = answer
} else {
logWithColor("未找到答案 随机回答并提交以获取正确答案")
opList = ["A", "A"]
}
// 开始答题
while (true) {
const op = opList.shift().split("") // 当前要作答的选项
// 所有选项列表
const ddList = document.querySelector("div[data-current='exam/exam/question/types/answer/choise:options']").querySelectorAll("dd")
logWithColor("进行选择: " + op)
ddList.forEach(d => {
const leftNum = d.querySelector(".option-num").innerHTML
op.forEach(o => {
if(leftNum.includes(o)) {
console.log(d.id)
document.getElementById(d.id).querySelector("input").click()
}
})
})
const bottomBtn = document.querySelector(".preview-content").querySelectorAll(".m-bottom").item(1).firstElementChild
console.log(bottomBtn)
if (bottomBtn.innerHTML.includes("上一题")) {
logWithColor("已作答全部题目 提交试卷")
break
}
if (bottomBtn.innerHTML.includes("下一题")) {
logWithColor("加载下个题目")
bottomBtn.click()
await waitForElementReady(document, textSelector, (el) => !!document.querySelector(".question").querySelector("span"))
}
}
// 提交试卷
document.querySelector("#D165submit").click()
await waitForElementReady(document, "#alertify", () => true)
document.querySelector("#alertify").querySelector(".alertify-button-ok").click()
logWithColor(`跳转到上次打开的课程详情页面 = ${localStorage.getItem("subject-detail")}`)
window.open(localStorage.getItem("subject-detail"), "_blank")
setTimeout(() => window.close(), 3000)
}
(async function() {
'use strict';
const href = window.location.href
if (href.includes("study/subject/detail")) {
await pageSubjectDetail()
}
if (href.includes("study/course/detail")) {
await pageStudyCourse()
}
if (href.includes("exam/exam/front/score-detail")) {
await scoreDetail()
}
if (href.includes("exam/exam/answer-paper")) {
await pageExam()
}
})();