// ==UserScript== // @name 华医网全能助手(最新版) // @namespace https://github.com/yourname/ // @version 2.2.0 // @description 自动静音播放、自动切换视频、屏蔽弹窗、智能考试、支持“选择答题模式”,修复课程完成页面跳转 // @match *://*.91huayi.com/course_ware/course_ware_polyv.aspx?* // @match *://*.91huayi.com/course_ware/course_ware_cc.aspx?* // @match *://*.91huayi.com/pages/exam.aspx?* // @match *://*.91huayi.com/pages/exam_result.aspx?* // @match *://*.91huayi.com/pages/course.aspx* // @match *://*.91huayi.com/course_ware/course_list.aspx* // @match *://*.91huayi.com/course_ware/course.aspx* // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @grant GM_openInTab // @grant GM_addStyle // @run-at document-idle // @license MIT // ==/UserScript== (function () { 'use strict'; // ==================== 全局配置 ==================== const MODE_KEY = 'hy_mode_v2'; let currentMode = GM_getValue(MODE_KEY, 'video_exam'); // 'video_exam', 'video_only', 'exam_only' // 考试相关存储 const STORAGE = { test: 'hy_test', result: 'hy_result', testAnswer: 'hy_testAnswer', rightAnswer: 'hy_rightAnswer', allAnswer: 'hy_allAnswer', thisTitle: 'hy_thisTitle', }; const DELAY = { submit: 6800, retry: 3500, examEnter: 7000, random: 5000, }; // ==================== 工具函数 ==================== function log(msg) { console.log(`[华医助手] ${msg}`); } function sleep(ms) { return new Promise(r => setTimeout(r, ms)); } function randomDelay(min = 1000, max = 3000) { return Math.floor(Math.random() * (max - min + 1) + min); } function getBaseUrl() { return window.location.origin; } function getCwidFromUrl() { return new URLSearchParams(window.location.search).get('cwid'); } // ==================== 调试窗口 ==================== function createDebugWindow() { if (document.getElementById('hy-debug-box')) return; const box = document.createElement('div'); box.id = 'hy-debug-box'; box.style.cssText = ` position: fixed; top: 12px; left: 12px; z-index: 9999999; width: 420px; background: #1e293b; color: #e2e8f0; padding: 16px; border-radius: 16px; font-size: 14px; font-family: -apple-system, "PingFang SC", sans-serif; box-shadow: 0 8px 30px rgba(0,0,0,0.5); border: 1px solid #475569; max-height: 80vh; display: flex; flex-direction: column; `; box.innerHTML = `
🩺 华医全能助手
`; document.body.appendChild(box); document.getElementById('hy-close-debug').onclick = () => box.remove(); document.getElementById('hy-mode-select').onchange = function () { currentMode = this.value; GM_setValue(MODE_KEY, currentMode); debugLog(`模式切换为: ${this.options[this.selectedIndex].text}`); }; document.getElementById('hy-force-next').onclick = jumpToNextCourse; document.getElementById('hy-force-exam').onclick = startExamQueueManually; window.debugLog = function (msg) { const logEl = document.getElementById('hy-log'); if (!logEl) return; const time = new Date().toLocaleTimeString(); logEl.value = `[${time}] ${msg}\n` + logEl.value; if (logEl.value.length > 2000) logEl.value = logEl.value.slice(0, 2000); }; debugLog('🚀 脚本启动成功'); } function debugLog(msg) { if (window.debugLog) window.debugLog(msg); else console.log(`[DEBUG] ${msg}`); } // ==================== 弹窗拦截 ==================== function initPopupBlocker() { setInterval(() => { try { if (window.player && typeof window.player.j2s_pauseVideo === 'function') { window.player.j2s_pauseVideo = () => { debugLog('拦截 pauseVideo'); }; } if (typeof window.initialSign !== 'undefined') { window.initialSign = () => { debugLog('自动签到'); if (typeof window.addPlaySign === 'function') window.addPlaySign(); window.isInitialSign = true; }; } } catch (e) {} }, 500); setInterval(() => { try { const ask = document.querySelector('.pv-ask-head'); if (ask) { const skip = document.querySelector('.pv-ask-skip'); if (skip) { skip.click(); debugLog('已跳过课堂问答'); } } const sign = document.querySelector('.signBtn'); if (sign) { sign.click(); debugLog('已跳过签到'); } const tip = document.querySelector('button[onclick="closeBangZhu()"], button[onclick="closeProcessbarTip()"]'); if (tip) { tip.click(); debugLog('已关闭温馨提示'); } const know = document.querySelector('input.rig_btn[value="知道了"]'); if (know && document.getElementById('div_processbar_tip')?.style.display !== 'none') { know.click(); } const modeDialog = document.querySelector('.layui-layer-content'); if (modeDialog) { const text = modeDialog.innerText; if (text.includes('选择答题模式') || text.includes('考试模式') || text.includes('练习模式')) { const btns = modeDialog.parentElement.querySelectorAll('.layui-layer-btn0, .layui-layer-btn1'); for (let btn of btns) { if (btn.innerText.includes('正式考试') || btn.innerText.includes('考试')) { btn.click(); debugLog('已选择“正式考试”模式'); break; } } } } } catch (e) {} try { const video = document.querySelector('video'); if (video && video.paused && video.currentTime > 0 && video.currentTime < video.duration - 1) { video.muted = true; video.volume = 0; video.play().catch(() => {}); debugLog('视频意外暂停,已恢复'); } } catch (e) {} }, 3000); const observer = new MutationObserver(mutations => { for (const m of mutations) { for (const node of m.addedNodes) { if (node.nodeType !== 1) continue; const text = node.innerText || ''; if (text.includes('选择答题模式')) { const btns = node.querySelectorAll('.layui-layer-btn0, .layui-layer-btn1'); for (let btn of btns) { if (btn.innerText.includes('正式考试') || btn.innerText.includes('考试')) { btn.click(); debugLog('自动选择“正式考试”'); break; } } } if (node.classList?.contains('study_diaog')) { node.remove(); debugLog('移除 study_diaog'); } } } }); if (document.body) observer.observe(document.body, { childList: true, subtree: true }); } // ==================== 视频播放 ==================== function setupVideo(video) { if (video._hySetup) return; video._hySetup = true; video.muted = true; video.volume = 0; const playVideo = () => { if (video.readyState >= 2) { video.play().catch(() => {}); } else { video.addEventListener('canplay', () => video.play(), { once: true }); } }; setTimeout(playVideo, 1000); setInterval(() => { if (video.paused && video.currentTime > 0 && video.currentTime < video.duration - 1) { video.muted = true; video.volume = 0; video.play().catch(() => {}); debugLog('守护恢复播放'); } }, 5000); video.addEventListener('ended', () => { debugLog('视频播放结束'); setTimeout(() => { // 检测是否已完成(通过状态文字) const stateEl = document.querySelector('i[id="top_play"]')?.parentElement?.nextElementSibling?.nextElementSibling?.nextElementSibling; let state = stateEl ? stateEl.innerText : ''; if (state === '已完成' || state === '待考试') { if (currentMode === 'video_exam' && state === '待考试') { const cwid = getCwidFromUrl(); if (cwid) { const examUrl = `/pages/exam.aspx?cwid=${cwid}`; debugLog('进入考试: ' + examUrl); setTimeout(() => { window.location.href = examUrl; }, randomDelay(1000, 3000)); return; } } // 否则跳转下一课(可能跳转到课程详情页) jumpToNextCourse(); } else { // 如果状态不是已完成,可能页面已跳转,尝试通用跳转 jumpToNextCourse(); } }, 2000); }, { once: true }); } function observeVideos() { const obs = new MutationObserver(() => { document.querySelectorAll('video').forEach(v => setupVideo(v)); }); if (document.body) obs.observe(document.body, { childList: true, subtree: true }); document.querySelectorAll('video').forEach(v => setupVideo(v)); } // ==================== 课程完成页面处理(新增) ==================== function handleCourseCompletePage() { // 查找“立即学习”按钮 let learnBtn = document.querySelector('input[value="立即学习"]'); if (!learnBtn) learnBtn = document.querySelector('a[value="立即学习"]'); if (!learnBtn) { // 通过文本查找 const btns = Array.from(document.querySelectorAll('button, input, a')); for (let btn of btns) { if (btn.innerText && btn.innerText.trim() === '立即学习') { learnBtn = btn; break; } } } if (learnBtn) { const href = learnBtn.getAttribute('href'); if (href) { debugLog('点击“立即学习”跳转到: ' + href); window.location.href = href; } else { debugLog('点击“立即学习”按钮'); learnBtn.click(); } return true; } return false; } // ==================== 跳课逻辑(增强) ==================== function jumpToNextCourse() { debugLog('尝试跳转到下一课程'); // 1. 如果是课程完成页面,优先点击“立即学习” if (handleCourseCompletePage()) { return; } // 2. 如果是课程列表页 if (window.location.pathname.includes('/pages/course.aspx') || window.location.pathname.includes('/course_ware/course_list.aspx')) { const items = document.querySelectorAll('.course, .lis-inside-content'); for (let item of items) { if (item.innerText.includes('选修') || item.innerText.includes('互动')) continue; const status = item.querySelector('span')?.innerText || item.querySelector('button')?.innerText || ''; if (status.includes('已完成') || status.includes('待考试')) continue; const link = item.querySelector('a[href*="course_ware"]') || item.querySelector('h2[onclick]'); if (link) { let url = link.href || link.getAttribute('onclick')?.match(/href='([^']+)'/)?.[1]; if (url) { if (url.startsWith('http')) window.location.href = url; else window.location.href = getBaseUrl() + url; debugLog('跳转到: ' + url); return; } } } debugLog('没有找到未学课程'); return; } // 3. 视频页面左侧列表(如果未跳转,尝试从目录找) const items = document.querySelectorAll('li.lis-inside-content'); if (items.length === 0) { debugLog('未找到目录列表'); return; } let currentIndex = -1; for (let i = 0; i < items.length; i++) { if (items[i].querySelector('#top_play')) { currentIndex = i; break; } } let start = currentIndex + 1; for (let i = start; i < items.length; i++) { const li = items[i]; if (li.innerText.includes('选修') || li.innerText.includes('互动')) continue; const btn = li.querySelector('button'); const text = btn ? btn.innerText.trim() : ''; if (text === '未学习' || text === '学习中' || text === '') { const h2 = li.querySelector('h2'); if (h2) { const onclick = h2.getAttribute('onclick'); if (onclick) { const match = onclick.match(/window\.location\.href='([^']+)'/); if (match) { window.location.href = match[1]; debugLog('跳转下一节: ' + match[1]); return; } } const a = h2.querySelector('a'); if (a && a.href) { window.location.href = a.href; debugLog('跳转下一节: ' + a.href); return; } } } } debugLog('没有更多未学课程'); } // ==================== 考试模块 ==================== function loadData(key) { return JSON.parse(GM_getValue(key, 'null')); } function saveData(key, data) { GM_setValue(key, JSON.stringify(data)); } function removeData(key) { GM_deleteValue(key); } function normalize(txt) { return txt ? txt.trim().replace(/^\d+、\s*/, '').replace(/[()()\s]/g, '') : ''; } async function handleExamPage() { debugLog('开始考试'); const questions = document.querySelectorAll('.tablestyle'); if (questions.length === 0) { debugLog('未找到题目'); return; } const allAnswers = loadData(STORAGE.allAnswer) || {}; const title = document.title || '未知章节'; let rightAnswers = loadData(STORAGE.rightAnswer) || {}; if (Object.keys(rightAnswers).length === 0 && allAnswers[title]) { rightAnswers = allAnswers[title]; } let currentTest = loadData(STORAGE.test) || {}; let testAnswers = {}; for (let index = 0; index < questions.length; index++) { const qEl = questions[index]; const qText = normalize(qEl.querySelector('.q_name')?.innerText || ''); if (!qText) continue; const options = qEl.querySelectorAll('tbody label'); if (options.length === 0) continue; if (rightAnswers[qText]) { const answerText = rightAnswers[qText]; for (let opt of options) { if (normalize(opt.innerText) === answerText) { opt.click(); debugLog(`题目 "${qText}" 已选答案: ${answerText}`); break; } } continue; } let selected = false; const wrongs = loadData(STORAGE.result) || {}; const wrongList = wrongs[qText] || []; for (let opt of options) { const content = normalize(opt.innerText); if (!wrongList.includes(content)) { opt.click(); testAnswers[qText] = content; if (!currentTest[qText]) currentTest[qText] = []; currentTest[qText].push(content); selected = true; debugLog(`试错选择: "${qText}" -> "${content}"`); break; } } if (!selected && options.length > 0) { options[0].click(); testAnswers[qText] = normalize(options[0].innerText); debugLog(`所有选项试过,选第一个: "${qText}"`); } await sleep(randomDelay(500, 1500)); } saveData(STORAGE.test, currentTest); saveData(STORAGE.testAnswer, testAnswers); setTimeout(() => { const btn = document.getElementById('btn_submit'); if (btn) { btn.click(); debugLog('已交卷'); } else { debugLog('找不到提交按钮'); } }, DELAY.submit + randomDelay(0, DELAY.random)); } async function handleResultPage() { debugLog('处理考试结果'); const resultText = document.querySelector('.tips_text')?.innerText || ''; const isPassed = resultText.includes('考试通过') || resultText.includes('完成项目学习'); const wrongs = loadData(STORAGE.result) || {}; const testAnswers = loadData(STORAGE.testAnswer) || {}; const rightAnswers = loadData(STORAGE.rightAnswer) || {}; const allAnswers = loadData(STORAGE.allAnswer) || {}; const title = document.title || '未知章节'; if (isPassed) { debugLog('考试通过,记录正确答案'); for (let q in testAnswers) { if (!wrongs[q]) { rightAnswers[q] = testAnswers[q]; } } if (!allAnswers[title]) allAnswers[title] = {}; Object.assign(allAnswers[title], rightAnswers); saveData(STORAGE.allAnswer, allAnswers); saveData(STORAGE.rightAnswer, rightAnswers); removeData(STORAGE.test); removeData(STORAGE.testAnswer); removeData(STORAGE.result); const cwid = getCwidFromUrl(); if (cwid) { const videoUrl = `/course_ware/course_ware_polyv.aspx?cwid=${cwid}`; debugLog('跳回视频: ' + videoUrl); setTimeout(() => { window.location.href = videoUrl; }, 2000); } else { debugLog('未找到cwid,跳转到课程列表'); setTimeout(() => { window.location.href = '/pages/course.aspx'; }, 2000); } } else { debugLog('考试未通过,记录错题'); const items = document.querySelectorAll('.state_cour_lis'); let newWrongs = {}; for (let item of items) { const qTitle = item.querySelector('.state_lis_text:first-of-type')?.title; if (!qTitle) continue; const ansText = item.querySelectorAll('.state_lis_text')[1]?.innerText?.replace(/【您的答案:|】/g, '') || ''; const isCorrect = item.querySelector('.state_error')?.src?.includes('bar_img.png') || false; const qKey = normalize(qTitle); if (!isCorrect && qKey) { if (!newWrongs[qKey]) newWrongs[qKey] = []; newWrongs[qKey].push(normalize(ansText)); } } for (let q in newWrongs) { if (!wrongs[q]) wrongs[q] = []; for (let ans of newWrongs[q]) { if (!wrongs[q].includes(ans)) wrongs[q].push(ans); } } saveData(STORAGE.result, wrongs); const retryBtn = document.querySelector('input[value="重新考试"]'); if (retryBtn) { setTimeout(() => retryBtn.click(), DELAY.retry + randomDelay(0, DELAY.random)); debugLog('点击重新考试'); } else { debugLog('未找到重新考试按钮'); } } } function startExamQueueManually() { debugLog('手动触发考试扫描'); if (window.location.pathname.includes('/pages/course.aspx')) { const items = document.querySelectorAll('.course'); for (let item of items) { if (item.innerText.includes('选修') || item.innerText.includes('互动')) continue; const status = item.querySelector('span')?.innerText || ''; if (status.includes('待考试')) { const link = item.querySelector('a[href*="exam"]'); if (link) { window.location.href = link.href; debugLog('进入待考课程'); return; } } } debugLog('没有待考课程'); } else { const cwid = getCwidFromUrl(); if (cwid) { window.location.href = `/pages/exam.aspx?cwid=${cwid}`; debugLog('直接进入考试'); } } } // ==================== 主路由 ==================== function main() { createDebugWindow(); initPopupBlocker(); const path = window.location.pathname; // 处理课程完成页面(包含“本课件已学习完毕”或“立即学习”按钮) if (path.includes('/pages/course.aspx') || path.includes('/course_ware/course.aspx') || path.includes('/course_ware/course_list.aspx')) { debugLog('课程页面'); // 优先处理完成页面跳转 if (handleCourseCompletePage()) { return; } // 否则走原有逻辑 if (currentMode !== 'exam_only') { setTimeout(jumpToNextCourse, 2000); } else { debugLog('仅考试模式,不自动跳视频'); } if (currentMode === 'video_exam' || currentMode === 'exam_only') { setTimeout(startExamQueueManually, 3000); } return; } if (path.includes('course_ware_polyv.aspx') || path.includes('course_ware_cc.aspx')) { debugLog('视频页面'); if (currentMode === 'exam_only') { debugLog('仅考试模式,不播放视频'); return; } observeVideos(); const examBtn = document.getElementById('jrks'); if (examBtn) { const check = setInterval(() => { if (examBtn.getAttribute('disabled') === null) { clearInterval(check); debugLog('考试按钮已激活'); if (currentMode === 'video_exam') { const cwid = getCwidFromUrl(); if (cwid) { setTimeout(() => { window.location.href = `/pages/exam.aspx?cwid=${cwid}`; }, DELAY.examEnter + randomDelay(0, DELAY.random)); } } else { setTimeout(jumpToNextCourse, 3000); } } }, 2000); } setTimeout(() => { const qBtn = document.querySelector('.pv-quality-btn'); if (qBtn) { qBtn.click(); setTimeout(() => { const f = Array.from(document.querySelectorAll('.pv-quality-select div')).find(d => d.innerText === '流畅'); if (f) f.click(); }, 500); } }, 3000); return; } if (path.includes('/exam.aspx')) { debugLog('考试页面'); if (currentMode !== 'video_only') { handleExamPage(); } else { debugLog('仅视频模式,跳过考试'); } return; } if (path.includes('/exam_result.aspx')) { debugLog('考试结果页面'); if (currentMode !== 'video_only') { handleResultPage(); } else { debugLog('仅视频模式,跳过考试结果处理'); } return; } debugLog('未知页面,不执行任务'); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', main); } else { main(); } })();