您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
简单的学习通讲座抢名额脚本:右上角 GUI,可实时开关、可调刷新间隔、日志面板。
// ==UserScript== // @name 学习通自动抢讲座 // @version 0.7.0 // @description 简单的学习通讲座抢名额脚本:右上角 GUI,可实时开关、可调刷新间隔、日志面板。 // @license MIT // @author 和川 // @match *.chaoxing.com/page/*/show // @icon https://statics.chaoxing.com/favicon.ico // @grant GM_notification // @grant GM_setValue // @grant GM_getValue // @run-at document-idle // @namespace https://greasyfork.org/users/1471327 // ==/UserScript== (function () { 'use strict'; /* ---------- 状态持久化 ---------- */ let enabled = GM_getValue('enabled', true); // 自动抢开关 let logVisible = GM_getValue('logVisible', false); // 日志面板显隐 let refreshInterval = GM_getValue('refreshInterval', 3000); // 刷新间隔(ms) /* ---------- UI: 右上角控制面板 ---------- */ const ctrlWrap = document.createElement('div'); ctrlWrap.style.cssText = ` position: fixed; top: 20px; right: 20px; z-index: 2147483647; display: flex; gap: 8px;`; const toggleBtn = document.createElement('button'); Object.assign(toggleBtn.style, baseBtnStyle(enabled ? '#4caf50' : '#f44336')); toggleBtn.textContent = `自动抢: ${enabled ? 'ON' : 'OFF'}`; ctrlWrap.appendChild(toggleBtn); const logBtn = document.createElement('button'); Object.assign(logBtn.style, baseBtnStyle('#2196f3')); logBtn.textContent = logVisible ? '隐藏日志' : '显示日志'; ctrlWrap.appendChild(logBtn); const refreshInput = document.createElement('input'); refreshInput.type = 'number'; refreshInput.min = '500'; refreshInput.step = '500'; refreshInput.value = refreshInterval; refreshInput.title = '刷新间隔(ms)'; refreshInput.style.cssText = ` width: 80px; padding: 8px 4px; border: 1px solid #ccc; border-radius: 6px;`; ctrlWrap.appendChild(refreshInput); const refreshLabel = document.createElement('span'); refreshLabel.textContent = ' ms'; refreshLabel.style.color = '#fff'; ctrlWrap.appendChild(refreshLabel); document.body.appendChild(ctrlWrap); /* ---------- 日志面板 ---------- */ const logPanel = document.createElement('pre'); logPanel.id = 'cxg-log-panel'; logPanel.style.cssText = ` position: fixed; top: 60px; right: 20px; width: 360px; height: 220px; background: rgba(0,0,0,.8); color: #eee; padding: 8px; font-size: 12px; line-height: 1.4; border-radius: 6px; overflow-y: auto; white-space: pre-wrap; z-index: 2147483646; display: ${logVisible ? 'block' : 'none'};`; document.body.appendChild(logPanel); /* ---------- 事件绑定 ---------- */ toggleBtn.addEventListener('click', () => switchEnable(!enabled)); logBtn.addEventListener('click', () => { logVisible = !logVisible; GM_setValue('logVisible', logVisible); logPanel.style.display = logVisible ? 'block' : 'none'; logBtn.textContent = logVisible ? '隐藏日志' : '显示日志'; }); refreshInput.addEventListener('change', () => { const val = parseInt(refreshInput.value, 10); if (!isNaN(val) && val >= 500) { refreshInterval = val; GM_setValue('refreshInterval', refreshInterval); log('Refresh interval set to ' + refreshInterval + ' ms'); if (enabled) { stopLoop(); startLoop(true); } } else { refreshInput.value = refreshInterval; } }); /* ---------- 辅助函数 ---------- */ function baseBtnStyle(bg) { return { padding: '8px 14px', borderRadius: '6px', background: bg, color: '#fff', border: 'none', cursor: 'pointer', fontSize: '14px', boxShadow: '0 2px 6px rgba(0,0,0,.2)', }; } function notify(text) { if (typeof GM_notification === 'function') { GM_notification({ text, title: '学习通抢讲座', timeout: 5000 }); } } function log(msg) { const line = `[${new Date().toLocaleTimeString()}] ${msg}`; console.log('%c[CX-Log] ' + msg, 'color:#03a9f4'); logPanel.textContent += line + '\n'; logPanel.scrollTop = logPanel.scrollHeight; } function switchEnable(state) { enabled = state; GM_setValue('enabled', enabled); toggleBtn.textContent = `自动抢: ${enabled ? 'ON' : 'OFF'}`; toggleBtn.style.background = enabled ? '#4caf50' : '#f44336'; if (enabled) { notify('已开启自动抢讲座'); log('Auto-enroll ENABLED'); startLoop(true); } else { notify('已暂停自动抢讲座'); log('Auto-enroll DISABLED'); stopLoop(); } } /* ---------- 业务逻辑 ---------- */ let loopID = null; let refreshCount = 0; function tryEnroll() { const btnList = Array.from(document.querySelectorAll('a[class*="255-btn"]')); const enrollBtn = btnList.find((b) => b.textContent.includes('报名参与')); const signedBtn = btnList.find((b) => b.textContent.includes('去签到')); const fullBtn = btnList.find((b) => b.textContent.includes('名额已满')); if (signedBtn) { notify('🎉 已成功抢到讲座名额,快去签到!'); log('Got the seat! Stopping.'); switchEnable(false); return; } if (enrollBtn) { log('Found enroll button, clicking…'); enrollBtn.click(); notify('🤖 正在尝试报名…'); setTimeout(() => location.reload(), 1500); return; } if (fullBtn) { log('名额已满,触发熔断,停止刷新'); notify('名额已满,已停止自动抢'); switchEnable(false); return; } log('No available seat yet (' + ++refreshCount + ')'); } function startLoop(immediate = false) { if (loopID !== null) return; log('Start loop (interval ' + refreshInterval + ' ms)'); const intervalFn = () => { tryEnroll(); log('Next reload in ' + refreshInterval + ' ms'); setTimeout(() => { if (enabled) location.reload(); }, refreshInterval); }; if (immediate) intervalFn(); loopID = setInterval(intervalFn, refreshInterval); } function stopLoop() { if (loopID !== null) { clearInterval(loopID); loopID = null; log('Loop stopped'); } } /* ---------- 初始化 ---------- */ log(`Script init — enabled=${enabled}, interval=${refreshInterval} ms`); if (enabled) startLoop(); })();