// ==UserScript== // @name 北航研究生抢课助手(增强版) // @namespace https://www.wanghaiyang.site // @version 0.2.3 // @description 北航研究生抢课助手 - 支持自动捕获课程信息 // @author 王海洋 // @match https://yjsxk.buaa.edu.cn/* // @grant none // @require https://code.jquery.com/jquery-3.6.0.min.js // @run-at document-start // @license MIT // @downloadURL none // ==/UserScript== (function() { 'use strict'; // 创建操作框样式 const style = ` .control-panel { position: fixed; top: 20px; right: 20px; background: white; padding: 15px; border-radius: 8px; box-shadow: 0 0 10px rgba(0,0,0,0.1); z-index: 10000; cursor: move; user-select: none; min-width: 300px; } .control-panel * { cursor: auto; } .control-panel input { margin: 5px 0; padding: 5px; width: 200px; } .control-panel button { margin: 10px 0; padding: 5px 15px; background: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; } .control-panel button:disabled { background: #cccccc; } .result-area { margin-top: 10px; padding: 10px; border: 1px solid #ddd; max-height: 150px; overflow-y: auto; } .capture-area { margin-top: 10px; padding: 10px; border: 1px solid #ddd; background: #f5f5f5; } .capture-area table { width: 100%; border-collapse: collapse; } .capture-area td { padding: 4px; border-bottom: 1px solid #eee; } .capture-area td:last-child { color: #1890ff; cursor: pointer; text-align: right; user-select: all; } .auto-fill-btn { background: #1890ff !important; margin-top: 5px !important; width: 100%; } `; // 创建样式元素 const styleElement = document.createElement('style'); styleElement.textContent = style; document.head.appendChild(styleElement); // 保存配置函数 function saveConfig(kcdm, kclx, interval, refreshInterval, autoStart = false) { const config = { kcdm, kclx, interval, refreshInterval, autoStart, timestamp: Date.now() }; localStorage.setItem('buaaCourseSniperConfig', JSON.stringify(config)); } // 获取配置函数 function getConfig() { const configStr = localStorage.getItem('buaaCourseSniperConfig'); return configStr ? JSON.parse(configStr) : null; } // 添加拖拽功能 function makeDraggable(panel) { let isDragging = false; let currentX; let currentY; let initialX; let initialY; panel.addEventListener('mousedown', function(e) { if (e.target.tagName === 'INPUT' || e.target.tagName === 'BUTTON') { return; } isDragging = true; const style = window.getComputedStyle(panel); const matrix = new WebKitCSSMatrix(style.transform); initialX = e.clientX - (matrix.m41 || 0); initialY = e.clientY - (matrix.m42 || 0); panel.style.transition = 'none'; }); document.addEventListener('mousemove', function(e) { if (!isDragging) return; e.preventDefault(); currentX = e.clientX - initialX; currentY = e.clientY - initialY; const panelRect = panel.getBoundingClientRect(); const viewportWidth = window.innerWidth; const viewportHeight = window.innerHeight; if (currentX < 0) { currentX = 0; } else if (currentX + panelRect.width > viewportWidth) { currentX = viewportWidth - panelRect.width; } if (currentY < 0) { currentY = 0; } else if (currentY + panelRect.height > viewportHeight) { currentY = viewportHeight - panelRect.height; } panel.style.transform = `translate(${currentX}px, ${currentY}px)`; }); document.addEventListener('mouseup', function() { if (!isDragging) return; isDragging = false; panel.style.transition = 'transform 0.2s'; localStorage.setItem('panelPosition', JSON.stringify({x: currentX, y: currentY})); }); } // 添加请求拦截器 const originalXHR = window.XMLHttpRequest; window.XMLHttpRequest = function() { const xhr = new originalXHR(); const originalSend = xhr.send; xhr.send = function(body) { try { if (body) { const params = new URLSearchParams(body); const bjdm = params.get('bjdm'); const lx = params.get('lx'); if (bjdm && lx) { const time = new Date().toLocaleTimeString(); document.getElementById('timeValue').textContent = time; document.getElementById('bjdmValue').textContent = bjdm; document.getElementById('lxValue').textContent = lx; console.log("fetch 解析成功"); } } } catch (error) { console.error('解析请求参数时出错:', error); } return originalSend.apply(this, arguments); }; return xhr; }; // 添加fetch拦截器 const originalFetch = window.fetch; window.fetch = async function(url, options) { if (options && options.body) { try { const params = new URLSearchParams(options.body); const bjdm = params.get('bjdm'); const lx = params.get('lx'); if (bjdm && lx) { const time = new Date().toLocaleTimeString(); document.getElementById('timeValue').textContent = time; document.getElementById('bjdmValue').textContent = bjdm; document.getElementById('lxValue').textContent = lx; console.log("fetch 解析成功"); } } catch (error) { console.error('解析fetch请求参数时出错:', error); } } return originalFetch.apply(this, arguments); }; // 发送请求函数 function sendRequest(kcdm, kclx) { try { const csrfToken = document.querySelector('#csrfToken'); if (!csrfToken) { console.error('未找到csrfToken'); return; } const data = { bjdm: kcdm, lx: kclx, csrfToken: csrfToken.value }; const urlEncodedData = new URLSearchParams(data).toString(); const url = 'https://yjsxk.buaa.edu.cn/yjsxkapp/sys/xsxkappbuaa/xsxkCourse/choiceCourse.do?_=' + String(Date.now()); originalFetch(url, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }, body: urlEncodedData, credentials: 'include' }) .then(response => response.json()) .then(data => { const resultArea = document.getElementById('resultArea'); const time = new Date().toLocaleTimeString(); if (data.code == 1) { resultArea.innerHTML = `${time}: 抢课成功
` + resultArea.innerHTML; // 抢课成功后,停止抢课 stopSniper(); } else { resultArea.innerHTML = `${time}: ${JSON.stringify(data)}
` + resultArea.innerHTML; } }) .catch(error => { console.error('请求失败:', error); const resultArea = document.getElementById('resultArea'); const time = new Date().toLocaleTimeString(); resultArea.innerHTML = `${time}: 错误: ${error}
` + resultArea.innerHTML; }); } catch (error) { console.error('请求发送失败:', error); } } // 声明全局变量 let intervalId = null; let refreshTimeout = null; // 开始抢课函数 function startSniper(kcdm, kclx, interval, refreshInterval) { try { const csrfToken = document.querySelector('#csrfToken'); if (!csrfToken) { console.error('未找到csrfToken,等待1秒后重试'); setTimeout(() => startSniper(kcdm, kclx, interval, refreshInterval), 1000); return; } saveConfig(kcdm, kclx, interval, refreshInterval, true); const startBtn = document.getElementById('startBtn'); const stopBtn = document.getElementById('stopBtn'); if (startBtn && stopBtn) { startBtn.disabled = true; stopBtn.disabled = false; } if (intervalId) { clearInterval(intervalId); intervalId = null; } if (refreshTimeout) { clearTimeout(refreshTimeout); refreshTimeout = null; } intervalId = setInterval(() => sendRequest(kcdm, kclx), interval); refreshTimeout = setTimeout(() => { saveConfig(kcdm, kclx, interval, refreshInterval, true); window.location.reload(); }, refreshInterval * 1000); } catch (error) { console.error('启动失败:', error); setTimeout(() => startSniper(kcdm, kclx, interval, refreshInterval), 1000); } } // 停止抢课函数 function stopSniper() { try { if (intervalId) { clearInterval(intervalId); intervalId = null; } if (refreshTimeout) { clearTimeout(refreshTimeout); refreshTimeout = null; } const config = getConfig(); if (config) { saveConfig(config.kcdm, config.kclx, config.interval, config.refreshInterval, false); } const startBtn = document.getElementById('startBtn'); const stopBtn = document.getElementById('stopBtn'); if (startBtn && stopBtn) { startBtn.disabled = false; stopBtn.disabled = true; } const resultArea = document.getElementById('resultArea'); const time = new Date().toLocaleTimeString(); resultArea.innerHTML = `${time}: 已停止抢课
` + resultArea.innerHTML; } catch (error) { console.error('停止失败:', error); } } // 初始化面板函数 function initializePanel() { const config = getConfig(); if (config) { const kcdmInput = document.getElementById('kcdm'); const kclxInput = document.getElementById('kclx'); const intervalInput = document.getElementById('interval'); const refreshIntervalInput = document.getElementById('refreshInterval'); if (kcdmInput && kclxInput && intervalInput && refreshIntervalInput) { kcdmInput.value = config.kcdm; kclxInput.value = config.kclx; intervalInput.value = config.interval; refreshIntervalInput.value = config.refreshInterval || 60; if (config.autoStart) { setTimeout(() => { startSniper(config.kcdm, config.kclx, config.interval, config.refreshInterval); }, 1000); } } } } // 创建面板函数 function createAndInitPanel() { if (document.querySelector('.control-panel')) { return; } const panel = document.createElement('div'); panel.className = 'control-panel'; panel.innerHTML = `

捡漏抢课助手(抢别人退掉的课)

记得感谢王海洋学长

最近捕获的课程信息(手动选课即可捕获信息):
时间: -
课程代码: -
类型: -








等待开始...
`; document.body.appendChild(panel); // 添加自动填充按钮事件 document.getElementById('autoFillBtn').addEventListener('click', () => { const bjdm = document.getElementById('bjdmValue').textContent; const lx = document.getElementById('lxValue').textContent; if (bjdm !== '-' && lx !== '-') { document.getElementById('kcdm').value = bjdm; document.getElementById('kclx').value = lx; } }); // 绑定开始和停止按钮事件 document.getElementById('startBtn').addEventListener('click', () => { const kcdm = document.getElementById('kcdm').value; const kclx = document.getElementById('kclx').value; const interval = parseInt(document.getElementById('interval').value); const refreshInterval = parseInt(document.getElementById('refreshInterval').value); if (!kcdm || !kclx) { alert('请填写完整信息!'); return; } if (refreshInterval < 10) { alert('刷新间隔不能小于10秒!'); return; } startSniper(kcdm, kclx, interval, refreshInterval); }); document.getElementById('stopBtn').addEventListener('click', stopSniper); // 应用拖拽功能 makeDraggable(panel); // 恢复上次保存的位置 const savedPosition = localStorage.getItem('panelPosition'); if (savedPosition) { const position = JSON.parse(savedPosition); panel.style.transform = `translate(${position.x}px, ${position.y}px)`; } // 初始化面板配置 initializePanel(); } // 在页面关闭或刷新前停止抢课 window.addEventListener('beforeunload', function() { // 注释掉以允许自动重启 // if (intervalId || refreshTimeout) { // stopSniper(); // } }); // 确保在DOM加载完成后创建面板 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', createAndInitPanel); } else { createAndInitPanel(); } })();