forked from juzheng/sdu-course-bot
		
	feat:支持使用统一认证自动登录选课系统
This commit is contained in:
		| @@ -1,7 +1,9 @@ | ||||
| # 教务cookie | ||||
| cookie: "JSESSIONID=asd;" | ||||
| # 学号 | ||||
| username: "2020123456" | ||||
| # 密码 | ||||
| password: "abcdefg123123" | ||||
| # 在 https://qmsg.zendee.cn 获取 | ||||
| qmsgKey: "" | ||||
| qmsgKey: "15rg6s5t1h6t1s5g66" | ||||
| ## telegram api 域名 | ||||
| #tgApiDomain: "api.telegram.org" | ||||
| ## @BotFather 建立机器人获取 | ||||
|   | ||||
| @@ -12,10 +12,13 @@ | ||||
|   "dependencies": { | ||||
|     "@types/node": "^16.9.1", | ||||
|     "@types/qs": "^6.9.7", | ||||
|     "@types/tough-cookie": "^4.0.1", | ||||
|     "axios": "^0.21.4", | ||||
|     "axios-cookiejar-support": "^1.0.1", | ||||
|     "axios-retry": "^3.1.9", | ||||
|     "dayjs": "^1.10.7", | ||||
|     "qs": "^6.10.1", | ||||
|     "tough-cookie": "^4.0.0", | ||||
|     "ts-node": "^10.2.1", | ||||
|     "typescript": "^4.4.3", | ||||
|     "yaml": "^1.10.2" | ||||
|   | ||||
							
								
								
									
										1029
									
								
								src/des.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1029
									
								
								src/des.js
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -3,24 +3,31 @@ import {appConfig} from "./config" | ||||
| import * as qs from "qs" | ||||
| import dayjs from "dayjs" | ||||
| import {ICourse, IJsonCourse, TChannel} from "./types" | ||||
| import {JsonCourseList} from "./poll" | ||||
| import {JsonCourseList, reqBody} from "./poll" | ||||
| import axiosRetry from "axios-retry" | ||||
| import tough from "tough-cookie" | ||||
| import axiosCookieJarSupport from "axios-cookiejar-support" | ||||
| import {strEnc} from "./des" | ||||
| import {scalarOptions} from "yaml" | ||||
| import * as process from "process" | ||||
|  | ||||
| let sduAxios = axios.create({ | ||||
|     headers: { | ||||
|         'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', | ||||
|         'Cookie': appConfig.cookie, | ||||
|         'X-Requested-With': 'XMLHttpRequest', | ||||
|         'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:92.0) Gecko/20100101 Firefox/92.0' | ||||
|         'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:92.0) Gecko/20100101 Firefox/92.0', | ||||
|     }, | ||||
|     baseURL: 'https://bkzhjx.wh.sdu.edu.cn/', | ||||
|     timeout: 5000, | ||||
|     transformResponse: data => { | ||||
|         try { | ||||
|             return JSON.parse(data) | ||||
|         } catch (_e) { | ||||
|             return data | ||||
|         } | ||||
|     // maxRedirects: 0 | ||||
|     }, | ||||
|     // maxRedirects: 10 | ||||
| }) | ||||
|  | ||||
| axiosRetry(sduAxios, { | ||||
|     retries: 2, | ||||
|     shouldResetTimeout: true, | ||||
| @@ -95,4 +102,42 @@ export async function exitCourse(course: IJsonCourse): Promise<boolean> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| export async function resetCookie() { | ||||
|     let cookieJar = new tough.CookieJar() | ||||
|     let cookieAxios = axios.create({ | ||||
|         headers: { | ||||
|             'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', | ||||
|             'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:92.0) Gecko/20100101 Firefox/92.0' | ||||
|         }, | ||||
|         timeout: 6000, | ||||
|         maxRedirects: 10 | ||||
|     }) | ||||
|     axiosCookieJarSupport(cookieAxios) | ||||
|     cookieAxios.defaults.jar = cookieJar | ||||
|     cookieAxios.defaults.withCredentials = true | ||||
|     await cookieAxios.get('http://bkzhjx.wh.sdu.edu.cn/sso.jsp') | ||||
|     let casUrl = 'https://pass.sdu.edu.cn/cas/login?service=http%3A%2F%2Fbkzhjx.wh.sdu.edu.cn%2Fsso.jsp' | ||||
|     let resp = await cookieAxios.get(casUrl) | ||||
|     let ticket = /name="lt" value="(.*?)"/.exec(resp.data)?.[1] || null | ||||
|     let rsa = strEnc(appConfig.username + appConfig.password + ticket, '1', '2', '3') | ||||
|     resp = await cookieAxios.post(casUrl, qs.stringify({ | ||||
|         rsa, | ||||
|         ul: appConfig.username.length, | ||||
|         pl: appConfig.password.length, | ||||
|         lt: ticket, | ||||
|         execution: 'e1s1', | ||||
|         _eventId: 'submit' | ||||
|     })) | ||||
|     let nextUrl = 'https://bkzhjx.wh.sdu.edu.cn' + /<a href="(.*?)">进入选课/.exec(resp.data)![1] | ||||
|     resp = await cookieAxios.get(nextUrl) | ||||
|     let xkId = /jrxk\('1','(.*?)'/.exec(resp.data)![1] | ||||
|     nextUrl = 'https://bkzhjx.wh.sdu.edu.cn/jsxsd/xsxk/newXsxkzx?jx0502zbid=' + xkId | ||||
|     await cookieAxios.get(nextUrl) | ||||
|     await cookieAxios.get('https://bkzhjx.wh.sdu.edu.cn/jsxsd/xsxk/selectBottom?jx0502zbid=' + xkId) | ||||
|     let cookieString = cookieJar.getCookieStringSync('https://bkzhjx.wh.sdu.edu.cn/jsxsd/xsxk') | ||||
|     console.log('[cookie]取得Cookie:' + cookieString) | ||||
|     appConfig.cookie = cookieString | ||||
|     sduAxios.defaults.headers['Cookie'] = cookieString | ||||
| } | ||||
|  | ||||
| export {sduAxios} | ||||
|   | ||||
| @@ -1,10 +1,12 @@ | ||||
| import {JsonCourseList, poll} from "./poll" | ||||
| import {appConfig} from "./config" | ||||
| import {acquireProcess, monitProcess, replaceProcess} from "./actions" | ||||
| import {sleep} from "./includes" | ||||
| import {resetCookie, sleep} from "./includes" | ||||
| import {health} from "./health" | ||||
|  | ||||
| async function start() { | ||||
|     console.log('开始获取Cookie') | ||||
|     await resetCookie() | ||||
|     console.log('开始启动轮询进程') | ||||
|     poll() | ||||
|     while (true) { | ||||
|   | ||||
							
								
								
									
										24
									
								
								src/poll.ts
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								src/poll.ts
									
									
									
									
									
								
							| @@ -1,5 +1,5 @@ | ||||
| import {IJsonCourse} from "./types" | ||||
| import {getTimeNow, logAndNotifyUser, sduAxios, sleep} from "./includes" | ||||
| import {getTimeNow, logAndNotifyUser, resetCookie, sduAxios, sleep} from "./includes" | ||||
| import {appConfig} from "./config" | ||||
| import * as qs from "qs" | ||||
|  | ||||
| @@ -15,10 +15,13 @@ export const JsonCourseList = <{ bx: IJsonCourse[], xx: IJsonCourse[], rx: IJson | ||||
| async function updateBx() { | ||||
|     try { | ||||
|         let resp = await sduAxios.post('/jsxsd/xsxkkc/xsxkBxxk?1=1&kcxx=&skls=&skfs=&xqid=', reqBody) | ||||
|         if (resp.data.aaData.length) { | ||||
|             JsonCourseList.bx.length = 0 | ||||
|             JsonCourseList.bx.push(...resp.data.aaData) | ||||
|         } | ||||
|     } catch (_e) { | ||||
|         processError(_e as Error) | ||||
|         // console.log(_e) | ||||
|         await processError(_e as Error) | ||||
|         console.log('获取必修JsonList失败 at ' + getTimeNow()) | ||||
|     } | ||||
| } | ||||
| @@ -26,10 +29,12 @@ async function updateBx() { | ||||
| async function updateXx() { | ||||
|     try { | ||||
|         let resp = await sduAxios.post('/jsxsd/xsxkkc/xsxkXxxk?1=1&kcxx=&skls=&skfs=&xqid=', reqBody) | ||||
|         if (resp.data.aaData.length) { | ||||
|             JsonCourseList.xx.length = 0 | ||||
|             JsonCourseList.xx.push(...resp.data.aaData) | ||||
|         } | ||||
|     } catch (_e) { | ||||
|         processError(_e as Error) | ||||
|         await processError(_e as Error) | ||||
|         console.log('获取限选JsonList失败 at ' + getTimeNow()) | ||||
|     } | ||||
| } | ||||
| @@ -37,10 +42,12 @@ async function updateXx() { | ||||
| async function updateRx() { | ||||
|     try { | ||||
|         let resp = await sduAxios.post('/jsxsd/xsxkkc/xsxkGgxxkxk?kcxx=&skls=&skxq=&skjc=&sfym=false&sfct=false&szjylb=&sfxx=true&skfs=&xqid=', reqBody) | ||||
|         if (resp.data.aaData.length) { | ||||
|             JsonCourseList.rx.length = 0 | ||||
|             JsonCourseList.rx.push(...resp.data.aaData) | ||||
|         } | ||||
|     } catch (_e) { | ||||
|         processError(_e as Error) | ||||
|         await processError(_e as Error) | ||||
|         console.log('获取任选JsonList失败 at ' + getTimeNow()) | ||||
|     } | ||||
| } | ||||
| @@ -61,12 +68,11 @@ export async function poll() { | ||||
|     setImmediate(poll) | ||||
| } | ||||
|  | ||||
| function processError(e: Error) { | ||||
|     if (errorCount != -1) { | ||||
| async function processError(e: Error) { | ||||
|     errorCount++ | ||||
|     if (errorCount >= 30) { | ||||
|             logAndNotifyUser('[error]出现轮询error过多,请检查。上一次异常信息:' + e.message) | ||||
|             errorCount = -1 | ||||
|         } | ||||
|         logAndNotifyUser('[error]出现轮询error过多,进行重新登录。上一次异常信息:' + e.message) | ||||
|         await resetCookie() | ||||
|         errorCount = 0 | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,3 +1,6 @@ | ||||
| import {scalarOptions} from "yaml" | ||||
| import Str = scalarOptions.Str | ||||
|  | ||||
| export type TChannel = 'bx' | 'xx' | 'rx' | ||||
|  | ||||
| export interface ICourse { | ||||
| @@ -7,6 +10,8 @@ export interface ICourse { | ||||
| } | ||||
|  | ||||
| export interface IAppConfig { | ||||
|     username: string, | ||||
|     password: string, | ||||
|     cookie: string, | ||||
|     qmsgKey: string, | ||||
|     tgApiDomain: string, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user