// ==UserScript==
// @name 江西省会计继续教育-会计人员继续教育网络培训-东奥继教刷课助手(jiangxi.dongao.cn)
// @version 1.0
// @description 东奥会计在线江西站刷课助手,支持视频刷课+自动答题,客服微信wkds995
// @author 晓晓
// @match *://jiangxi.dongao.cn/*
// @grant GM_getValue
// @grant GM_setValue
// @run-at document-end
// @license MIT
// ==/UserScript==
(function () {
'use strict';
// ===================== 弹窗模板(标准4条须知,打赏需点按钮)=====================
(function(){
function _esc(s) {
return s.replace(/[<>&'"]/g, function(c){
return {'<':'<','>':'>','&':'&',"'":''','"':'"'}[c];
});
}
function _injectStyles() {
if (document.getElementById('xx-popup-styles')) return;
var s = document.createElement('style');
s.id = 'xx-popup-styles';
s.textContent = [
'.xx-mask{position:fixed;inset:0;background:rgba(0,0,0,0.75);z-index:99999998;display:flex;align-items:center;justify-content:center;}',
'.xx-box{background:#181c2b;border-radius:16px;border:1px solid rgba(255,255,255,0.12);padding:28px;color:#fff;font-family:system-ui,-apple-system,sans-serif;}',
'.xx-box h2{margin:0 0 18px;font-size:18px;text-align:center;border-bottom:1px solid rgba(255,255,255,0.1);padding-bottom:14px;color:#fff;}',
'.xx-content{line-height:1.8;font-size:14px;color:#e2e8f0;}',
'.xx-content p{margin:6px 0;}',
'.xx-footer{margin-top:22px;text-align:center;}',
'.xx-btn{padding:11px 36px;border:none;border-radius:10px;cursor:pointer;font-size:14px;font-weight:600;transition:opacity .2s;}',
'.xx-btn:hover{opacity:0.88;}',
'.xx-btn-primary{background:linear-gradient(135deg,#6a11cb,#2575fc);color:#fff;}',
'.xx-btn-secondary{background:rgba(255,255,255,0.08);color:#aaa;border:1px solid rgba(255,255,255,0.15);}',
'.xx-donate{width:360px;}',
'.xx-donate h3{margin:0 0 14px;font-size:16px;text-align:center;letter-spacing:.5px;color:#fff;}',
'.xx-wechat-label{text-align:center;font-size:13px;color:#c9d1d9;margin-bottom:10px;letter-spacing:1px;}',
'.xx-qr-wrap{width:220px;height:220px;background:#fff;border-radius:12px;margin:0 auto 12px;display:flex;align-items:center;justify-content:center;overflow:hidden;}',
'.xx-qr-wrap img{width:200px;height:200px;border-radius:8px;object-fit:cover;}',
'.xx-donate-desc{font-size:12px;color:#c9d1d9;margin-bottom:16px;text-align:center;line-height:1.6;}'
].join('');
document.head.appendChild(s);
}
window.__popupGuide = function(items, btnText, onClose) {
if (document.querySelector('.xx-guide-mask')) return;
btnText = btnText || '我已知晓';
onClose = onClose || function(){};
_injectStyles();
var lines = items.map(function(item){
var icon = item.ok !== false ? '\u2705' : '\u26a0\ufe0f';
return '
' + icon + ' ' + _esc(item.text) + '
';
}).join('');
var mask = document.createElement('div');
mask.className = 'xx-mask xx-guide-mask';
mask.innerHTML = '\ud83d\udcd6 \u811a\u672c\u4f7f\u7528\u9875\u77e5
' + lines + '
';
document.body.appendChild(mask);
function close(){ mask.remove(); onClose(); }
mask.querySelector('.guide-close-btn').onclick = close;
mask.onclick = function(e){ if(e.target === mask) close(); };
};
window.__popupDonate = function(wxQrUrl, desc, onClose) {
if (document.querySelector('.xx-donate-mask')) return;
onClose = onClose || function(){};
_injectStyles();
var descHtml = desc ? '' + _esc(desc) + '
' : '\u81ea\u613f\u6253\u8d4d\uff0c\u60a8\u7684\u652f\u6301\u662f\u6301\u7eed\u66f4\u65b0\u7684\u52a8\u529b
';
var mask = document.createElement('div');
mask.className = 'xx-mask xx-donate-mask';
mask.innerHTML = '\ud83d\udcb0 \u611f\u8c22\u652f\u6301\u811a\u672c\u6301\u7eed\u7ef4\u62a4\u66f4\u65b0
\u5fae\u4fe1\u652f\u4ed8
' + descHtml + '
';
document.body.appendChild(mask);
function close(){ mask.remove(); onClose(); }
mask.querySelector('.donate-close-btn').onclick = close;
mask.onclick = function(e){ if(e.target === mask) close(); };
};
})();
// ===================== 平台配置(东奥会计在线江西站)=====================
const PLATFORM_CONFIGS = {
// ========== 东奥会计在线 专属精细配置 ==========
dongao: {
name: "东奥会计在线",
match: /dongao\.com/,
selectors: {
// 课程列表页
courseItem: '.course-item, .kc-item',
unfinishedCourse: '.course-item.status-unfinish, .kc-item.no-study',
courseTitle: '.course-name, .kc-title',
courseEnterBtn: '.study-btn, .enter-study, .btn-study',
// 课程内章节列表
chapterItem: '.chapter-item, .section-item',
unfinishedChapter: '.chapter-item.unfinish, .section-item.no-study',
chapterTitle: '.chapter-name, .section-title',
chapterPlayBtn: '.play-btn, .btn-play',
nextChapterBtn: '.next-section-btn, .btn-next',
// 视频播放器
videoPlayer: 'video',
playerBox: '.player-box, .video-player',
progressBar: '.progress-bar-inner',
// 验证码(中途弹窗+考试前)
captchaModal: '.verify-modal, .captcha-dialog',
captchaImg: '#verifyImg, .captcha-img img',
captchaInput: '#verifyCode, .captcha-input',
captchaSubmit: '#btnSubmit, .captcha-confirm-btn',
// 考试页面
questionItem: '.question-item, .exam-question',
questionTitle: '.question-stem, .q-title',
optionSingle: '.option-item input[type="radio"] + label, .option-single',
optionMulti: '.option-item input[type="checkbox"] + label, .option-multi',
judgeTrue: '.judge-option.true, .option-true',
judgeFalse: '.judge-option.false, .option-false',
fillInput: '.fill-blank input, .blank-input',
submitPaperBtn: '#submitPaper, .btn-submit-paper',
confirmSubmitBtn: '.dialog-confirm-btn, .el-button--primary',
scoreText: '.score-number, .exam-score-value',
retryBtn: '#reExam, .btn-retake-exam'
},
special: {
chapterMode: true, // 启用章节遍历模式
captchaInterval: 180, // 每180秒检查一次验证码弹窗
antiPause: true // 强制防视频暂停
}
}
};
// ===================== 全局配置 =====================
const GLOBAL_CONFIG = {
passScore: 60,
maxRetry: 4,
videoMuted: true,
answerDelaySingle: [5000, 7000], // 单选/判断 5-7秒
answerDelayFill: [10000, 12000], // 填空 10-12秒
activityInterval: 25000, // 活跃度模拟间隔
questionBank: [
{ question: "会计的基本职能包括核算和监督。", type: "judge", answer: "正确" },
{ question: "下列各项中,属于会计要素的有", type: "multi", answer: "资产,负债,所有者权益" },
{ question: "企业会计核算的基础是", type: "single", answer: "权责发生制" },
{ question: "会计恒等式:资产 = 负债 + ________", type: "fill", answer: "所有者权益" }
]
}
// ===================== 当前平台自动识别 =====================
var SELECTORS = PLATFORM_CONFIGS.dongao.selectors;
var SPECIAL = PLATFORM_CONFIGS.dongao.special || {};
var PLATFORM_NAME = PLATFORM_CONFIGS.dongao.name;
// ===================== GM存储兼容层 =====================
var GM = {
get: function(k, def) { var v = GM_getValue("_da_" + k); return v !== undefined ? v : def; },
set: function(k, v) { GM_setValue("_da_" + k, v); },
del: function(k) { GM_setValue("_da_" + k, null); }
};
// ===================== 工具函数 =====================
const Utils = {
randomDelay(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
},
similarity(str1, str2) {
const s1 = str1.trim().replace(/[\s\W]/g, '');
const s2 = str2.trim().replace(/[\s\W]/g, '');
if (s1 === s2) return 1;
const len = Math.max(s1.length, s2.length);
if (len === 0) return 1;
let match = 0;
for (let i = 0; i < s1.length; i++) {
if (s2.includes(s1[i])) match++;
}
return match / len;
},
log(msg, type = 'info') {
const time = new Date().toLocaleTimeString();
const text = `[${time}] [${type.toUpperCase()}] ${msg}`;
console.log(text);
UI.appendLog(text);
},
waitFor(selector, timeout = 15000) {
return new Promise((resolve, reject) => {
const timer = setInterval(() => {
const el = document.querySelector(selector);
if (el && el.offsetParent !== null) {
clearInterval(timer);
resolve(el);
}
}, 500);
setTimeout(() => {
clearInterval(timer);
reject(new Error(`元素超时未找到: ${selector}`));
}, timeout);
});
},
async humanMoveTo(element) {
if (!element) return;
const rect = element.getBoundingClientRect();
const targetX = rect.left + rect.width / 2 + this.randomDelay(-12, 12);
const targetY = rect.top + rect.height / 2 + this.randomDelay(-6, 6);
const steps = this.randomDelay(10, 18);
const startX = window.mouseX || window.innerWidth / 2;
const startY = window.mouseY || window.innerHeight / 2;
for (let i = 1; i <= steps; i++) {
const x = startX + (targetX - startX) * (i / steps) + Math.sin(i / 2) * 3;
const y = startY + (targetY - startY) * (i / steps) + Math.cos(i / 2) * 2;
document.dispatchEvent(new MouseEvent('mousemove', {
clientX: x, clientY: y, bubbles: true, cancelable: true
}));
window.mouseX = x;
window.mouseY = y;
await new Promise(r => setTimeout(r, 15));
}
},
sleep(ms) {
return new Promise(r => setTimeout(r, ms));
}
}
// ===================== 反作弊模块 =====================
const AntiCheat = {
init() {
this.hijackVisibilityDeep();
this.startActivitySimulation();
this.hookVideoAntiPause();
// 平台专属反作弊
if (currentPlatform?.name === '东奥会计在线') {
this.dongaoSpecial();
}
if (currentPlatform?.name === '正保会计网校') {
this.chinaaccSpecial();
}
Utils.log(`深度反作弊模块已加载,当前平台:${currentPlatform?.name || '通用模式'}`);
},
// 深度劫持可见性与失焦检测
hijackVisibilityDeep() {
// 重写核心属性
Object.defineProperty(document, 'hidden', { value: false, writable: false, configurable: false });
Object.defineProperty(document, 'visibilityState', { value: 'visible', writable: false, configurable: false });
document.hasFocus = () => true;
window.focus = () => true;
// 捕获阶段拦截所有失焦/可见性事件
['visibilitychange', 'blur', 'focusout', 'pagehide'].forEach(evt => {
window.addEventListener(evt, e => e.stopImmediatePropagation(), true);
document.addEventListener(evt, e => e.stopImmediatePropagation(), true);
});
// 注入页面级覆盖脚本
const injectJS = `
(function(){
const _origDesc = Object.getOwnPropertyDescriptor(Document.prototype, 'hidden');
Object.defineProperty(document, 'hidden', { value: false, writable: false, configurable: false });
Object.defineProperty(document, 'visibilityState', { value: 'visible', writable: false, configurable: false });
document.hasFocus = function(){ return true; };
})();
`;
const script = document.createElement('script');
script.textContent = injectJS;
document.documentElement.appendChild(script);
script.remove();
},
// 真人活跃度模拟
startActivitySimulation() {
setInterval(() => {
if (!window.autoRunning) return;
// 随机滚动页面
window.scrollBy({
top: Utils.randomDelay(-60, 120),
behavior: 'smooth'
});
// 随机鼠标移动
const x = Utils.randomDelay(100, window.innerWidth - 100);
const y = Utils.randomDelay(100, window.innerHeight - 100);
document.dispatchEvent(new MouseEvent('mousemove', {
clientX: x, clientY: y, bubbles: true
}));
window.mouseX = x;
window.mouseY = y;
// 30%概率点击空白区域
if (Math.random() > 0.7) {
document.body.dispatchEvent(new MouseEvent('click', {
clientX: x, clientY: y, bubbles: true
}));
}
}, GLOBAL_CONFIG.activityInterval);
},
// 视频防暂停钩子
hookVideoAntiPause() {
const observer = new MutationObserver(() => {
const videos = document.querySelectorAll('video');
videos.forEach(v => {
// 强制静音
if (GLOBAL_CONFIG.videoMuted) {
v.muted = true;
v.volume = 0;
}
// 拦截暂停事件,自动恢复播放
v.addEventListener('pause', () => {
if (window.autoRunning && SPECIAL.antiPause) {
setTimeout(() => v.play().catch(() => { }), 500);
}
});
// 禁止快进检测:不修改currentTime,仅自然播放
});
});
observer.observe(document.body, { childList: true, subtree: true });
},
// 东奥专属反作弊优化
dongaoSpecial() {
Utils.log('加载东奥专属反作弊策略:播放器区域模拟交互、防中断');
setInterval(() => {
if (!window.autoRunning) return;
const player = document.querySelector(SELECTORS.playerBox);
if (player) {
// 模拟鼠标在播放器内移动,防止挂机判定
const rect = player.getBoundingClientRect();
const x = rect.left + Utils.randomDelay(50, rect.width - 50);
const y = rect.top + Utils.randomDelay(50, rect.height - 50);
player.dispatchEvent(new MouseEvent('mousemove', {
clientX: x, clientY: y, bubbles: true
}));
}
}, 15000);
},
// 正保专属反作弊优化
chinaaccSpecial() {
Utils.log('加载正保专属策略:自动同步学时、拦截中断弹窗');
if (SPECIAL.autoSyncStudy) {
setInterval(() => {
if (!window.autoRunning) return;
// 自动触发学时同步
const syncBtn = document.querySelector('.sync-btn, .tongbu_btn');
if (syncBtn) syncBtn.click();
// 自动关闭学习提示弹窗
document.querySelectorAll('.dialog-close, .close-btn').forEach(btn => {
if (btn.offsetParent !== null) btn.click();
});
}, SPECIAL.syncInterval * 1000);
}
}
}
// ===================== 验证码模块 =====================
const CaptchaSolver = {
async init() {
if (window.Tesseract) return;
const script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/tesseract.js@5/dist/tesseract.min.js';
document.head.appendChild(script);
await new Promise(resolve => script.onload = resolve);
Utils.log('OCR验证码识别引擎加载完成');
},
async solve(imgSelector) {
try {
const img = document.querySelector(imgSelector);
if (!img) return null;
await this.init();
const result = await Tesseract.recognize(img.src, 'eng', {
tessedit_char_whitelist: '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
});
const code = result.data.text.trim().replace(/\s/g, '').substring(0, 4);
Utils.log(`验证码识别结果: ${code}`);
return code;
} catch (e) {
Utils.log(`验证码识别失败: ${e.message}`, 'error');
return null;
}
},
// 检测并处理所有验证码弹窗
async checkAndSolve() {
const modal = document.querySelector(SELECTORS.captchaModal);
if (!modal || modal.offsetParent === null) return false;
Utils.log('检测到验证码弹窗,正在识别...');
const code = await this.solve(SELECTORS.captchaImg);
if (!code) return false;
const input = document.querySelector(SELECTORS.captchaInput);
const submit = document.querySelector(SELECTORS.captchaSubmit);
if (!input || !submit) return false;
await Utils.humanMoveTo(input);
input.focus();
input.value = code;
input.dispatchEvent(new Event('input', { bubbles: true }));
input.dispatchEvent(new Event('change', { bubbles: true }));
await Utils.sleep(800);
await Utils.humanMoveTo(submit);
submit.click();
Utils.log('验证码已提交');
await Utils.sleep(1500);
return true;
}
}
// ===================== 课程学习模块 =====================
const CourseLearner = {
async start() {
Utils.log('=== 自动学习模块启动(课程→章节→小节 三级遍历) ===');
UI.updateStatus('学习中', '正在扫描未完成课程...');
// 1. 获取课程列表
const courses = document.querySelectorAll(SELECTORS.unfinishedCourse);
if (courses.length === 0) {
Utils.log('课程列表页未找到未完成课程,尝试检测章节页...');
// 如果已经在课程内,直接学习章节
if (document.querySelector(SELECTORS.chapterItem)) {
await this.studyAllChapters();
return true;
}
Utils.log('未发现可学习内容,学习阶段结束');
return true;
}
Utils.log(`共找到 ${courses.length} 门未完成课程,将按顺序逐章学习`);
// 2. 遍历每门课程
for (let i = 0; i < courses.length; i++) {
if (!window.autoRunning) break;
const course = courses[i];
const title = course.querySelector(SELECTORS.courseTitle)?.textContent.trim() || `第${i + 1}门课程`;
UI.updateStatus('课程学习中', title, courses.length - i);
Utils.log(`===== 进入课程:${title} =====`);
// 进入课程
const enterBtn = course.querySelector(SELECTORS.courseEnterBtn);
await Utils.humanMoveTo(enterBtn);
enterBtn.click();
await Utils.sleep(3500);
// 学习该课程下所有章节
await this.studyAllChapters();
Utils.log(`课程《${title}》全部章节学习完成`);
// 返回课程列表
history.back();
await Utils.sleep(2500);
}
Utils.log('=== 全部课程学习完成 ===');
UI.updateStatus('学习完成', '所有课程已结束');
return true;
},
// 学习当前课程下所有章节
async studyAllChapters() {
await Utils.sleep(2000);
const chapters = document.querySelectorAll(SELECTORS.unfinishedChapter);
if (chapters.length === 0) {
Utils.log('当前课程无未完成章节');
return;
}
Utils.log(`当前课程共 ${chapters.length} 个未完成小节`);
for (let j = 0; j < chapters.length; j++) {
if (!window.autoRunning) break;
const chapter = chapters[j];
const title = chapter.querySelector(SELECTORS.chapterTitle)?.textContent.trim() || `第${j + 1}小节`;
Utils.log(`开始学习小节:${title}`);
// 点击播放
const playBtn = chapter.querySelector(SELECTORS.chapterPlayBtn) || chapter;
await Utils.humanMoveTo(playBtn);
playBtn.click();
await Utils.sleep(3000);
// 监听播放进度
await this.watchVideoProgress(title);
Utils.log(`小节完成:${title}`);
}
},
// 监听单个视频播放进度
async watchVideoProgress(title) {
return new Promise(resolve => {
const timer = setInterval(async () => {
if (!window.autoRunning) {
clearInterval(timer);
resolve();
return;
}
// 定时检查验证码
await CaptchaSolver.checkAndSolve();
// 获取播放进度
const video = document.querySelector(SELECTORS.videoPlayer);
if (video && video.duration && !isNaN(video.duration)) {
const progress = video.currentTime / video.duration;
if (progress >= 0.98) {
clearInterval(timer);
resolve();
}
}
}, 6000);
});
}
}
// ===================== 题库答题模块 =====================
const ExamTaker = {
async start() {
Utils.log('=== 自动考试模块启动 ===');
let retryCount = 0;
while (retryCount < GLOBAL_CONFIG.maxRetry && window.autoRunning) {
UI.updateStatus('考试中', `第 ${retryCount + 1} 次考试`);
Utils.log(`第 ${retryCount + 1} 次考试开始`);
// 处理入场验证码
await CaptchaSolver.checkAndSolve();
await Utils.sleep(2000);
// 获取所有题目
const questions = document.querySelectorAll(SELECTORS.questionItem);
if (questions.length === 0) {
Utils.log('未找到考试题目,页面可能未加载完成', 'error');
retryCount++;
continue;
}
Utils.log(`本次考试共 ${questions.length} 道题`);
// 逐题作答
for (let i = 0; i < questions.length; i++) {
if (!window.autoRunning) break;
await this.answerQuestion(questions[i], i + 1);
}
// 提交试卷
Utils.log('答题完成,准备提交试卷');
const submitBtn = document.querySelector(SELECTORS.submitPaperBtn);
if (submitBtn) {
await Utils.humanMoveTo(submitBtn);
submitBtn.click();
await Utils.sleep(2000);
// 确认提交弹窗
const confirmBtn = document.querySelector(SELECTORS.confirmSubmitBtn);
if (confirmBtn) {
await Utils.humanMoveTo(confirmBtn);
confirmBtn.click();
}
}
// 等待成绩加载
await Utils.sleep(5000);
const scoreEl = document.querySelector(SELECTORS.scoreText);
if (!scoreEl) {
Utils.log('无法获取考试成绩,流程异常', 'error');
retryCount++;
continue;
}
const score = parseFloat(scoreEl.textContent.trim());
Utils.log(`本次考试得分:${score} 分`);
if (score >= GLOBAL_CONFIG.passScore) {
Utils.log('🎉 考试通过!');
UI.updateStatus('考试通过', `最终得分:${score}分`);
return true;
} else {
retryCount++;
if (retryCount >= GLOBAL_CONFIG.maxRetry) {
Utils.log('已达最大重试次数,考试结束');
break;
}
Utils.log(`未及格,准备第 ${retryCount + 1} 次补考`);
const retryBtn = document.querySelector(SELECTORS.retryBtn);
if (retryBtn) {
await Utils.humanMoveTo(retryBtn);
retryBtn.click();
await Utils.sleep(3000);
} else {
Utils.log('未找到补考按钮,结束考试', 'error');
break;
}
}
}
UI.updateStatus('考试结束', '已达最大重试次数');
return false;
},
async answerQuestion(questionEl, index) {
const titleEl = questionEl.querySelector(SELECTORS.questionTitle);
if (!titleEl) return;
const title = titleEl.textContent.trim();
Utils.log(`作答第 ${index} 题:${title.substring(0, 35)}...`);
// 题库模糊匹配
const match = this.matchBank(title);
if (!match) {
Utils.log(`第 ${index} 题未匹配到答案`, 'warn');
return;
}
switch (match.type) {
case 'single':
await this.answerSingle(questionEl, match.answer);
await Utils.sleep(Utils.randomDelay(...GLOBAL_CONFIG.answerDelaySingle));
break;
case 'multi':
await this.answerMulti(questionEl, match.answer);
await Utils.sleep(Utils.randomDelay(...GLOBAL_CONFIG.answerDelaySingle));
break;
case 'judge':
await this.answerJudge(questionEl, match.answer);
await Utils.sleep(Utils.randomDelay(...GLOBAL_CONFIG.answerDelaySingle));
break;
case 'fill':
await this.answerFill(questionEl, match.answer);
await Utils.sleep(Utils.randomDelay(...GLOBAL_CONFIG.answerDelayFill));
break;
}
},
matchBank(questionText) {
let best = null;
let bestScore = 0;
for (const item of GLOBAL_CONFIG.questionBank) {
const score = Utils.similarity(questionText, item.question);
if (score > bestScore) {
bestScore = score;
best = item;
}
}
return bestScore >= 0.78 ? best : null;
},
async answerSingle(el, answer) {
const options = el.querySelectorAll(SELECTORS.optionSingle);
for (const opt of options) {
if (opt.textContent.trim().includes(answer.trim())) {
await Utils.humanMoveTo(opt);
opt.click();
return;
}
}
},
async answerMulti(el, answer) {
const answers = answer.split(/[,,、]/).map(s => s.trim());
const options = el.querySelectorAll(SELECTORS.optionMulti);
for (const opt of options) {
const text = opt.textContent.trim();
if (answers.some(a => text.includes(a))) {
await Utils.humanMoveTo(opt);
opt.click();
await Utils.sleep(350);
}
}
},
async answerJudge(el, answer) {
const isTrue = answer.includes('正确') || answer.includes('对') || answer.includes('√');
const selector = isTrue ? SELECTORS.judgeTrue : SELECTORS.judgeFalse;
const btn = el.querySelector(selector);
if (btn) {
await Utils.humanMoveTo(btn);
btn.click();
}
},
async answerFill(el, answer) {
const input = el.querySelector(SELECTORS.fillInput);
if (input) {
await Utils.humanMoveTo(input);
input.focus();
input.value = answer;
input.dispatchEvent(new Event('input', { bubbles: true }));
input.dispatchEvent(new Event('change', { bubbles: true }));
}
}
}
// ===================== 两轮答题模块(来自zhengbaodati.js)====================
var CAStorage = {
prefix: "ca2_",
get: function(k, def) { try { var v = localStorage.getItem(this.prefix + k); return v !== null ? v : def; } catch(e) { return def; } },
set: function(k, v) { try { localStorage.setItem(this.prefix + k, v); return true; } catch(e) { return false; } },
del: function(k) { try { localStorage.removeItem(this.prefix + k); } catch(e) {} }
};
function detectPageType() {
const html = document.body.innerHTML;
const text = document.body.textContent;
const hasQuestions = document.querySelector('input[name^="qt"]');
const hasSubmit = document.querySelector('#PostBtn');
const hasAnswer = text.includes('正确答案') || text.includes('【正确答案】');
if (hasQuestions && hasSubmit && !hasAnswer) {
return 'exam'; // 答题页面
}
if (hasAnswer) {
return 'result'; // 答案页面
}
return 'unknown';
}
function getPaperId() {
const html = document.documentElement.innerHTML;
// 方法1: 使用ShowNum
let match = html.match(/var ShowNum = '([^']+)'/);
if (match && match[1]) {
return 'paper_' + match[1].replace(/[^0-9,]/g, '').substring(0, 80);
}
// 方法2: 使用TIDList1
match = html.match(/var TIDList1 = '([^']+)'/);
if (match && match[1]) {
return 'paper_' + match[1].replace(/[^0-9,]/g, '').substring(0, 80);
}
return 'paper_default';
}
function getAllQuestionIds() {
const html = document.documentElement.innerHTML;
const ids = [];
// 优先使用ShowNum
let match = html.match(/var ShowNum = '([^']+)'/);
if (match) {
const extracted = match[1].split(',').filter(id => id.trim() && /^\d+$/.test(id.trim()));
ids.push(...extracted);
}
// 备用: 使用TIDList
if (ids.length === 0) {
for (let i = 1; i <= 4; i++) {
match = html.match(new RegExp(`var TIDList${i} = '([^']+)'`));
if (match) {
const extracted = match[1].split(',').filter(id => id.trim() && /^\d+$/.test(id.trim()));
ids.push(...extracted);
}
}
}
console.log(`找到 ${ids.length} 道题:`, ids);
return ids;
}
function getQuestionType(qid) {
const inputs = document.querySelectorAll(`input[name='qt${qid}']`);
if (inputs.length === 0) return null;
if (inputs[0].type === 'checkbox') return 'multiple';
if (inputs.length === 2) {
const values = Array.from(inputs).map(i => i.value);
if (values.includes('Y') && values.includes('N')) {
return 'judge';
}
}
return 'single';
}
function clearAllAnswers() {
const inputs = document.querySelectorAll('input[type="radio"], input[type="checkbox"]');
inputs.forEach(input => input.checked = false);
console.log('✓ 已清空所有选项');
}
function randomFillAnswer(qid, type) {
if (type === 'judge') {
const answer = Math.random() > 0.5 ? 'Y' : 'N';
const input = document.querySelector(`input[name='qt${qid}'][value='${answer}']`);
if (input) input.checked = true;
} else if (type === 'single') {
const inputs = document.querySelectorAll(`input[name='qt${qid}']`);
if (inputs.length > 0) {
inputs[Math.floor(Math.random() * inputs.length)].checked = true;
}
} else if (type === 'multiple') {
const inputs = document.querySelectorAll(`input[name='qt${qid}']`);
const count = Math.min(inputs.length, Math.floor(Math.random() * 2) + 2);
const selected = new Set();
while (selected.size < count) {
selected.add(Math.floor(Math.random() * inputs.length));
}
selected.forEach(idx => inputs[idx].checked = true);
}
}
function firstSubmit() {
console.log('%c===== 第一步: 获取答案 =====', 'color: #FF9800; font-weight: bold');
clearAllAnswers();
showMessage('正在随机填充并提交...', 'info');
const qids = getAllQuestionIds();
const paperId = getPaperId();
// 保存试卷信息
CAStorage.set('current_paper_id', paperId);
CAStorage.set(paperId + '_question_ids', JSON.stringify(qids));
CAStorage.set('is_getting_answer', 'true');
console.log('试卷ID:', paperId);
console.log('题目数:', qids.length);
// 随机填充
qids.forEach(qid => {
const type = getQuestionType(qid);
if (type) randomFillAnswer(qid, type);
});
setTimeout(() => {
showMessage('提交中,请等待跳转...', 'info');
const btn = document.querySelector('#PostBtn');
if (btn) {
btn.click();
} else {
showMessage('找不到提交按钮', 'error');
}
}, 1000);
}
function parseAnswersFromResultPage() {
console.log('%c===== 第二步: 解析答案 =====', 'color: #4CAF50; font-weight: bold');
const paperId = CAStorage.get('current_paper_id');
if (!paperId) {
console.error('未找到试卷ID');
showMessage('错误: 未找到试卷ID', 'error');
return;
}
const qidsJson = CAStorage.get(paperId + '_question_ids', '[]');
let qids = [];
try {
qids = JSON.parse(qidsJson);
} catch (e) {
console.error('解析题目列表失败:', e);
return;
}
if (qids.length === 0) {
console.error('题目列表为空');
return;
}
console.log(`试卷: ${paperId}`);
console.log(`题数: ${qids.length}`);
const answers = {};
let parsed = 0;
qids.forEach((qid, idx) => {
console.log(`\n[${idx + 1}/${qids.length}] 题目 ${qid}`);
// 查找题目锚点
const anchor = document.querySelector(`a[name='md${qid}']`);
if (!anchor) {
console.warn(' ⚠️ 找不到锚点');
return;
}
// 找到容器
const container = anchor.closest('tr') || anchor.closest('div.showclass');
if (!container) {
console.warn(' ⚠️ 找不到容器');
return;
}
const text = container.textContent || container.innerText;
// 匹配答案: 【正确答案】D 或 【正确答案】AB
const patterns = [
/【正确答案】([A-Z]+|对|错)/,
/正确答案】([A-Z]+|对|错)/,
/正确答案[::]([A-Z]+|对|错)/i
];
let found = null;
for (let pattern of patterns) {
const match = text.match(pattern);
if (match && match[1]) {
found = match[1].trim();
break;
}
}
if (!found) {
console.warn(' ⚠️ 未找到答案');
return;
}
// 处理答案
let answer = found;
if (answer === '对') {
answer = 'Y';
console.log(` ✓ 判断题: Y (对)`);
} else if (answer === '错') {
answer = 'N';
console.log(` ✓ 判断题: N (错)`);
} else if (/^[A-Z]{2,}$/.test(answer)) {
answer = answer.split('');
console.log(` ✓ 多选题: [${answer.join(', ')}]`);
} else {
console.log(` ✓ 单选题: ${answer}`);
}
answers[qid] = answer;
parsed++;
});
console.log(`\n解析完成: ${parsed}/${qids.length} 题`);
console.table(answers);
if (parsed > 0) {
const cacheKey = paperId + '_answers';
CAStorage.set(cacheKey, JSON.stringify(answers));
CAStorage.set('is_getting_answer', 'false');
showMessage(`✅ 成功保存 ${parsed}/${qids.length} 题答案!\n\n5秒后自动返回`, 'success');
addReturnButton();
setTimeout(() => {
console.log('返回答题页面...');
window.history.back();
}, 5000);
} else {
showMessage('未能解析到任何答案', 'error');
addReturnButton();
}
}
function autoFillWithCache() {
console.log('%c===== 第三步: 使用答案 =====', 'color: #4CAF50; font-weight: bold');
const paperId = getPaperId();
const cacheKey = paperId + '_answers';
const cached = CAStorage.get(cacheKey);
console.log(`试卷: ${paperId}`);
console.log(`缓存: ${cached ? '存在' : '不存在'}`);
if (!cached || cached === '{}') {
showMessage('❌ 没有缓存答案\n\n请先"获取正确答案"', 'warning');
return;
}
let answers = {};
try {
answers = JSON.parse(cached);
} catch (e) {
console.error('解析缓存失败:', e);
showMessage('缓存数据错误', 'error');
return;
}
if (Object.keys(answers).length === 0) {
showMessage('缓存为空', 'warning');
return;
}
console.log('缓存答案:', answers);
clearAllAnswers();
const qids = getAllQuestionIds();
let filled = 0;
let missing = 0;
qids.forEach(qid => {
const answer = answers[qid];
if (!answer) {
console.warn(`题 ${qid}: 无缓存`);
missing++;
return;
}
const type = getQuestionType(qid);
if (type === 'multiple' && Array.isArray(answer)) {
answer.forEach(opt => {
const input = document.querySelector(`input[name='qt${qid}'][value='${opt}']`);
if (input) input.checked = true;
});
filled++;
console.log(`题 ${qid}: [${answer.join(',')}]`);
} else if (typeof answer === 'string') {
const input = document.querySelector(`input[name='qt${qid}'][value='${answer}']`);
if (input) {
input.checked = true;
filled++;
console.log(`题 ${qid}: ${answer}`);
} else {
console.error(`题 ${qid}: 找不到选项 ${answer}`);
}
}
});
console.log(`填充: ${filled}/${qids.length}, 缺失: ${missing}`);
if (filled > 0) {
showMessage(`✅ 已填充 ${filled}/${qids.length} 题\n${missing > 0 ? `⚠️ ${missing}题无缓存\n` : ''}\n请检查后手动提交`, 'success');
} else {
showMessage('未能填充任何题目', 'error');
}
}
function addReturnButton() {
const btn = document.createElement('button');
btn.textContent = '🔙 立即返回';
btn.style.cssText = `
position: fixed;
bottom: 20px;
right: 20px;
padding: 15px 30px;
background: #4CAF50;
color: white;
border: none;
border-radius: 8px;
font-size: 16px;
font-weight: bold;
cursor: pointer;
z-index: 99999;
box-shadow: 0 4px 15px rgba(0,0,0,0.3);
`;
btn.onclick = () => window.history.back();
document.body.appendChild(btn);
}
function showMessage(msg, type = 'info') {
const colors = {
success: '#4CAF50',
info: '#2196F3',
warning: '#FF9800',
error: '#F44336'
};
const old = document.getElementById('autoAnswerMessage');
if (old) old.remove();
const div = document.createElement('div');
div.id = 'autoAnswerMessage';
div.style.cssText = `
position: fixed;
top: 20px;
left: 50%;
transform: translateX(-50%);
background: ${colors[type]};
color: white;
padding: 20px 30px;
border-radius: 8px;
font-size: 16px;
z-index: 99999;
box-shadow: 0 4px 15px rgba(0,0,0,0.3);
text-align: center;
white-space: pre-line;
font-weight: bold;
`;
div.textContent = msg;
document.body.appendChild(div);
if (type !== 'error') {
setTimeout(() => div.remove(), 8000);
}
}
function addControlPanel() {
const paperId = getPaperId();
const cacheKey = paperId + '_answers';
const cached = CAStorage.get(cacheKey);
let cacheCount = 0;
if (cached && cached !== '{}') {
try {
cacheCount = Object.keys(JSON.parse(cached)).length;
} catch (e) {}
}
const panel = document.createElement('div');
panel.style.cssText = `
position: fixed;
top: 120px;
right: 20px;
background: white;
border: 3px solid #2196F3;
border-radius: 10px;
padding: 20px;
z-index: 10000;
box-shadow: 0 4px 15px rgba(0,0,0,0.3);
width: 260px;
`;
const qcount = getAllQuestionIds().length;
panel.innerHTML = `
🤖 猪猪的自动答题助手
📋 题数: ${qcount}
💾 缓存: ${cacheCount} 题
📖 使用说明:
1️⃣ 清空表单
2️⃣ 获取正确答案(自动提交)
3️⃣ 等待解析(自动返回)
4️⃣ 使用缓存答案(自动填充)
5️⃣ 检查后手动提交 ✅
`;
document.body.appendChild(panel);
// 事件绑定
document.getElementById('clearBtn').onclick = () => {
clearAllAnswers();
showMessage('✓ 已清空', 'success');
};
document.getElementById('getBtn').onclick = () => {
if (confirm('将随机填充并提交以获取答案\n\n确定继续吗?')) {
firstSubmit();
}
};
document.getElementById('fillBtn').onclick = () => {
autoFillWithCache();
};
document.getElementById('viewBtn').onclick = () => {
const data = CAStorage.get(cacheKey);
if (data && data !== '{}') {
try {
const obj = JSON.parse(data);
console.log('%c=== 缓存答案 ===', 'color: #2196F3; font-weight: bold');
console.table(obj);
showMessage(`已在控制台输出\n共 ${Object.keys(obj).length} 题`, 'info');
} catch (e) {
showMessage('缓存格式错误', 'error');
}
} else {
showMessage('暂无缓存', 'warning');
}
};
document.getElementById('delBtn').onclick = () => {
if (confirm('确定清除缓存?')) {
CAStorage.set(cacheKey, '{}');
showMessage('已清除', 'success');
setTimeout(() => location.reload(), 1000);
}
};
}
function init() {
const pageType = detectPageType();
console.log('页面类型:', pageType);
if (pageType === 'exam') {
console.log('✓ 答题页面');
// 检查是否从答案页返回
if (CAStorage.get('is_getting_answer') === 'true') {
console.log('检测到从答案页返回,清空表单');
setTimeout(() => {
clearAllAnswers();
CAStorage.set('is_getting_answer', 'false');
}, 500);
}
addControlPanel();
} else if (pageType === 'result') {
console.log('✓ 答案页面');
showMessage('检测到答案页面\n正在解析...', 'info');
setTimeout(() => {
parseAnswersFromResultPage();
}, 2000);
} else {
console.log('⚠️ 未识别的页面');
}
}
// ===================== 统一UI面板 =====================
const UI = {
panel: null,
logBox: null,
statusText: null,
courseText: null,
logExpanded: false,
init() {
this.createPanel();
this.bindDrag();
AntiCheat.init();
},
createPanel() {
const panel = document.createElement('div');
panel.id = 'auto-study-panel';
panel.style.cssText = `
position: fixed; top: 100px; right: 20px; z-index: 2147483647;
width: 300px; background: #ffffff; border-radius: 10px;
box-shadow: 0 6px 20px rgba(0,0,0,0.18);
font-family: "Microsoft YaHei", "PingFang SC", sans-serif;
font-size: 13px; user-select: none; overflow: hidden;
border: 1px solid #e5e7eb;
`;
panel.innerHTML = `
继教助手 | ${currentPlatform?.name || '通用'}
×
▼ 展开运行日志
`;
document.body.appendChild(panel);
this.panel = panel;
this.logBox = panel.querySelector('#log-container');
this.statusText = panel.querySelector('#status-text');
this.courseText = panel.querySelector('#course-text');
// 绑定事件
panel.querySelector('#btn-full-auto').addEventListener('click', () => this.startFullAuto());
panel.querySelector('#btn-exam-only').addEventListener('click', () => this.startExamOnly());
panel.querySelector('#panel-close-btn').addEventListener('click', () => {
window.autoRunning = false;
panel.remove();
});
panel.querySelector('#log-toggle-btn').addEventListener('click', () => {
this.logExpanded = !this.logExpanded;
this.logBox.style.display = this.logExpanded ? 'block' : 'none';
panel.querySelector('#log-toggle-btn').textContent =
this.logExpanded ? '▲ 收起运行日志' : '▼ 展开运行日志';
});
},
// 拖拽功能
bindDrag() {
const titleBar = this.panel.querySelector('#panel-title-bar');
let dragging = false;
let offsetX = 0;
let offsetY = 0;
titleBar.addEventListener('mousedown', e => {
dragging = true;
const rect = this.panel.getBoundingClientRect();
offsetX = e.clientX - rect.left;
offsetY = e.clientY - rect.top;
e.preventDefault();
});
document.addEventListener('mousemove', e => {
if (!dragging) return;
this.panel.style.left = (e.clientX - offsetX) + 'px';
this.panel.style.top = (e.clientY - offsetY) + 'px';
this.panel.style.right = 'auto';
});
document.addEventListener('mouseup', () => {
dragging = false;
});
},
updateStatus(status, course, remain = '') {
this.statusText.textContent = status;
this.courseText.textContent = course + (remain ? ` (剩余${remain}项)` : '');
},
appendLog(text) {
if (!this.logBox) return;
const line = document.createElement('div');
line.textContent = text;
line.style.marginBottom = '3px';
this.logBox.appendChild(line);
this.logBox.scrollTop = this.logBox.scrollHeight;
},
async startFullAuto() {
if (window.autoRunning) return;
window.autoRunning = true;
Utils.log('全程自动模式启动:课程学习→自动考试 全流程无人干预');
const studySuccess = await CourseLearner.start();
if (studySuccess && window.autoRunning) {
await ExamTaker.start();
}
},
async startExamOnly() {
if (window.autoRunning) return;
window.autoRunning = true;
Utils.log('单独考试模式启动');
await ExamTaker.start();
}
}
// ===================== 主程序入口 =====================
function main() {
if (!PLATFORM_NAME) { return; }
console.log("[da] 东奥会计在线刷课助手启动,当前平台:", PLATFORM_NAME);
AntiCheat.init();
setTimeout(function() {
if (window.__popupGuide) {
window.__popupGuide([
{ text: "脚本仅作研究用途,风险自担,转滚压状态将被检测", ok: false },
{ text: "建议选择适当倍速(推荐 4~8倍),过快可能被检测", ok: true },
{ text: "如有打赏意愿,点击底部「打赏支持」按钮,屏幕外我们无法提供资源维护", ok: true },
{ text: "继续教育需要按照官方要求完成,请努力学习", ok: true }
], "我已知晓", function() {
setTimeout(function() {
if (window.__popupDonate) {
if (!GM.get("donate_shown", false)) {
window.__popupDonate("https://i.postimg.cc/QdhkjkFG/wxpay.png", "自愿打赏,您的支持是持续更新的动力", function() { GM.set("donate_shown", true); });
}
}
}, 2000);
});
}
}, 1500);
UI.init();
init();
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", main);
} else {
setTimeout(main, 1000);
}
})();
// 东奥江西站 | 客服微信:wkds995