├── requirements.txt ├── .gitignore ├── search_class_id.py ├── img └── img.png ├── readme.md ├── dlut_sso.py ├── app.py ├── main.py ├── templates ├── index.html ├── monitor.html └── auto_select.html └── des.py /requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | requests 3 | configparser 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | test.py 2 | config.ini 3 | .idea 4 | __pycache__ 5 | .venv -------------------------------------------------------------------------------- /search_class_id.py: -------------------------------------------------------------------------------- 1 | from main import * 2 | 3 | print(search_class("大学物理",ilist)) -------------------------------------------------------------------------------- /img/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/songhahaha66/dlut_auto_select_courses/HEAD/img/img.png -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # 大连理工大学自动抢课脚本 2 | ## 介绍 3 | 本脚本仅在正选阶段测试过,不适用于预选阶段 4 | ## 配置 5 | ### config.ini 6 | 需要在项目目录中配置config.ini 7 | ``` 8 | [dlut_sso] 9 | userid = 用户名 10 | password = 密码 11 | [turn] 12 | number = 0 #选课界面会有多个轮次,这里填写你要选的轮次 13 | ``` 14 | 关于turn number解释: 15 | ![img.png](img/img.png) 16 | 如图所示,选课界面有多个轮次,上图第一个代表0,第二个代表1,以此类推 17 | 18 | ## 运行 19 | ### 获取class_id 20 | 将脚本中的`print(search_class("大学物理",ilist))`,中大学物理改为你的课程名字,并运行该脚本。从而获取id 21 | ### 运行抢课脚本 22 | 在main.py中修改 23 | ``` 24 | class_id = 6666 #请将此替换为你要选的class_id 25 | ``` 26 | 然后运行main.py即可 27 | 28 | **注:请在抢课正式开始前就完成获取class_id的操作,并运行main.py,以免耽误抢课时间** 29 | -------------------------------------------------------------------------------- /dlut_sso.py: -------------------------------------------------------------------------------- 1 | import des 2 | import requests 3 | 4 | def initial(initUrl, id): 5 | s = requests.Session() 6 | response = s.get(initUrl) 7 | al = 'LT{}cas'.format(response.text.split('LT')[1].split('cas')[0]) 8 | s.cookies.set('dlut_cas_un', id) 9 | s.cookies.set('cas_hash', "") 10 | return s, al 11 | 12 | def constructPara(id, passwd, lt): 13 | al = { 14 | #'none': 'on', 15 | 'rsa': des.strEnc(id + passwd + lt, '1', '2', '3'), 16 | 'ul': str(len(id)), 17 | 'pl': str(len(passwd)), 18 | 'lt': lt, 19 | 'sl':"0", #不知道干什么的 20 | 'execution': 'e1s1', 21 | '_eventId': 'submit', 22 | } 23 | return '&'.join([i+'='+j for i, j in al.items()]) 24 | 25 | def login(id, passwd): 26 | targetUrl = 'https://sso.dlut.edu.cn/cas/login?service=http%3A%2F%2Fjxgl.dlut.edu.cn%2Fstudent%2Fucas-sso%2Flogin' 27 | s, lt = initial(targetUrl, id) 28 | s.headers = { 29 | 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36 Edg/138.0.0.0', 30 | 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8' 31 | } 32 | res = s.post(targetUrl, constructPara(id, passwd, lt), headers={'Content-Type': 'application/x-www-form-urlencoded'}).headers 33 | return s 34 | 35 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, render_template, request, jsonify 2 | import json 3 | import threading 4 | import time 5 | from main import * 6 | 7 | app = Flask(__name__) 8 | 9 | # 使用main.py中已经初始化的登录信息 10 | # cookies, stu_id, turn_id, ilist 已经在main.py中定义 11 | 12 | @app.route('/') 13 | def index(): 14 | return render_template('index.html') 15 | 16 | @app.route('/auto_select') 17 | def auto_select(): 18 | return render_template('auto_select.html') 19 | 20 | @app.route('/search_course', methods=['POST']) 21 | def search_course(): 22 | try: 23 | data = request.get_json() 24 | course_name = data.get('course_name', '') 25 | campus = data.get('campus', '') 26 | 27 | # 搜索课程 28 | result = [] 29 | lesson_ids = [] 30 | for i in ilist: 31 | # 校区筛选 32 | course_campus = i.get('campus', {}).get('nameZh', '') if 'campus' in i else '' 33 | 34 | # 课程名称匹配 + 校区筛选 35 | if course_name in i['course']['nameZh'] and (not campus or campus == course_campus): 36 | teachers = ', '.join([t['nameZh'] for t in i['teachers']]) 37 | result.append({ 38 | "name": i['course']['nameZh'], 39 | "code": i['code'], 40 | "id": i['id'], 41 | "teachers": teachers, 42 | "credits": i['course']['credits'], 43 | "capacity": i['limitCount'], 44 | "campus": course_campus 45 | }) 46 | lesson_ids.append(i['id']) 47 | 48 | # 获取已选人数 49 | if lesson_ids: 50 | selected_numbers = get_selected_numbers(lesson_ids) 51 | for course in result: 52 | course_id = str(course['id']) 53 | if course_id in selected_numbers: 54 | # 格式为"已选人数-候补人数" 55 | selected_info = selected_numbers[course_id] 56 | course['selected'] = selected_info.split('-')[0] # 只显示已选人数 57 | course['selected_full'] = selected_info # 完整信息"已选-候补" 58 | else: 59 | course['selected'] = '0' 60 | course['selected_full'] = '0-0' 61 | 62 | return jsonify({'success': True, 'courses': result}) 63 | except Exception as e: 64 | return jsonify({'success': False, 'message': f'搜索失败: {str(e)}'}) 65 | 66 | @app.route('/select_course', methods=['POST']) 67 | def select_course(): 68 | try: 69 | data = request.get_json() 70 | class_id = data.get('class_id') 71 | 72 | # 选课 73 | result = select_classes(class_id, turn_id) 74 | if result is True: 75 | return jsonify({'success': True, 'message': '选课成功'}) 76 | else: 77 | error_msg = str(result) if result else "选课失败,未知错误" 78 | return jsonify({'success': False, 'message': error_msg}) 79 | except Exception as e: 80 | return jsonify({'success': False, 'message': f'选课失败: {str(e)}'}) 81 | 82 | @app.route('/drop_course', methods=['POST']) 83 | def drop_course(): 84 | try: 85 | data = request.get_json() 86 | class_id = data.get('class_id') 87 | 88 | # 退课 89 | result = drop_classes(class_id, turn_id) 90 | if result is True: 91 | return jsonify({'success': True, 'message': '退课成功'}) 92 | else: 93 | error_msg = str(result) if result else "退课失败,未知错误" 94 | return jsonify({'success': False, 'message': error_msg}) 95 | except Exception as e: 96 | return jsonify({'success': False, 'message': f'退课失败: {str(e)}'}) 97 | 98 | @app.route('/selected_courses') 99 | def selected_courses(): 100 | try: 101 | # 获取已选课程 102 | selected = get_selected_classes(turn_id) 103 | 104 | courses = [] 105 | lesson_ids = [] 106 | for course in selected: 107 | teachers = ', '.join([t['nameZh'] for t in course['teachers']]) 108 | course_campus = course.get('campus', {}).get('nameZh', '') if 'campus' in course else '' 109 | courses.append({ 110 | "name": course['course']['nameZh'], 111 | "code": course['code'], 112 | "id": course['id'], 113 | "teachers": teachers, 114 | "credits": course['course']['credits'], 115 | "campus": course_campus, 116 | "capacity": course['limitCount'] 117 | }) 118 | lesson_ids.append(course['id']) 119 | 120 | # 获取已选人数 121 | if lesson_ids: 122 | selected_numbers = get_selected_numbers(lesson_ids) 123 | for course in courses: 124 | course_id = str(course['id']) 125 | if course_id in selected_numbers: 126 | # 格式为"已选人数-候补人数" 127 | selected_info = selected_numbers[course_id] 128 | course['selected'] = selected_info.split('-')[0] # 只显示已选人数 129 | course['selected_full'] = selected_info # 完整信息"已选-候补" 130 | else: 131 | course['selected'] = '0' 132 | course['selected_full'] = '0-0' 133 | 134 | return jsonify({'success': True, 'courses': courses}) 135 | except Exception as e: 136 | return jsonify({'success': False, 'message': f'获取已选课程失败: {str(e)}'}) 137 | 138 | 139 | @app.route('/get_campuses') 140 | def get_campuses(): 141 | try: 142 | campuses = set() 143 | for i in ilist: 144 | if 'campus' in i and 'nameZh' in i['campus']: 145 | campuses.add(i['campus']['nameZh']) 146 | return jsonify({'success': True, 'campuses': sorted(list(campuses))}) 147 | except Exception as e: 148 | return jsonify({'success': False, 'message': f'获取校区失败: {str(e)}'}) 149 | 150 | @app.route("/refresh_lesson_cache", methods=['GET']) 151 | def refresh_lesson_cache(): 152 | try: 153 | global ilist 154 | ilist = get_itemList(turn_id) # 重新获取课程列表 155 | ilist = json.loads(ilist) 156 | with open("ilist.json", "w", encoding="utf-8") as f: 157 | json.dump(ilist, f, ensure_ascii=False, indent=4) 158 | return jsonify({'success': True, 'message': '课程缓存已刷新'}) 159 | except Exception as e: 160 | return jsonify({'success': False, 'message': f'刷新课程缓存失败: {str(e)}'}) 161 | 162 | @app.route('/monitor') 163 | def monitor(): 164 | return render_template('monitor.html') 165 | 166 | @app.route('/check_course_availability', methods=['POST']) 167 | def check_course_availability(): 168 | try: 169 | data = request.get_json() 170 | course_ids = data.get('course_ids', []) 171 | 172 | if not course_ids: 173 | return jsonify({'success': False, 'message': '没有提供课程ID'}) 174 | 175 | # 获取课程的当前选课人数 176 | selected_numbers = get_selected_numbers(course_ids) 177 | 178 | available_courses = [] 179 | for course_id in course_ids: 180 | course_id_str = str(course_id) 181 | if course_id_str in selected_numbers: 182 | selected_info = selected_numbers[course_id_str] 183 | selected_count = int(selected_info.split('-')[0]) 184 | 185 | # 从ilist中找到对应课程的容量信息 186 | course_info = None 187 | for course in ilist: 188 | if course['id'] == course_id: 189 | course_info = course 190 | break 191 | 192 | if course_info: 193 | capacity = course_info['limitCount'] 194 | available_spots = capacity - selected_count 195 | 196 | # 如果有余量,记录课程信息 197 | if available_spots > 0: 198 | teachers = ', '.join([t['nameZh'] for t in course_info['teachers']]) 199 | course_campus = course_info.get('campus', {}).get('nameZh', '') if 'campus' in course_info else '' 200 | available_courses.append({ 201 | 'id': course_id, 202 | 'name': course_info['course']['nameZh'], 203 | 'code': course_info['code'], 204 | 'teachers': teachers, 205 | 'campus': course_campus, 206 | 'selected': selected_count, 207 | 'capacity': capacity, 208 | 'available': available_spots 209 | }) 210 | 211 | return jsonify({ 212 | 'success': True, 213 | 'available_courses': available_courses, 214 | 'total_monitored': len(course_ids) 215 | }) 216 | except Exception as e: 217 | return jsonify({'success': False, 'message': f'检查课程余量失败: {str(e)}'}) 218 | 219 | if __name__ == '__main__': 220 | app.run(debug=True, host='0.0.0.0', port=5000) 221 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import configparser 2 | import json 3 | import re 4 | import time 5 | 6 | import requests 7 | import dlut_sso 8 | 9 | config = configparser.ConfigParser() 10 | config.read("./config.ini",encoding="utf-8") 11 | userid = config.get("dlut_sso","userid") 12 | password = config.get("dlut_sso","password") 13 | turn_number = config.getint("turn","number") 14 | 15 | def jw_login(): 16 | s = dlut_sso.login(userid, password) 17 | cookies = { 18 | "SESSION": s.cookies['SESSION'], 19 | "INGRESSCOOKIE": s.cookies['INGRESSCOOKIE'], 20 | "SERVERNAME": s.cookies['SERVERNAME'] 21 | } 22 | s.get("http://jxgl.dlut.edu.cn/student/for-std/course-select") 23 | return cookies 24 | 25 | def get_class_turns(i): 26 | url = "http://jxgl.dlut.edu.cn/student/ws/for-std/course-select/open-turns" 27 | data = { 28 | "bizTypeId":"2", 29 | "studentId":stu_id 30 | } 31 | r = requests.post(url, data=data, cookies=cookies) 32 | data1=json.loads(r.text) 33 | data1=data1[i] #0一般是主修选课 34 | return data1['id'] 35 | 36 | def get_itemList(id): 37 | url = f"http://jxgl.dlut.edu.cn/student/cache/course-select/version/{id}/version.json" 38 | r = requests.get(url, cookies=cookies) 39 | data = json.loads(r.text) 40 | a = data['itemList'][0] 41 | url1 = f"http://cdn-dlut.supwisdom.com/student/cache/course-select/addable-lessons/{id}/{a}.json" 42 | return json.loads(requests.get(url1, cookies=cookies).text)['data'] 43 | 44 | def get_student_id(): 45 | url = "http://jxgl.dlut.edu.cn/student/for-std/course-select/single-student/turns" 46 | html = requests.get(url,cookies=cookies).text 47 | match = re.search(r'studentId\s*:\s*(\d+),', html) 48 | if match: 49 | student_id = match.group(1) 50 | return int(student_id) 51 | 52 | def select_classes(class_id, turn_id): 53 | try: 54 | url = "http://jxgl.dlut.edu.cn/student/ws/for-std/course-select/add-request" 55 | data = {"studentAssoc":stu_id,"courseSelectTurnAssoc":turn_id,"requestMiddleDtos":[{"lessonAssoc":class_id,"virtualCost":0,"scheduleGroupAssoc":None}]} 56 | r1= requests.post(url,json=data,cookies=cookies) 57 | uuid1 = r1.text 58 | url1 = "http://jxgl.dlut.edu.cn/student/ws/for-std/course-select/add-drop-response" 59 | data1 ={ 60 | "studentId":stu_id, 61 | "requestId":uuid1 62 | } 63 | r2 = requests.post(url1,data=data1,cookies=cookies) 64 | 65 | if r2.status_code != 200: 66 | return {"error": f"HTTP错误: {r2.status_code}"} 67 | 68 | try: 69 | r2_res = json.loads(r2.text) 70 | except json.JSONDecodeError: 71 | return {"error": "服务器返回无效的JSON格式"} 72 | 73 | if r2_res is None: 74 | return {"error": "服务器返回空响应"} 75 | 76 | if r2_res.get('success'): 77 | print("选课成功") 78 | return True 79 | else: 80 | print("选课失败") 81 | # 返回完整错误信息字典 82 | return r2_res['errorMessage']['textZh'] if r2_res else {"error": "未知错误"} 83 | except Exception as e: 84 | return {"error": f"选课请求异常: {str(e)}"} 85 | 86 | def drop_classes(class_id,turn_id): 87 | try: 88 | url = "http://jxgl.dlut.edu.cn/student/ws/for-std/course-select/drop-request" 89 | data = {"studentAssoc":stu_id,"lessonAssocs":[class_id],"courseSelectTurnAssoc":turn_id,"coursePackAssoc":None} 90 | r1 = requests.post(url,json=data,cookies=cookies) 91 | uuid1 = r1.text 92 | url1 = "http://jxgl.dlut.edu.cn/student/ws/for-std/course-select/add-drop-response" 93 | data1 = { 94 | "studentId": stu_id, 95 | "requestId": uuid1 96 | } 97 | r2 = requests.post(url1, data=data1, cookies=cookies) 98 | 99 | if r2.status_code != 200: 100 | return {"error": f"HTTP错误: {r2.status_code}"} 101 | 102 | try: 103 | r2_res = json.loads(r2.text) 104 | except json.JSONDecodeError: 105 | return {"error": "服务器返回无效的JSON格式"} 106 | 107 | if r2_res is None: 108 | return {"error": "服务器返回空响应"} 109 | 110 | if r2_res.get('success'): 111 | print("退课成功") 112 | return True 113 | else: 114 | print("退课失败") 115 | print(r2_res) 116 | # 返回完整错误信息字典 117 | return r2_res['errorMessage']['textZh'] if r2_res else {"error": "未知错误"} 118 | except Exception as e: 119 | return {"error": f"退课请求异常: {str(e)}"} 120 | 121 | 122 | def search_class(class_name,ilist): 123 | result = [] 124 | for i in ilist: 125 | if class_name in i['course']['nameZh']: 126 | result.append({"name":i['course']['nameZh'],"id":i['id'], "teachers":i['teachers']}) 127 | return result 128 | 129 | def get_selected_classes(turn_Id): 130 | url = "http://jxgl.dlut.edu.cn/student/ws/for-std/course-select/selected-lessons" 131 | data = { 132 | "studentId":stu_id, 133 | "turnId":turn_Id 134 | } 135 | r = requests.post(url,data=data,cookies=cookies) 136 | d = json.loads(r.text) 137 | return d 138 | 139 | def get_selected_numbers(lesson_ids): 140 | url = "http://jxgl.dlut.edu.cn/student/ws/for-std/course-select/std-count" 141 | data = [("lessonIds[]", lid) for lid in lesson_ids] 142 | r = requests.post(url, data=data, cookies=cookies) 143 | return json.loads(r.text) 144 | 145 | def batch_operations(operations, interval=2): 146 | """ 147 | 批量执行选课/退课操作 148 | operations: 操作列表,每个操作包含 {'type': 'select'|'drop', 'class_id': int} 149 | interval: 操作间隔时间(秒) 150 | """ 151 | results = [] 152 | 153 | for i, operation in enumerate(operations): 154 | operation_type = operation.get('type') 155 | class_id = operation.get('class_id') 156 | 157 | if operation_type not in ['select', 'drop']: 158 | results.append({ 159 | 'success': False, 160 | 'message': f'无效的操作类型: {operation_type}', 161 | 'operation': operation 162 | }) 163 | continue 164 | 165 | try: 166 | if operation_type == 'select': 167 | result = select_classes(class_id, turn_id) 168 | else: # drop 169 | result = drop_classes(class_id, turn_id) 170 | 171 | if result is True: 172 | msg = f'{"选课" if operation_type == "select" else "退课"}成功' 173 | results.append({ 174 | 'success': True, 175 | 'message': msg, 176 | 'operation': operation 177 | }) 178 | else: 179 | # result为错误信息字典 180 | error_msg = "未知错误" 181 | if isinstance(result, dict): 182 | # 尝试提取更详细的错误信息 183 | if 'errorMessage' in result: 184 | error_detail = result['errorMessage'] 185 | if isinstance(error_detail, dict): 186 | error_msg = error_detail.get('textZh', error_detail.get('text', error_msg)) 187 | else: 188 | error_msg = str(error_detail) 189 | elif 'message' in result: 190 | error_msg = result['message'] 191 | elif 'error' in result: 192 | error_msg = result['error'] 193 | elif isinstance(result, str): 194 | error_msg = result 195 | msg = f'{"选课" if operation_type == "select" else "退课"}失败: {error_msg}' 196 | results.append({ 197 | 'success': False, 198 | 'message': msg, 199 | 'operation': operation 200 | }) 201 | 202 | except Exception as e: 203 | results.append({ 204 | 'success': False, 205 | 'message': f'操作出错: {str(e)}', 206 | 'operation': operation 207 | }) 208 | 209 | # 如果不是最后一个操作,等待间隔时间 210 | if i < len(operations) - 1: 211 | time.sleep(interval) 212 | 213 | return results 214 | 215 | def continuous_operations(operations, interval=2, max_attempts=None, stop_check=None): 216 | """ 217 | 持续执行操作直到所有操作成功 218 | operations: 操作列表 219 | interval: 操作间隔时间(秒) 220 | max_attempts: 最大尝试次数,None表示无限制 221 | stop_check: 停止检查函数,返回True时停止执行 222 | """ 223 | attempt = 0 224 | remaining_operations = operations.copy() 225 | 226 | while remaining_operations and (max_attempts is None or attempt < max_attempts): 227 | # 检查是否需要停止 228 | if stop_check and stop_check(): 229 | print("收到停止信号,脚本终止") 230 | break 231 | 232 | attempt += 1 233 | print(f"第 {attempt} 次尝试,剩余 {len(remaining_operations)} 个操作") 234 | 235 | results = batch_operations(remaining_operations, interval) 236 | 237 | # 移除成功的操作 238 | successful_operations = [] 239 | for i, result in enumerate(results): 240 | if result['success']: 241 | successful_operations.append(remaining_operations[i]) 242 | 243 | for op in successful_operations: 244 | remaining_operations.remove(op) 245 | 246 | if remaining_operations: 247 | print(f"还有 {len(remaining_operations)} 个操作未完成,等待 {interval} 秒后继续...") 248 | time.sleep(interval) 249 | else: 250 | print("所有操作已完成!") 251 | break 252 | 253 | return len(remaining_operations) == 0, attempt 254 | 255 | def continuous_operations_with_log(operations, interval=2, max_attempts=None, stop_check=None, log_callback=None): 256 | """ 257 | 持续执行操作直到所有操作成功,带日志记录功能 258 | operations: 操作列表,每个操作包含 {'type': 'select'|'drop', 'class_id': int, 'course_info': dict} 259 | interval: 操作间隔时间(秒) 260 | max_attempts: 最大尝试次数,None表示无限制 261 | stop_check: 停止检查函数,返回True时停止执行 262 | log_callback: 日志回调函数 263 | """ 264 | attempt = 0 265 | remaining_operations = operations.copy() 266 | 267 | while remaining_operations and (max_attempts is None or attempt < max_attempts): 268 | # 检查是否需要停止 269 | if stop_check and stop_check(): 270 | if log_callback: 271 | log_callback("收到停止信号,脚本终止", 'warning') 272 | break 273 | 274 | attempt += 1 275 | if log_callback: 276 | log_callback(f"第 {attempt} 次尝试,剩余 {len(remaining_operations)} 个操作", 'info') 277 | 278 | # 执行当前轮次的所有操作 279 | successful_operations = [] 280 | for i, operation in enumerate(remaining_operations): 281 | # 添加空值检查 282 | if operation is None: 283 | if log_callback: 284 | log_callback("❌ 发现空操作,跳过", 'error') 285 | continue 286 | 287 | operation_type = operation.get('type') if isinstance(operation, dict) else None 288 | class_id = operation.get('class_id') if isinstance(operation, dict) else None 289 | course_info = operation.get('course_info', {}) if isinstance(operation, dict) else {} 290 | 291 | # 确保course_info是字典 292 | if not isinstance(course_info, dict): 293 | course_info = {} 294 | 295 | course_name = course_info.get('name', f'课程ID:{class_id}') if course_info else f'课程ID:{class_id}' 296 | 297 | if operation_type not in ['select', 'drop']: 298 | if log_callback: 299 | log_callback(f"❌ {course_name}: 无效的操作类型 {operation_type}", 'error', course_info) 300 | continue 301 | 302 | if class_id is None: 303 | if log_callback: 304 | log_callback(f"❌ {course_name}: 课程ID为空", 'error', course_info) 305 | continue 306 | 307 | try: 308 | if operation_type == 'select': 309 | result = select_classes(class_id, turn_id) 310 | else: # drop 311 | result = drop_classes(class_id, turn_id) 312 | 313 | if result is True: 314 | action = "选课" if operation_type == "select" else "退课" 315 | msg = f"✅ {course_name}: {action}成功" 316 | if log_callback: 317 | log_callback(msg, 'success', course_info) 318 | successful_operations.append(operation) 319 | else: 320 | # result为错误信息字典 321 | error_msg = "未知错误" 322 | if isinstance(result, dict): 323 | # 尝试提取更详细的错误信息 324 | if 'errorMessage' in result: 325 | error_detail = result['errorMessage'] 326 | if isinstance(error_detail, dict): 327 | error_msg = error_detail.get('textZh', error_detail.get('text', error_msg)) 328 | else: 329 | error_msg = str(error_detail) 330 | elif 'message' in result: 331 | error_msg = result['message'] 332 | elif 'error' in result: 333 | error_msg = result['error'] 334 | elif isinstance(result, str): 335 | error_msg = result 336 | 337 | action = "选课" if operation_type == "select" else "退课" 338 | msg = f"❌ {course_name}: {action}失败 - {error_msg}" 339 | if log_callback: 340 | log_callback(msg, 'error', course_info) 341 | 342 | except Exception as e: 343 | action = "选课" if operation_type == "select" else "退课" 344 | msg = f"❌ {course_name}: {action}操作出错 - {str(e)}" 345 | if log_callback: 346 | log_callback(msg, 'error', course_info) 347 | 348 | # 操作间短暂延时 349 | if i < len(remaining_operations) - 1: 350 | time.sleep(min(interval * 0.2, 1)) # 单个操作间的小延时 351 | 352 | # 移除成功的操作 353 | for op in successful_operations: 354 | remaining_operations.remove(op) 355 | 356 | if remaining_operations: 357 | if log_callback: 358 | log_callback(f"还有 {len(remaining_operations)} 个操作未完成,等待 {interval} 秒后继续...", 'info') 359 | time.sleep(interval) 360 | else: 361 | if log_callback: 362 | log_callback("🎉 所有操作已完成!", 'success') 363 | break 364 | 365 | return len(remaining_operations) == 0, attempt 366 | 367 | cookies = jw_login() 368 | stu_id = get_student_id() 369 | turn_id = get_class_turns(turn_number) 370 | try: 371 | with open("ilist.json", "r", encoding="utf-8") as f: 372 | ilist = json.load(f) 373 | except FileNotFoundError: 374 | print("ilist.json not found, fetching from server...") 375 | ilist = get_itemList(turn_id) 376 | ilist = json.loads(ilist) 377 | with open("ilist.json", "w", encoding="utf-8") as f: 378 | json.dump(ilist, f, ensure_ascii=False, indent=4) 379 | 380 | if __name__ == "__main__": 381 | class_id = 6666 #请将此替换为你要选的class_id 382 | while True: 383 | res = select_classes(class_id, turn_id) 384 | if res: 385 | break -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 大连理工大学选课系统 7 | 195 | 196 | 197 |
198 |
199 |

大连理工大学选课系统

200 |
201 | 🚀 自动抢课脚本 202 | 🔍 余量监控 203 | 🏠 主页 204 |
205 |
已从配置文件登录
206 | 207 |
208 | 209 | 210 |
211 |
212 |

搜索课程

213 |
214 | 215 | 216 |
217 |
218 | 219 | 222 |
223 | 224 |
225 |
226 | 227 | 228 |
229 |

已选课程

230 | 231 |
232 |
233 |
234 | 235 |
236 |
237 |
238 | 434 | 435 | 436 | -------------------------------------------------------------------------------- /des.py: -------------------------------------------------------------------------------- 1 | # > * DES加密解密 2 | # > * @Copyright Copyright (c) 2006 3 | # > * @author Guapo 4 | # > * @see DESCore 5 | 6 | # this file is translated from js code above, by beautyyu 7 | def strEnc(data, firstKey, secondKey, thirdKey): 8 | leng = len(data) 9 | encData = "" 10 | if (firstKey != None and firstKey != ""): 11 | firstKeyBt = getKeyBytes(firstKey) 12 | firstLength = len(firstKeyBt) 13 | 14 | if (secondKey != None and secondKey != ""): 15 | secondKeyBt = getKeyBytes(secondKey) 16 | secondLength = len(secondKeyBt) 17 | 18 | if (thirdKey != None and thirdKey != ""): 19 | thirdKeyBt = getKeyBytes(thirdKey) 20 | thirdLength = len(thirdKeyBt) 21 | 22 | if (leng > 0): 23 | if (leng < 4): 24 | bt = strToBt(data) 25 | if ( 26 | firstKey != None and firstKey != "" and secondKey != None and secondKey != "" and thirdKey != None and thirdKey != ""): 27 | tempBt = bt 28 | for x in range(firstLength): 29 | tempBt = enc(tempBt, firstKeyBt[x]) 30 | 31 | for y in range(secondLength): 32 | tempBt = enc(tempBt, secondKeyBt[y]) 33 | 34 | for z in range(thirdLength): 35 | tempBt = enc(tempBt, thirdKeyBt[z]) 36 | 37 | encByte = tempBt 38 | else: 39 | if (firstKey != None and firstKey != "" and secondKey != None and secondKey != ""): 40 | tempBt = bt 41 | for x in range(firstLength): 42 | tempBt = enc(tempBt, firstKeyBt[x]) 43 | 44 | for y in range(secondLength): 45 | tempBt = enc(tempBt, secondKeyBt[y]) 46 | 47 | encByte = tempBt 48 | else: 49 | if (firstKey != None and firstKey != ""): 50 | x = 0 51 | tempBt = bt 52 | for x in range(firstLength): 53 | tempBt = enc(tempBt, firstKeyBt[x]) 54 | 55 | encByte = tempBt 56 | 57 | encData = bt64ToHex(encByte) 58 | else: 59 | iterator = int(leng / 4) 60 | remainder = leng % 4 61 | i = 0 62 | for i in range(iterator): 63 | tempData = data[i * 4 + 0: i * 4 + 4] 64 | tempByte = strToBt(tempData) 65 | if ( 66 | firstKey != None and firstKey != "" and secondKey != None and secondKey != "" and thirdKey != None and thirdKey != ""): 67 | tempBt = tempByte 68 | for x in range(firstLength): 69 | tempBt = enc(tempBt, firstKeyBt[x]) 70 | 71 | for y in range(secondLength): 72 | tempBt = enc(tempBt, secondKeyBt[y]) 73 | 74 | for z in range(thirdLength): 75 | tempBt = enc(tempBt, thirdKeyBt[z]) 76 | 77 | encByte = tempBt 78 | else: 79 | if (firstKey != None and firstKey != "" and secondKey != None and secondKey != ""): 80 | tempBt 81 | x, y 82 | tempBt = tempByte 83 | for x in range(firstLength): 84 | tempBt = enc(tempBt, firstKeyBt[x]) 85 | 86 | for y in range(secondLength): 87 | tempBt = enc(tempBt, secondKeyBt[y]) 88 | 89 | encByte = tempBt 90 | else: 91 | if (firstKey != None and firstKey != ""): 92 | tempBt 93 | x 94 | tempBt = tempByte 95 | for x in range(firstLength): 96 | tempBt = enc(tempBt, firstKeyBt[x]) 97 | 98 | encByte = tempBt 99 | 100 | encData += bt64ToHex(encByte) 101 | 102 | if (remainder > 0): 103 | remainderData = data[iterator * 4 + 0: leng] 104 | tempByte = strToBt(remainderData) 105 | encByte 106 | if ( 107 | firstKey != None and firstKey != "" and secondKey != None and secondKey != "" and thirdKey != None and thirdKey != ""): 108 | tempBt 109 | x, y, z 110 | tempBt = tempByte 111 | for x in range(firstLength): 112 | tempBt = enc(tempBt, firstKeyBt[x]) 113 | 114 | for y in range(secondLength): 115 | tempBt = enc(tempBt, secondKeyBt[y]) 116 | 117 | for z in range(thirdLength): 118 | tempBt = enc(tempBt, thirdKeyBt[z]) 119 | 120 | encByte = tempBt 121 | else: 122 | if (firstKey != None and firstKey != "" and secondKey != None and secondKey != ""): 123 | tempBt 124 | x, y 125 | tempBt = tempByte 126 | for x in range(firstLength): 127 | tempBt = enc(tempBt, firstKeyBt[x]) 128 | 129 | for y in range(secondLength): 130 | tempBt = enc(tempBt, secondKeyBt[y]) 131 | 132 | encByte = tempBt 133 | else: 134 | if (firstKey != None and firstKey != ""): 135 | tempBt 136 | x 137 | tempBt = tempByte 138 | for x in range(firstLength): 139 | tempBt = enc(tempBt, firstKeyBt[x]) 140 | 141 | encByte = tempBt 142 | 143 | encData += bt64ToHex(encByte) 144 | 145 | return encData 146 | 147 | 148 | def strDec(data, firstKey, secondKey, thirdKey): 149 | leng = len(data) 150 | decStr = "" 151 | if (firstKey != None and firstKey != ""): 152 | firstKeyBt = getKeyBytes(firstKey) 153 | firstLength = len(firstKeyBt) 154 | 155 | if (secondKey != None and secondKey != ""): 156 | secondKeyBt = getKeyBytes(secondKey) 157 | secondLength = len(secondKeyBt) 158 | 159 | if (thirdKey != None and thirdKey != ""): 160 | thirdKeyBt = getKeyBytes(thirdKey) 161 | thirdLength = len(thirdKeyBt) 162 | 163 | iterator = int(leng / 16) 164 | i = 0 165 | for i in range(iterator): 166 | tempData = data[i * 16 + 0: i * 16 + 16] 167 | strByte = hexToBt64(tempData) 168 | intByte = [0] * 64 169 | j = 0 170 | for j in range(64): 171 | intByte[j] = int(strByte[j: j + 1]) 172 | 173 | if ( 174 | firstKey != None and firstKey != "" and secondKey != None and secondKey != "" and thirdKey != None and thirdKey != ""): 175 | tempBt = intByte 176 | for x in range(thirdLength - 1, -1, -1): 177 | tempBt = dec(tempBt, thirdKeyBt[x]) 178 | 179 | for y in range(secondLength - 1, -1, -1): 180 | tempBt = dec(tempBt, secondKeyBt[y]) 181 | 182 | for z in range(firstLength - 1, -1, -1): 183 | tempBt = dec(tempBt, firstKeyBt[z]) 184 | 185 | decByte = tempBt 186 | else: 187 | if (firstKey != None and firstKey != "" and secondKey != None and secondKey != ""): 188 | tempBt 189 | x, y, z 190 | tempBt = intByte 191 | for x in range(secondLength - 1, -1, -1): 192 | tempBt = dec(tempBt, secondKeyBt[x]) 193 | 194 | for y in range(firstLength - 1, -1, -1): 195 | tempBt = dec(tempBt, firstKeyBt[y]) 196 | 197 | decByte = tempBt 198 | else: 199 | if (firstKey != None and firstKey != ""): 200 | tempBt 201 | x, y, z 202 | tempBt = intByte 203 | for x in range(firstLength - 1, -1, -1): 204 | tempBt = dec(tempBt, firstKeyBt[x]) 205 | 206 | decByte = tempBt 207 | 208 | decStr += byteToString(decByte) 209 | return decStr 210 | 211 | 212 | def getKeyBytes(key): 213 | leng = len(key) 214 | iterator = int(leng / 4) 215 | keyBytes = [0] * (iterator + 1) 216 | remainder = leng % 4 217 | i = 0 218 | for i in range(iterator): 219 | keyBytes[i] = strToBt(key[i * 4 + 0:i * 4 + 4]) 220 | 221 | i = iterator 222 | if (remainder > 0): 223 | keyBytes[i] = strToBt(key[i * 4 + 0:leng]) 224 | 225 | return keyBytes 226 | 227 | 228 | def strToBt(str): 229 | leng = len(str) 230 | bt = [0] * (64) 231 | if (leng < 4): 232 | i = 0 233 | j = 0 234 | p = 0 235 | q = 0 236 | for i in range(leng): 237 | k = ord(str[i]) 238 | for j in range(16): 239 | pow = 1 240 | m = 0 241 | for m in range(15, j, -1): 242 | pow *= 2 243 | 244 | bt[16 * i + j] = int(k / pow) % 2 245 | 246 | for p in range(leng, 4, 1): 247 | k = 0 248 | for q in range(16): 249 | pow = 1 250 | m = 0 251 | for m in range(15, q, -1): 252 | pow *= 2 253 | 254 | bt[16 * p + q] = int(k / pow) % 2 255 | 256 | else: 257 | for i in range(4): 258 | k = ord(str[i]) 259 | for j in range(16): 260 | pow = 1 261 | for m in range(15, j, -1): 262 | pow *= 2 263 | 264 | bt[16 * i + j] = int(k / pow) % 2 265 | 266 | return bt 267 | 268 | 269 | def bt4ToHex(binary): 270 | switch = { 271 | "0000": "0", 272 | "0001": "1", 273 | "0010": "2", 274 | "0011": "3", 275 | "0100": "4", 276 | "0101": "5", 277 | "0110": "6", 278 | "0111": "7", 279 | "1000": "8", 280 | "1001": "9", 281 | "1010": "A", 282 | "1011": "B", 283 | "1100": "C", 284 | "1101": "D", 285 | "1110": "E", 286 | "1111": "F", 287 | } 288 | hex = switch[binary] 289 | 290 | return hex 291 | 292 | 293 | def hexToBt4(hex): 294 | switch = { 295 | "0": "0000", 296 | "1": "0001", 297 | "2": "0010", 298 | "3": "0011", 299 | "4": "0100", 300 | "5": "0101", 301 | "6": "0110", 302 | "7": "0111", 303 | "8": "1000", 304 | "9": "1001", 305 | "A": "1010", 306 | "B": "1011", 307 | "C": "1100", 308 | "D": "1101", 309 | "E": "1110", 310 | "F": "1111", 311 | } 312 | binary = switch[hex] 313 | 314 | return binary 315 | 316 | 317 | def byteToString(byteData): 318 | str = "" 319 | for i in range(4): 320 | count = 0 321 | for j in range(16): 322 | pow = 1 323 | for m in range(15, j, -1): 324 | pow *= 2 325 | 326 | count += byteData[16 * i + j] * pow 327 | 328 | if (count != 0): 329 | str += chr(count) 330 | 331 | return str 332 | 333 | 334 | def bt64ToHex(byteData): 335 | hex = "" 336 | for i in range(16): 337 | bt = "" 338 | for j in range(4): 339 | bt += str(byteData[i * 4 + j]) 340 | 341 | hex += bt4ToHex(bt) 342 | 343 | return hex 344 | 345 | 346 | def hexToBt64(hex): 347 | binary = "" 348 | for i in range(16): 349 | binary += hexToBt4(hex[i: i + 1]) 350 | 351 | return binary 352 | 353 | 354 | def enc(dataByte, keyByte): 355 | keys = generateKeys(keyByte) 356 | ipByte = initPermute(dataByte) 357 | ipLeft = [0] * (32) 358 | ipRight = [0] * (32) 359 | tempLeft = [0] * (32) 360 | for k in range(32): 361 | ipLeft[k] = ipByte[k] 362 | ipRight[k] = ipByte[32 + k] 363 | 364 | for i in range(16): 365 | for j in range(32): 366 | tempLeft[j] = ipLeft[j] 367 | ipLeft[j] = ipRight[j] 368 | 369 | key = [0] * (48) 370 | for m in range(48): 371 | key[m] = keys[i][m] 372 | 373 | tempRight = xor(pPermute(sBoxPermute( 374 | xor(expandPermute(ipRight), key))), tempLeft) 375 | for n in range(32): 376 | ipRight[n] = tempRight[n] 377 | 378 | finalData = [0] * (64) 379 | for i in range(32): 380 | finalData[i] = ipRight[i] 381 | finalData[32 + i] = ipLeft[i] 382 | 383 | return finallyPermute(finalData) 384 | 385 | 386 | def dec(dataByte, keyByte): 387 | keys = generateKeys(keyByte) 388 | ipByte = initPermute(dataByte) 389 | ipLeft = [0] * (32) 390 | ipRight = [0] * (32) 391 | tempLeft = [0] * (32) 392 | for k in range(32): 393 | ipLeft[k] = ipByte[k] 394 | ipRight[k] = ipByte[32 + k] 395 | 396 | for i in range(15, -1, -1): 397 | for j in range(32): 398 | tempLeft[j] = ipLeft[j] 399 | ipLeft[j] = ipRight[j] 400 | 401 | key = [0] * (48) 402 | for m in range(48): 403 | key[m] = keys[i][m] 404 | 405 | tempRight = xor(pPermute(sBoxPermute( 406 | xor(expandPermute(ipRight), key))), tempLeft) 407 | for n in range(32): 408 | ipRight[n] = tempRight[n] 409 | 410 | finalData = [0] * (64) 411 | for i in range(32): 412 | finalData[i] = ipRight[i] 413 | finalData[32 + i] = ipLeft[i] 414 | 415 | return finallyPermute(finalData) 416 | 417 | 418 | def initPermute(originalData): 419 | ipByte = [0] * (64) 420 | m = 1 421 | n = 0 422 | for i in range(4): 423 | k = 0 424 | for j in range(7, -1, -1): 425 | ipByte[i * 8 + k] = originalData[j * 8 + m] 426 | ipByte[i * 8 + k + 32] = originalData[j * 8 + n] 427 | k += 1 428 | m += 2 429 | n += 2 430 | 431 | return ipByte 432 | 433 | 434 | def expandPermute(rightData): 435 | epByte = [0] * (48) 436 | for i in range(8): 437 | if i == 0: 438 | epByte[i * 6 + 0] = rightData[31] 439 | else: 440 | epByte[i * 6 + 0] = rightData[i * 4 - 1] 441 | 442 | epByte[i * 6 + 1] = rightData[i * 4 + 0] 443 | epByte[i * 6 + 2] = rightData[i * 4 + 1] 444 | epByte[i * 6 + 3] = rightData[i * 4 + 2] 445 | epByte[i * 6 + 4] = rightData[i * 4 + 3] 446 | if i == 7: 447 | epByte[i * 6 + 5] = rightData[0] 448 | else: 449 | epByte[i * 6 + 5] = rightData[i * 4 + 4] 450 | return epByte 451 | 452 | 453 | def xor(byteOne, byteTwo): 454 | xorByte = [0] * (len(byteOne)) 455 | for i in range(len(byteOne)): 456 | xorByte[i] = byteOne[i] ^ byteTwo[i] 457 | 458 | return xorByte 459 | 460 | 461 | def sBoxPermute(expandByte): 462 | sBoxByte = [0] * (32) 463 | binary = "" 464 | s1 = [ 465 | [14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7], 466 | [0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8], 467 | [4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0], 468 | [15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13]] 469 | 470 | s2 = [ 471 | [15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10], 472 | [3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5], 473 | [0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15], 474 | [13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9]] 475 | 476 | s3 = [ 477 | [10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8], 478 | [13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1], 479 | [13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7], 480 | [1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12]] 481 | s4 = [ 482 | [7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15], 483 | [13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9], 484 | [10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4], 485 | [3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14]] 486 | 487 | s5 = [ 488 | [2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9], 489 | [14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6], 490 | [4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14], 491 | [11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3]] 492 | 493 | s6 = [ 494 | [12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11], 495 | [10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8], 496 | [9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6], 497 | [4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13]] 498 | 499 | s7 = [ 500 | [4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1], 501 | [13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6], 502 | [1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2], 503 | [6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12]] 504 | 505 | s8 = [ 506 | [13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7], 507 | [1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2], 508 | [7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8], 509 | [2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11]] 510 | 511 | for m in range(8): 512 | i = 0 513 | j = 0 514 | i = expandByte[m * 6 + 0] * 2 + expandByte[m * 6 + 5] 515 | j = expandByte[m * 6 + 1] * 2 * 2 * 2 \ 516 | + expandByte[m * 6 + 2] * 2 * 2 \ 517 | + expandByte[m * 6 + 3] * 2 \ 518 | + expandByte[m * 6 + 4] 519 | if True: 520 | if m == 0: 521 | binary = getBoxBinary(s1[i][j]) 522 | if m == 1: 523 | binary = getBoxBinary(s2[i][j]) 524 | if m == 2: 525 | binary = getBoxBinary(s3[i][j]) 526 | if m == 3: 527 | binary = getBoxBinary(s4[i][j]) 528 | if m == 4: 529 | binary = getBoxBinary(s5[i][j]) 530 | if m == 5: 531 | binary = getBoxBinary(s6[i][j]) 532 | if m == 6: 533 | binary = getBoxBinary(s7[i][j]) 534 | if m == 7: 535 | binary = getBoxBinary(s8[i][j]) 536 | 537 | sBoxByte[m * 4 + 0] = int(binary[0: 1]) 538 | sBoxByte[m * 4 + 1] = int(binary[1: 2]) 539 | sBoxByte[m * 4 + 2] = int(binary[2: 3]) 540 | sBoxByte[m * 4 + 3] = int(binary[3: 4]) 541 | return sBoxByte 542 | 543 | 544 | def pPermute(sBoxByte): 545 | pBoxPermute = [0] * (32) 546 | pBoxPermute[0] = sBoxByte[15] 547 | pBoxPermute[1] = sBoxByte[6] 548 | pBoxPermute[2] = sBoxByte[19] 549 | pBoxPermute[3] = sBoxByte[20] 550 | pBoxPermute[4] = sBoxByte[28] 551 | pBoxPermute[5] = sBoxByte[11] 552 | pBoxPermute[6] = sBoxByte[27] 553 | pBoxPermute[7] = sBoxByte[16] 554 | pBoxPermute[8] = sBoxByte[0] 555 | pBoxPermute[9] = sBoxByte[14] 556 | pBoxPermute[10] = sBoxByte[22] 557 | pBoxPermute[11] = sBoxByte[25] 558 | pBoxPermute[12] = sBoxByte[4] 559 | pBoxPermute[13] = sBoxByte[17] 560 | pBoxPermute[14] = sBoxByte[30] 561 | pBoxPermute[15] = sBoxByte[9] 562 | pBoxPermute[16] = sBoxByte[1] 563 | pBoxPermute[17] = sBoxByte[7] 564 | pBoxPermute[18] = sBoxByte[23] 565 | pBoxPermute[19] = sBoxByte[13] 566 | pBoxPermute[20] = sBoxByte[31] 567 | pBoxPermute[21] = sBoxByte[26] 568 | pBoxPermute[22] = sBoxByte[2] 569 | pBoxPermute[23] = sBoxByte[8] 570 | pBoxPermute[24] = sBoxByte[18] 571 | pBoxPermute[25] = sBoxByte[12] 572 | pBoxPermute[26] = sBoxByte[29] 573 | pBoxPermute[27] = sBoxByte[5] 574 | pBoxPermute[28] = sBoxByte[21] 575 | pBoxPermute[29] = sBoxByte[10] 576 | pBoxPermute[30] = sBoxByte[3] 577 | pBoxPermute[31] = sBoxByte[24] 578 | return pBoxPermute 579 | 580 | 581 | def finallyPermute(endByte): 582 | fpByte = [0] * (64) 583 | fpByte[0] = endByte[39] 584 | fpByte[1] = endByte[7] 585 | fpByte[2] = endByte[47] 586 | fpByte[3] = endByte[15] 587 | fpByte[4] = endByte[55] 588 | fpByte[5] = endByte[23] 589 | fpByte[6] = endByte[63] 590 | fpByte[7] = endByte[31] 591 | fpByte[8] = endByte[38] 592 | fpByte[9] = endByte[6] 593 | fpByte[10] = endByte[46] 594 | fpByte[11] = endByte[14] 595 | fpByte[12] = endByte[54] 596 | fpByte[13] = endByte[22] 597 | fpByte[14] = endByte[62] 598 | fpByte[15] = endByte[30] 599 | fpByte[16] = endByte[37] 600 | fpByte[17] = endByte[5] 601 | fpByte[18] = endByte[45] 602 | fpByte[19] = endByte[13] 603 | fpByte[20] = endByte[53] 604 | fpByte[21] = endByte[21] 605 | fpByte[22] = endByte[61] 606 | fpByte[23] = endByte[29] 607 | fpByte[24] = endByte[36] 608 | fpByte[25] = endByte[4] 609 | fpByte[26] = endByte[44] 610 | fpByte[27] = endByte[12] 611 | fpByte[28] = endByte[52] 612 | fpByte[29] = endByte[20] 613 | fpByte[30] = endByte[60] 614 | fpByte[31] = endByte[28] 615 | fpByte[32] = endByte[35] 616 | fpByte[33] = endByte[3] 617 | fpByte[34] = endByte[43] 618 | fpByte[35] = endByte[11] 619 | fpByte[36] = endByte[51] 620 | fpByte[37] = endByte[19] 621 | fpByte[38] = endByte[59] 622 | fpByte[39] = endByte[27] 623 | fpByte[40] = endByte[34] 624 | fpByte[41] = endByte[2] 625 | fpByte[42] = endByte[42] 626 | fpByte[43] = endByte[10] 627 | fpByte[44] = endByte[50] 628 | fpByte[45] = endByte[18] 629 | fpByte[46] = endByte[58] 630 | fpByte[47] = endByte[26] 631 | fpByte[48] = endByte[33] 632 | fpByte[49] = endByte[1] 633 | fpByte[50] = endByte[41] 634 | fpByte[51] = endByte[9] 635 | fpByte[52] = endByte[49] 636 | fpByte[53] = endByte[17] 637 | fpByte[54] = endByte[57] 638 | fpByte[55] = endByte[25] 639 | fpByte[56] = endByte[32] 640 | fpByte[57] = endByte[0] 641 | fpByte[58] = endByte[40] 642 | fpByte[59] = endByte[8] 643 | fpByte[60] = endByte[48] 644 | fpByte[61] = endByte[16] 645 | fpByte[62] = endByte[56] 646 | fpByte[63] = endByte[24] 647 | return fpByte 648 | 649 | 650 | def getBoxBinary(i): 651 | switch = { 652 | 0: "0000", 653 | 1: "0001", 654 | 2: "0010", 655 | 3: "0011", 656 | 4: "0100", 657 | 5: "0101", 658 | 6: "0110", 659 | 7: "0111", 660 | 8: "1000", 661 | 9: "1001", 662 | 10: "1010", 663 | 11: "1011", 664 | 12: "1100", 665 | 13: "1101", 666 | 14: "1110", 667 | 15: "1111", 668 | } 669 | binary = switch[i] 670 | return binary 671 | 672 | 673 | def generateKeys(keyByte): 674 | key = [0] * (56) 675 | keys = [[0] * 48] * (16) 676 | 677 | loop = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1] 678 | 679 | for i in range(7): 680 | k = 7 681 | for j in range(8): 682 | key[i * 8 + j] = keyByte[8 * k + i] 683 | k -= 1 684 | k -= 1 685 | 686 | i = 0 687 | for z in range(16): 688 | i = z 689 | tempLeft = 0 690 | tempRight = 0 691 | for j in range(loop[i]): 692 | tempLeft = key[0] 693 | tempRight = key[28] 694 | for k in range(27): 695 | key[k] = key[k + 1] 696 | key[28 + k] = key[29 + k] 697 | 698 | key[27] = tempLeft 699 | key[55] = tempRight 700 | 701 | tempKey = [0] * (48) 702 | tempKey[0] = key[13] 703 | tempKey[1] = key[16] 704 | tempKey[2] = key[10] 705 | tempKey[3] = key[23] 706 | tempKey[4] = key[0] 707 | tempKey[5] = key[4] 708 | tempKey[6] = key[2] 709 | tempKey[7] = key[27] 710 | tempKey[8] = key[14] 711 | tempKey[9] = key[5] 712 | tempKey[10] = key[20] 713 | tempKey[11] = key[9] 714 | tempKey[12] = key[22] 715 | tempKey[13] = key[18] 716 | tempKey[14] = key[11] 717 | tempKey[15] = key[3] 718 | tempKey[16] = key[25] 719 | tempKey[17] = key[7] 720 | tempKey[18] = key[15] 721 | tempKey[19] = key[6] 722 | tempKey[20] = key[26] 723 | tempKey[21] = key[19] 724 | tempKey[22] = key[12] 725 | tempKey[23] = key[1] 726 | tempKey[24] = key[40] 727 | tempKey[25] = key[51] 728 | tempKey[26] = key[30] 729 | tempKey[27] = key[36] 730 | tempKey[28] = key[46] 731 | tempKey[29] = key[54] 732 | tempKey[30] = key[29] 733 | tempKey[31] = key[39] 734 | tempKey[32] = key[50] 735 | tempKey[33] = key[44] 736 | tempKey[34] = key[32] 737 | tempKey[35] = key[47] 738 | tempKey[36] = key[43] 739 | tempKey[37] = key[48] 740 | tempKey[38] = key[38] 741 | tempKey[39] = key[55] 742 | tempKey[40] = key[33] 743 | tempKey[41] = key[52] 744 | tempKey[42] = key[45] 745 | tempKey[43] = key[41] 746 | tempKey[44] = key[49] 747 | tempKey[45] = key[35] 748 | tempKey[46] = key[28] 749 | tempKey[47] = key[31] 750 | keys[i] = tempKey 751 | 752 | return keys 753 | 754 | 755 | if __name__ == "__main__": 756 | pass -------------------------------------------------------------------------------- /templates/monitor.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 余量监控 - 大连理工大学选课系统 7 | 213 | 214 | 215 |
216 |
217 |

🔍 余量监控

218 |

实时监控课程余量,自动抢课

219 |
220 | 🚀 自动抢课脚本 221 | 🔍 余量监控 222 | 🏠 主页 223 |
224 |
225 | 226 |
227 |
228 |
229 |

搜索课程

230 |
231 | 232 | 233 |
234 | 235 |
236 | 237 | 240 |
241 | 242 | 243 | 244 |
245 |
246 | 请搜索课程 247 |
248 |
249 |
250 |
251 | 252 |
253 |
254 |

监控设置

255 |
256 | 257 | 258 | 259 |
260 | 261 |
262 | 263 | 264 |
265 | 266 |
267 | 271 |
272 | 273 |
274 |
275 | 暂无监控课程 276 |
277 |
278 |
279 |
280 |
281 | 282 |
283 |

监控统计

284 |
285 |
286 |
0
287 |
监控课程
288 |
289 |
290 |
0
291 |
有余量
292 |
293 |
294 |
0
295 |
检查次数
296 |
297 |
298 |
0
299 |
成功选课
300 |
301 |
302 |
303 | 304 |
305 |

监控日志

306 |
307 | 308 |
309 |
310 |
311 |
312 |
313 | 314 | 655 | 656 | 657 | -------------------------------------------------------------------------------- /templates/auto_select.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 自动抢课 - 大连理工大学选课系统 7 | 190 | 191 | 192 |
193 |
194 |

🚀 自动抢课

195 |

前端控制队列 - 逐个调用API

196 |
197 | 🚀 自动抢课脚本 198 | 🔍 余量监控 199 | 🏠 主页 200 |
201 |
202 | 203 |
204 |
205 |
206 |

搜索课程

207 |
208 | 209 | 210 |
211 | 212 |
213 | 214 | 217 |
218 | 219 | 220 | 221 | 222 |
223 |
224 | 请搜索课程 225 |
226 |
227 |
228 |
229 | 230 |
231 |
232 |

操作队列

233 |
234 | 235 | 236 | 237 |
238 | 239 |
240 | 241 | 242 |
243 | 244 |
245 | 246 | 250 |
251 | 252 | 259 | 260 |
261 | 265 |
266 | 267 | 274 | 275 |
276 |
277 | 暂无操作 278 |
279 |
280 |
281 |
282 |
283 | 284 |
285 |

运行状态

286 |
287 | 288 |
289 |
290 |
291 |
292 |
293 | 294 | 764 | 765 | 766 | --------------------------------------------------------------------------------