您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
自动抢课
// ==UserScript== // @name SWUST 抢课+ // @author lengthmin & Paranoid_AF // @namespace dean.swust.Qiangke // @version 1.0 // @description 自动抢课 // @require https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js // @include https://matrix.dean.swust.edu.cn/acadmicManager/index.cfm?event=chooseCourse:* // @grant GM_getValue // @grant GM_setValue // @grant GM_addValueChangeListener // ==/UserScript== function parseSearch() { var parms = {}; //把location.search解析成对象parms var query = location.search.substring(1).split("&"); for (var i = 0; i < query.length; i++) { var pos = query[i].split("="); if (pos[0]) { parms[pos[0]] = pos[1] ? pos[1] : ""; } } return parms; } var storageKey = "SWUST.Paranoid_AF.CourseList"; var logKey = "SWUST.Paranoid_AF.ShowLog"; var scriptName = "SWUST抢课+"; var subjectInfo = []; var currentPage = { DEFAULT_EVENT: "开始选课", fixupTask: "补修", programTask: "计划课程", commonTask: "全校通选课", retakeTask: "重修", sportTask: "体育", NON_COURSE_PICK: "非选课", }; var setting = { // 给查看每节课详细信息一点缓冲时间 wait: 100, // 抢课刷新间隔 timeout: 500, // 当前抢了多少次 count: 0, }; $(document).ready(function () { var params = parseSearch(); var event = params["event"]; let page_; if (event.startsWith("chooseCourse:")) { page_ = event.replace("chooseCourse:", ""); if (page_ === "chargeByTerm" || page_ === "courseTable") { page_ = "NON_COURSE_PICK"; } } else { page_ = "NON_COURSE_PICK"; } setting.div = $( `<div style=" width: 330px; position: fixed; top: 0; right: 0; z-index: 99999; background-color: rgba(255, 255, 255, 0.6); overflow-x: auto;"> <span style="font-size: medium;"></span> <div style="font-size: medium;">${currentPage[page_]}页面</div> ${ page_ === "DEFAULT_EVENT" || page_ === "NON_COURSE_PICK" ? `` : ` <button style="margin-right: 10px; font-weight: 800; color: #0078D7; font-size: 16px;" id="startQK">开始选课</button> ` } <button style="margin-right: 10px; cursor: pointer; color: #0078D7;" id="newCourse"> 添加课程 </button> <div style="max-height: 300px; overflow-y: auto;"> 抢课列表: <table border="1" style="font-size: 12px;"> <thead> <tr> <th style="min-width: 100px;">课程</th> <th style="min-width: 60px;">教师</th> <th style="min-width: 60px;">时间</th> <th>操作</th> </tr> </thead> <tbody> </tbody> </table> </div> </div> <div id="logWindow" style="bottom: calc(25px - 40vh);transition: bottom 1s;width: 330px; position: fixed; left: 0; z-index: 99999; background-color: #1f4359; overflow-x: auto; height: 40vh;"> <div style="font-size: medium; color: #fff;"> <span style="float:left">输出窗口</span> <span id="logSwitch" style="float:right;cursor: pointer; user-select: none;"></span> <div id="logContent" style="left: calc((100% - 97%) / 2); bottom: 0; width: 97%; position: absolute; background-color: #fff; height: calc(40vh - 25px); color: #000; overflow-y: scroll; font-size: 12px;"> </div> </div> </div> ` ).appendTo("body"); refreshList(); refreshLogPos(); $("#newCourse").click((e) => { let result = inputCourse(); if (result !== null) { addCourse(result); refreshList(); } }); $("#logSwitch").click((e) => { let logEnabled = GM_getValue(logKey, true); GM_setValue(logKey, !logEnabled); refreshLogPos(); }); $("#startQK").click(() => { if (subjectInfo.length == 0) { pushLog("没有添加课!", 2); return; } if (setting.loop) { clearInterval(setting.loop); delete setting.loop; $("#startQK")[0].innerText = "继续抢课"; } else { $("#startQK")[0].innerText = "暂停抢课"; setting.loop = setInterval(findClass, setting.timeout); } }); GM_addValueChangeListener( storageKey, (name, old_value, new_value, remote) => { if (new_value !== subjectInfo) { refreshList(); } } ); pushLog(`${scriptName} 已初始化完毕`, 3); }); function pushLog(info, type = 0) { let text = "信息"; switch (type) { case 0: text = "信息"; break; case 1: text = "警告"; break; case 2: text = "错误"; break; case 3: text = "成功"; break; default: } let date = new Date(); $("#logContent").append( `<p>[${text}] ${date.toLocaleTimeString("zh", { hour12: false, })} - ${info}</p>` ); $("#logContent").scrollTop($("#logContent")[0].scrollHeight); } function refreshLogPos() { let logEnabled = GM_getValue(logKey, true); if (logEnabled) { $("#logSwitch").html("👇收起 "); $("#logWindow").css("bottom", "0"); } else { $("#logSwitch").html("👆展开 "); $("#logWindow").css("bottom", "calc(25px - 40vh)"); } } function refreshList() { let subjectInfo = GM_getValue(storageKey, []); setting.div.find("tbody").html(` <tr> <td colspan="2" style="display: none;"></td> </tr> `); subjectInfo.map((value, index) => { setting.div.find("tbody").append( ` <tr index="${index}"> <td> ${value.subjectName} </td> <td> ${value.teacherName} </td> <td> ${value.time} </td> <td> <button style="margin-top: -2px; color: #0078D7; font-size: 11.5px; cursor: pointer;" class="editCourse">编辑</button> <button style="margin-top: -2px; color: #0078D7; font-size: 11.5px;cursor: pointer;" class="delCourse">移除</button> </td> </tr>` ); }); $(".delCourse").on("click", (e) => { let targetIndex = Number( e.currentTarget.parentElement.parentElement.attributes.index.value ); if (confirm("确认删除吗?")) { delCourse(targetIndex); } refreshList(); }); $(".editCourse").on("click", (e) => { let targetIndex = Number( e.currentTarget.parentElement.parentElement.attributes.index.value ); let result = inputCourse({ course: subjectInfo[targetIndex].subjectName, teacher: subjectInfo[targetIndex].teacherName, time: subjectInfo[targetIndex].time, }); if (result !== null) { editCourse(targetIndex, result); refreshList(); } }); } function addCourse(info) { subjectInfo.push({ subjectName: info.course, // 要抢的科目名字 teacherName: info.teacher, // 要抢的科目教师 time: info.time, }); GM_setValue(storageKey, subjectInfo); } function editCourse(index, info) { subjectInfo[index] = { subjectName: info.course, // 要抢的科目名字 teacherName: info.teacher, // 要抢的科目教师 time: info.time, }; GM_setValue(storageKey, subjectInfo); } function delCourse(index) { subjectInfo.splice(index, 1); GM_setValue(storageKey, subjectInfo); } function inputCourse(defaultInfo = { course: "", teacher: ".*", time: ".*" }) { let result = {}; let course = prompt( "输入课程名称(支持正则表达式)\n可以少写,但不要写错", defaultInfo.course ); if (course !== null) { if (course === "") { alert("课程名称不能为空!"); return null; } else { result.course = course; } } else { return null; } let teacher = prompt( "输入教师名称(支持正则表达式)\n.* 为任意。\n可以少写,但不要写错", defaultInfo.teacher ); if (teacher !== null) { if (teacher === "") { alert("教师名称不能为空!"); return null; } else { result.teacher = teacher; } } else { return null; } let time = prompt( `输入匹配的时间(支持正则表达式)\n如 ^((?!第一讲).)+$ 就是不匹配带有第一讲的\n可以少写,但不要写错`, defaultInfo.time ); if (time !== null) { if (time === "") { alert("时间不能为空!"); return null; } else { result.time = time; } } else { return null; } return result; } function findClass() { for (let k = 0; k < subjectInfo.length; k++) { // 从选课首页点进教师详情那一页 $("div.courseShow.clearfix span.name").each(function () { let subjectNameRegex = new RegExp(subjectInfo[k].subjectName, "iu"); let currsubjectName = $(this).text(); if (currsubjectName.match(subjectNameRegex)) { pushLog("已找到" + subjectInfo[k].subjectName, 0); // 进入选课详情 $(this).parent().find("a.trigger[cid]")[0].click(); } }); var flag = false; var empty_flag = true; // 遍历详情页 setTimeout(() => { pushLog( "对“" + subjectInfo[k].subjectName + "”开始查询,第" + setting.count + "次", 0 ); setting.count++; // 获取当前课的所有可选课程 for (var i = 0; i < $("a.stat.off").length; i++) { empty_flag = false; var $this = $("a.stat.off").eq(i); var $parents = $this.parents("tr.editRows"); // 获取可选课程的老师上课时间地点等信息 for (var j = 0; j < $parents.length; j++) { setting.time = $parents.eq(j).children().eq(8).text(); pushLog("上课时间:" + setting.time, 0); setting.location = $parents.eq(j).children().eq(9).text(); pushLog("上课地点:" + setting.location, 0); setting.teacher = $parents.eq(j).children().eq(2).text(); pushLog("上课老师:" + setting.teacher, 0); let teacherNameRegex = new RegExp(subjectInfo[k].teacherName, "iu"); let timeRegex = new RegExp(subjectInfo[k].time, "iu"); if ( setting.teacher.match(teacherNameRegex) && setting.time.match(timeRegex) ) { // 选课 eval($this.attr("href")); pushLog("完成对课程“" + subjectInfo[k].subjectName + "”的选课", 3); delCourse(k); if (subjectInfo.length === 0) { clearInterval(setting.loop); delete setting.loop; $("#startQK")[0].innerText = "开始抢课"; } } else { pushLog("目标课暂无满足要求的席位,重试中", 0); // 返回课程列表 $("a.trigger.open")[0].click(); } } } if (empty_flag) { pushLog("课程“" + subjectInfo[k].subjectName + "”暂无空课,重试中", 0); } }, setting.wait); // 没找到就返回刷新 } }