|
@@ -0,0 +1,198 @@
|
|
|
+#!/usr/bin/env python
|
|
|
+# -*- encoding: utf-8 -*-
|
|
|
+'''
|
|
|
+@Contact : liuyuqi.gov@msn.cn
|
|
|
+@Time : 2022/05/31 01:04:55
|
|
|
+@License : Copyright © 2017-2022 liuyuqi. All Rights Reserved.
|
|
|
+@Desc : cuit.edu.cn 自动选课
|
|
|
+'''
|
|
|
+from asyncio.log import logger
|
|
|
+import platform
|
|
|
+from auto_cuit.libs.json_conf import JsonConf
|
|
|
+from auto_cuit import api
|
|
|
+import requests
|
|
|
+import os
|
|
|
+import sys
|
|
|
+import re
|
|
|
+import json
|
|
|
+import time
|
|
|
+import random
|
|
|
+
|
|
|
+
|
|
|
+class Cuit(object):
|
|
|
+
|
|
|
+ def __init__(self, configPath="conf/config.josn"):
|
|
|
+ self.sess = requests.session()
|
|
|
+ self.conf = JsonConf(configPath)
|
|
|
+ self.sess.cookies = self.conf.data['cookies']
|
|
|
+ self.sess.headers = {
|
|
|
+ "X-Requested-With": "XMLHttpRequest",
|
|
|
+ "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.0 Safari/537.36 Edg/84.0.521.0"
|
|
|
+ }
|
|
|
+
|
|
|
+ def run(self):
|
|
|
+ profiledId = self.conf.data['profiled_id']
|
|
|
+ courseName = self.conf.data['course_name']
|
|
|
+
|
|
|
+ # 验证码
|
|
|
+ cnt = 0
|
|
|
+ while True:
|
|
|
+ pic = self.getCaptcha()
|
|
|
+ ocrResult = self.postOCRPic(pic)
|
|
|
+ print('OCR: ' + ocrResult['result'])
|
|
|
+
|
|
|
+ checkResult = self.checkCaptcha(ocrResult['result'], profiledId)
|
|
|
+ if checkResult:
|
|
|
+ break
|
|
|
+ cnt += 1
|
|
|
+ time.sleep(0.5)
|
|
|
+ if cnt % 5 == 0:
|
|
|
+ print('验证码错误次数过多, 等待5秒')
|
|
|
+ time.sleep(5)
|
|
|
+
|
|
|
+ print('验证码检测通过,等待1秒')
|
|
|
+ time.sleep(1)
|
|
|
+ # 检测开放状态
|
|
|
+ print('获取lessonId')
|
|
|
+ lessonId = self.courseName2Id(profiledId, courseName)
|
|
|
+ if lessonId == None:
|
|
|
+ print('没有找到相关课程:' + courseName)
|
|
|
+ exit(1)
|
|
|
+
|
|
|
+ print('检测选课开放状态')
|
|
|
+ cnt = 0
|
|
|
+ while True:
|
|
|
+ if self.isAvailable(ocrResult['result'], profiledId):
|
|
|
+ break
|
|
|
+ pass
|
|
|
+ cnt += 1
|
|
|
+ print('没有到选课时间,等待5秒 - ' + str(cnt))
|
|
|
+ time.sleep(5)
|
|
|
+
|
|
|
+ # 抢课
|
|
|
+ print('开始抢课')
|
|
|
+
|
|
|
+ i = 0
|
|
|
+ while True:
|
|
|
+ i += 1
|
|
|
+ print(i)
|
|
|
+ if self.fuckCourse(str(profiledId), str(lessonId)):
|
|
|
+ break
|
|
|
+ time.sleep(0.5)
|
|
|
+ if i >= 20:
|
|
|
+ i = 0
|
|
|
+ if platform.system().lower() == 'linux':
|
|
|
+ os.system("clear")
|
|
|
+ elif platform.system().lower() == 'windows':
|
|
|
+ os.system("cls")
|
|
|
+ pass
|
|
|
+ pass
|
|
|
+ pass
|
|
|
+
|
|
|
+ def fuckCourse(self, profiledId, lessonId):
|
|
|
+ try:
|
|
|
+ body = {
|
|
|
+ "optype": "true",
|
|
|
+ "operator0": lessonId + ":true:0",
|
|
|
+ "lesson0": lessonId,
|
|
|
+ "schLessonGroup_" + lessonId: "undefined"
|
|
|
+ }
|
|
|
+ req = requests.post(api.chooseCourse % profiledId,
|
|
|
+ data=body, timeout=5, allow_redirects=False)
|
|
|
+ req.encoding = 'utf-8'
|
|
|
+
|
|
|
+ html = req.text
|
|
|
+ ret = re.search(r"margin:auto;\">\n\t\t\t\t(.*)<\/br>", html)
|
|
|
+ if ret == None:
|
|
|
+ print("cookie过期")
|
|
|
+ exit(-1)
|
|
|
+ pass
|
|
|
+ print(ret.group(1))
|
|
|
+ req.close()
|
|
|
+ if '成功' in ret.group(1):
|
|
|
+ print('get')
|
|
|
+ return True
|
|
|
+ except Exception as err:
|
|
|
+ print("出错")
|
|
|
+ print(err)
|
|
|
+ return False
|
|
|
+ pass
|
|
|
+ pass
|
|
|
+
|
|
|
+ def getCaptcha(self):
|
|
|
+ try:
|
|
|
+ picReq = self.sess.get(url=api.getCaptcha, timeout=5)
|
|
|
+ except Exception as err:
|
|
|
+ print(err)
|
|
|
+ return picReq.content
|
|
|
+
|
|
|
+ def postOCRPic(self, pic):
|
|
|
+ url = self.ocrServer
|
|
|
+ files = {'captcha': pic}
|
|
|
+ data = {
|
|
|
+ 'enctype': 'multipart/form-data',
|
|
|
+ 'name': 'captcha'
|
|
|
+ }
|
|
|
+ ocrReq = requests.post(url=url, data=data, files=files)
|
|
|
+ return json.loads(ocrReq.content)
|
|
|
+
|
|
|
+ def checkCaptcha(self, captcha, profiledId):
|
|
|
+
|
|
|
+ data = {
|
|
|
+ 'captcha_response': captcha,
|
|
|
+ 'electionProfile.id': profiledId
|
|
|
+ }
|
|
|
+ try:
|
|
|
+ checkReq = requests.post(
|
|
|
+ url=api.checkCode, data=data, allow_redirects=False, timeout=5)
|
|
|
+ except Exception as err:
|
|
|
+ logger.error(err)
|
|
|
+
|
|
|
+ # print(checkReq.text)
|
|
|
+ # 未登录与验证码错误都是302,但Location去向不同
|
|
|
+ if checkReq.status_code == 200:
|
|
|
+ return True
|
|
|
+ elif 'sso' in checkReq.headers['Location']:
|
|
|
+ # 转到统一登录中心
|
|
|
+ print('cookie失效!!!')
|
|
|
+ exit(1)
|
|
|
+ return False
|
|
|
+
|
|
|
+ def isAvailable(self, captcha, profiledId):
|
|
|
+ '''
|
|
|
+ 检测登录状态
|
|
|
+ '''
|
|
|
+ data = {
|
|
|
+ 'captcha_response': captcha,
|
|
|
+ 'electionProfile.id': profiledId
|
|
|
+ }
|
|
|
+ try:
|
|
|
+ checkReq = self.sess.post(
|
|
|
+ url=api.checkCode, data=data, allow_redirects=False, timeout=5)
|
|
|
+ # 未登录与验证码错误都是302,但Location去向不同
|
|
|
+ if checkReq.status_code == 200:
|
|
|
+ return '不在选课时间内' not in checkReq.text
|
|
|
+ elif 'sso' in checkReq.headers['Location']:
|
|
|
+ # 转到统一登录中心
|
|
|
+ print('cookie失效')
|
|
|
+ False
|
|
|
+ except Exception as err:
|
|
|
+ logger.error(err)
|
|
|
+ return False
|
|
|
+
|
|
|
+ return False
|
|
|
+
|
|
|
+ def courseName2Id(self, profileId, courseName):
|
|
|
+ try:
|
|
|
+ courseListReq = self.sess.get(
|
|
|
+ url=api.getCourseList % str(profileId), allow_redirects=False, timeout=5)
|
|
|
+ except Exception as err:
|
|
|
+ print(err)
|
|
|
+ courseList = courseListReq.text
|
|
|
+ jsData = execjs.compile(courseList)
|
|
|
+ lessonJSONs = jsData.eval('lessonJSONs')
|
|
|
+ # print(lessonJSONs)
|
|
|
+ for lesson in lessonJSONs:
|
|
|
+ if courseName in lesson['name']:
|
|
|
+ return lesson['id']
|
|
|
+ return None
|