// ==UserScript==
// @name 四川执业药师-金航联平台
// @namespace http://tampermonkey.net/
// @version 1.5.0
// @description 针对 sc.mtnet.com.cn(四川执业药师-金航联平台) 网站设计的自动化刷课脚本,实现自动导航、静音、后台播放以及功能更强大的AI自动考试助手。请注意倍速不可用!!!
// @author Coren
// @match *://sc.mtnet.com.cn/*
// @grant GM_addStyle
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_xmlhttpRequest
// @connect api.deepseek.com
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// ===================================================================================
// --- 脚本配置 (Script Configuration) ---
// ===================================================================================
const CONFIG = {
VIDEO_PLAYBACK_RATE: 16.0, // 默认视频播放倍速
API_PROMPT: "你是一个乐于助人的问题回答助手。聚焦于执业药师相关的内容,请根据用户提出的问题,提供准确、清晰的解答。注意回答时仅仅包括答案,不允许其他额外任何解释,输出为一行一道题目的答案,答案只能是题目序号:字母选项,不能包含文字内容。单选输出示例:1.A。多选输出示例:1.ABC。",
};
// --- 脚本全局状态 (Global States) ---
let isServiceActive = GM_getValue('mtnet_service_active', true);
let currentPlaybackRate = GM_getValue('mtnet_playback_rate', CONFIG.VIDEO_PLAYBACK_RATE);
let isMuted = GM_getValue('mtnet_is_muted', true);
let deepseekApiKey = GM_getValue('deepseek_api_key', '');
let isAutoExamMode = GM_getValue('mtnet_auto_exam_mode', false);
let isPanelCreated = false;
let chapterCheckInterval = null;
let storedAnswers = null;
// ===================================================================================
// --- 辅助函数 (Helper Functions) ---
// ===================================================================================
function findElementByText(selector, text) {
try {
return Array.from(document.querySelectorAll(selector)).find(el => el.innerText.trim().includes(text.trim()));
} catch (e) {
console.error(`[脚本错误] 查找元素失败: selector="${selector}", text="${text}"`, e);
return null;
}
}
function clickElement(element, description) {
if (element && typeof element.click === 'function') {
console.log(`[脚本操作] 正在点击: ${description}`, element);
element.click();
return true;
} else {
console.warn(`[脚本警告] 尝试点击一个不存在或不可点击的元素: ${description}`, element);
return false;
}
}
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// ===================================================================================
// --- UI面板管理 (UI Panel Management) ---
// ===================================================================================
function createControlPanel() {
if (isPanelCreated) return;
isPanelCreated = true;
GM_addStyle(`
#control-panel { position: fixed; bottom: 20px; right: 20px; width: 240px; background-color: #fff; border: 1px solid #007bff; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.15); z-index: 10000; font-family: 'Microsoft YaHei', sans-serif; }
#control-panel-header { padding: 8px 12px; background-color: #007bff; color: white; cursor: move; user-select: none; border-top-left-radius: 7px; border-top-right-radius: 7px;}
#control-panel-content { padding: 15px; display: flex; flex-direction: column; align-items: center; gap: 10px; }
.panel-btn { padding: 8px 16px; font-size: 14px; color: white; border: none; border-radius: 5px; cursor: pointer; transition: background-color 0.3s; width: 100%; box-sizing: border-box; }
.service-btn-active { background-color: #28a745; }
.service-btn-paused { background-color: #dc3545; }
.mute-btn-on { background-color: #ffc107; color: black; }
.mute-btn-off { background-color: #6c757d; }
.nav-btn { background-color: #17a2b8; }
.exam-btn { background-color: #fd7e14; }
.panel-divider { width: 100%; height: 1px; background-color: #eee; margin: 5px 0; }
.setting-row { display: flex; flex-direction: column; width: 100%; align-items: center; gap: 5px; }
.setting-row > label { font-size: 14px; font-weight: bold; }
.speed-slider-container { display: flex; align-items: center; width: 100%; gap: 10px; }
#speed-slider { flex-grow: 1; }
#speed-display { font-weight: bold; font-size: 14px; color: #007bff; min-width: 45px; text-align: right; }
#api-key-input { width: 100%; padding: 6px; border-radius: 4px; border: 1px solid #ccc; box-sizing: border-box; }
`);
const panel = document.createElement('div');
panel.id = 'control-panel';
panel.innerHTML = `
`;
document.body.appendChild(panel);
// --- Event Listeners ---
const serviceBtn = document.getElementById('service-toggle-btn');
const muteBtn = document.getElementById('mute-toggle-btn');
const speedSlider = document.getElementById('speed-slider');
const speedDisplay = document.getElementById('speed-display');
const apiKeyInput = document.getElementById('api-key-input');
const navSpecializedBtn = document.getElementById('nav-specialized-video-btn');
const navPublicBtn = document.getElementById('nav-public-video-btn');
const navSpecializedExamBtn = document.getElementById('nav-specialized-exam-btn');
const navPublicExamBtn = document.getElementById('nav-public-exam-btn'); // 新增按钮
const updateServiceButton = (isActive) => { serviceBtn.innerText = isActive ? '服务运行中' : '服务已暂停'; serviceBtn.className = 'panel-btn ' + (isActive ? 'service-btn-active' : 'service-btn-paused'); };
const updateMuteButton = (isMutedState) => { muteBtn.innerText = isMutedState ? '静音模式' : '正常音量'; muteBtn.className = 'panel-btn ' + (isMutedState ? 'mute-btn-on' : 'mute-btn-off'); };
updateServiceButton(isServiceActive);
updateMuteButton(isMuted);
serviceBtn.onclick = () => { GM_setValue('mtnet_service_active', !isServiceActive); window.location.reload(); };
muteBtn.onclick = async () => {
isMuted = !isMuted;
GM_setValue('mtnet_is_muted', isMuted);
updateMuteButton(isMuted);
const video = await findVideo();
if (video) video.muted = isMuted;
};
speedSlider.addEventListener('input', () => { speedDisplay.textContent = `x${speedSlider.value}`; });
speedSlider.addEventListener('change', () => { currentPlaybackRate = parseFloat(speedSlider.value); GM_setValue('mtnet_playback_rate', currentPlaybackRate); });
apiKeyInput.addEventListener('change', () => { deepseekApiKey = apiKeyInput.value.trim(); GM_setValue('deepseek_api_key', deepseekApiKey); });
navSpecializedBtn.onclick = () => { GM_setValue('mtnet_target_task', 'specialized_video'); window.location.href = 'http://sc.mtnet.com.cn/user/courses'; };
navPublicBtn.onclick = () => { GM_setValue('mtnet_target_task', 'public_video'); window.location.href = 'http://sc.mtnet.com.cn/user/courses'; };
navSpecializedExamBtn.onclick = () => { GM_setValue('mtnet_target_task', 'specialized_exam'); window.location.href = 'http://sc.mtnet.com.cn/user/courses'; };
// 新增按钮的点击事件
navPublicExamBtn.onclick = () => { GM_setValue('mtnet_target_task', 'public_exam'); window.location.href = 'http://sc.mtnet.com.cn/user/courses'; };
makeDraggable(panel, document.getElementById('control-panel-header'));
}
function makeDraggable(panel, header) {
let isDragging = false, offsetX, offsetY;
header.addEventListener('mousedown', (e) => { isDragging = true; offsetX = e.clientX - panel.offsetLeft; offsetY = e.clientY - panel.offsetTop; header.style.cursor = 'grabbing'; });
document.addEventListener('mousemove', (e) => { if (!isDragging) return; panel.style.left = `${e.clientX - offsetX}px`; panel.style.top = `${e.clientY - offsetY}px`; });
document.addEventListener('mouseup', () => { isDragging = false; header.style.cursor = 'move'; });
}
// ===================================================================================
// --- 页面逻辑处理 (Page-Specific Logic) ---
// ===================================================================================
async function handleCourseListPage() {
console.log('[脚本流程] 进入课程列表页面处理流程...');
try {
const targetTask = GM_getValue('mtnet_target_task', 'specialized_video');
// 逻辑已足够通用,无需修改
const courseType = targetTask.includes('specialized') ? '专业科目' : '公需科目';
const isExamTask = targetTask.includes('exam');
const filterType = isExamTask ? '待考试' : '未完成';
const actionButtonSelector = isExamTask ? '.indexTextBtn.onlineTest' : '.indexTextBtn';
const actionButtonText = isExamTask ? '在线考试' : '进入学习';
const courseTab = findElementByText('span.gxkn', courseType);
if (courseTab && !courseTab.classList.contains('active')) { clickElement(courseTab, `"${courseType}" 标签`); await delay(2000); }
const filterBtn = findElementByText('a.screenItem', filterType);
if (filterBtn && !filterBtn.classList.contains('active')) { clickElement(filterBtn, `"${filterType}" 筛选按钮`); await delay(2000); }
const courseList = document.querySelectorAll('.indexCourseListSLi');
for (const course of courseList) {
const actionBtn = course.querySelector(actionButtonSelector);
if (actionBtn && actionBtn.innerText.trim() === actionButtonText) {
clickElement(actionBtn, `第一个课程的 "${actionButtonText}" 按钮`); return;
}
}
console.log(`[脚本完成] 在 "${courseType}" 的 "${filterType}" 列表中未找到任何可操作的课程。`);
} catch (error) { console.error('[脚本错误] 处理课程列表页面时出错:', error); }
}
async function handleVideoPage() {
console.log('[脚本流程] 进入视频学习页面处理流程...');
if (chapterCheckInterval) clearInterval(chapterCheckInterval);
try {
chapterCheckInterval = setInterval(() => {
if (!window.location.href.includes('/video/')) { clearInterval(chapterCheckInterval); return; }
const totalProgressElement = document.querySelector('.courseProgress .gkjd span[style*="color: rgb(255, 148, 102)"]');
if (totalProgressElement && totalProgressElement.innerText.trim().includes('100')) {
clearInterval(chapterCheckInterval); window.location.href = 'http://sc.mtnet.com.cn/user/courses'; return;
}
const chapters = document.querySelectorAll('.detailRightC .chapterList li');
const nextChapter = Array.from(chapters).find(ch => !ch.querySelector('span.chapterPro.floatR.currPlay')?.innerText.trim().includes('100'));
if (nextChapter && !nextChapter.classList.contains('active')) {
clickElement(nextChapter.querySelector('a'), '下一个未完成的章节');
} else if (!nextChapter) {
clearInterval(chapterCheckInterval); window.location.href = 'http://sc.mtnet.com.cn/user/courses';
}
}, 5000);
} catch (error) { console.error('[脚本错误] 处理视频学习页面时出错:', error); }
}
async function handleExamPage() {
console.log('[脚本流程] 进入考试页面处理流程...');
const existingPanel = document.getElementById('exam-helper-panel');
if (existingPanel) existingPanel.remove();
createExamHelperUI();
}
// ===================================================================================
// --- AI考试助手 (AI Exam Helper) ---
// ===================================================================================
function createExamHelperUI() {
GM_addStyle(`
#exam-helper-panel { position: fixed; top: 20px; left: 20px; width: 450px; background: #f9f9f9; border: 1px solid #007bff; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.2); z-index: 10001; font-family: 'Microsoft YaHei', sans-serif; display: flex; flex-direction: column; max-height: 90vh; }
#exam-helper-header { padding: 10px 15px; background-color: #007bff; color: white; cursor: move; border-top-left-radius: 7px; border-top-right-radius: 7px; font-size: 16px; flex-shrink: 0; }
#exam-helper-content { padding: 15px; display: flex; flex-direction: column; gap: 15px; overflow-y: auto; }
#exam-action-btn { color: white; padding: 10px; border: none; border-radius: 5px; font-size: 16px; cursor: pointer; transition: background-color 0.3s; }
#exam-action-btn:disabled { background-color: #ccc; cursor: not-allowed; }
#exam-status { margin-top: 5px; padding: 10px; background-color: #e9ecef; border-radius: 5px; text-align: center; font-size: 14px; color: #495057; min-height: 20px; }
.auto-mode-switch { display: flex; align-items: center; gap: 8px; background: #e9ecef; padding: 8px; border-radius: 5px; }
.display-area { border: 1px solid #ddd; background: #fff; padding: 10px; border-radius: 5px; max-height: 200px; overflow-y: auto; }
.display-area h4 { margin: 0 0 10px 0; font-size: 14px; border-bottom: 1px solid #eee; padding-bottom: 5px; }
.display-area pre { white-space: pre-wrap; word-wrap: break-word; font-size: 12px; margin: 0; }
#manual-ask-container { display: flex; flex-direction: column; gap: 10px; }
#manual-question-input { width: 100%; min-height: 60px; padding: 8px; border-radius: 4px; border: 1px solid #ccc; box-sizing: border-box; }
#manual-ask-btn { background-color: #5a6268; color: white; padding: 8px; border: none; border-radius: 5px; cursor: pointer; }
`);
const panel = document.createElement('div');
panel.id = 'exam-helper-panel';
panel.innerHTML = `