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 获取 | # 在 https://qmsg.zendee.cn 获取 | ||||||
| qmsgKey: "" | qmsgKey: "15rg6s5t1h6t1s5g66" | ||||||
| ## telegram api 域名 | ## telegram api 域名 | ||||||
| #tgApiDomain: "api.telegram.org" | #tgApiDomain: "api.telegram.org" | ||||||
| ## @BotFather 建立机器人获取 | ## @BotFather 建立机器人获取 | ||||||
|   | |||||||
| @@ -12,10 +12,13 @@ | |||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@types/node": "^16.9.1", |     "@types/node": "^16.9.1", | ||||||
|     "@types/qs": "^6.9.7", |     "@types/qs": "^6.9.7", | ||||||
|  |     "@types/tough-cookie": "^4.0.1", | ||||||
|     "axios": "^0.21.4", |     "axios": "^0.21.4", | ||||||
|  |     "axios-cookiejar-support": "^1.0.1", | ||||||
|     "axios-retry": "^3.1.9", |     "axios-retry": "^3.1.9", | ||||||
|     "dayjs": "^1.10.7", |     "dayjs": "^1.10.7", | ||||||
|     "qs": "^6.10.1", |     "qs": "^6.10.1", | ||||||
|  |     "tough-cookie": "^4.0.0", | ||||||
|     "ts-node": "^10.2.1", |     "ts-node": "^10.2.1", | ||||||
|     "typescript": "^4.4.3", |     "typescript": "^4.4.3", | ||||||
|     "yaml": "^1.10.2" |     "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 * as qs from "qs" | ||||||
| import dayjs from "dayjs" | import dayjs from "dayjs" | ||||||
| import {ICourse, IJsonCourse, TChannel} from "./types" | import {ICourse, IJsonCourse, TChannel} from "./types" | ||||||
| import {JsonCourseList} from "./poll" | import {JsonCourseList, reqBody} from "./poll" | ||||||
| import axiosRetry from "axios-retry" | 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({ | let sduAxios = axios.create({ | ||||||
|     headers: { |     headers: { | ||||||
|         'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', |         'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', | ||||||
|         'Cookie': appConfig.cookie, |  | ||||||
|         'X-Requested-With': 'XMLHttpRequest', |         '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/', |     baseURL: 'https://bkzhjx.wh.sdu.edu.cn/', | ||||||
|     timeout: 5000, |     timeout: 5000, | ||||||
|     transformResponse: data => { |     transformResponse: data => { | ||||||
|         return JSON.parse(data) |         try { | ||||||
|     } |             return JSON.parse(data) | ||||||
|     // maxRedirects: 0 |         } catch (_e) { | ||||||
|  |             return data | ||||||
|  |         } | ||||||
|  |     }, | ||||||
|  |     // maxRedirects: 10 | ||||||
| }) | }) | ||||||
|  |  | ||||||
| axiosRetry(sduAxios, { | axiosRetry(sduAxios, { | ||||||
|     retries: 2, |     retries: 2, | ||||||
|     shouldResetTimeout: true, |     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} | export {sduAxios} | ||||||
|   | |||||||
| @@ -1,10 +1,12 @@ | |||||||
| import {JsonCourseList, poll} from "./poll" | import {JsonCourseList, poll} from "./poll" | ||||||
| import {appConfig} from "./config" | import {appConfig} from "./config" | ||||||
| import {acquireProcess, monitProcess, replaceProcess} from "./actions" | import {acquireProcess, monitProcess, replaceProcess} from "./actions" | ||||||
| import {sleep} from "./includes" | import {resetCookie, sleep} from "./includes" | ||||||
| import {health} from "./health" | import {health} from "./health" | ||||||
|  |  | ||||||
| async function start() { | async function start() { | ||||||
|  |     console.log('开始获取Cookie') | ||||||
|  |     await resetCookie() | ||||||
|     console.log('开始启动轮询进程') |     console.log('开始启动轮询进程') | ||||||
|     poll() |     poll() | ||||||
|     while (true) { |     while (true) { | ||||||
|   | |||||||
							
								
								
									
										40
									
								
								src/poll.ts
									
									
									
									
									
								
							
							
						
						
									
										40
									
								
								src/poll.ts
									
									
									
									
									
								
							| @@ -1,5 +1,5 @@ | |||||||
| import {IJsonCourse} from "./types" | import {IJsonCourse} from "./types" | ||||||
| import {getTimeNow, logAndNotifyUser, sduAxios, sleep} from "./includes" | import {getTimeNow, logAndNotifyUser, resetCookie, sduAxios, sleep} from "./includes" | ||||||
| import {appConfig} from "./config" | import {appConfig} from "./config" | ||||||
| import * as qs from "qs" | import * as qs from "qs" | ||||||
|  |  | ||||||
| @@ -15,10 +15,13 @@ export const JsonCourseList = <{ bx: IJsonCourse[], xx: IJsonCourse[], rx: IJson | |||||||
| async function updateBx() { | async function updateBx() { | ||||||
|     try { |     try { | ||||||
|         let resp = await sduAxios.post('/jsxsd/xsxkkc/xsxkBxxk?1=1&kcxx=&skls=&skfs=&xqid=', reqBody) |         let resp = await sduAxios.post('/jsxsd/xsxkkc/xsxkBxxk?1=1&kcxx=&skls=&skfs=&xqid=', reqBody) | ||||||
|         JsonCourseList.bx.length = 0 |         if (resp.data.aaData.length) { | ||||||
|         JsonCourseList.bx.push(...resp.data.aaData) |             JsonCourseList.bx.length = 0 | ||||||
|  |             JsonCourseList.bx.push(...resp.data.aaData) | ||||||
|  |         } | ||||||
|     } catch (_e) { |     } catch (_e) { | ||||||
|         processError(_e as Error) |         // console.log(_e) | ||||||
|  |         await processError(_e as Error) | ||||||
|         console.log('获取必修JsonList失败 at ' + getTimeNow()) |         console.log('获取必修JsonList失败 at ' + getTimeNow()) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -26,10 +29,12 @@ async function updateBx() { | |||||||
| async function updateXx() { | async function updateXx() { | ||||||
|     try { |     try { | ||||||
|         let resp = await sduAxios.post('/jsxsd/xsxkkc/xsxkXxxk?1=1&kcxx=&skls=&skfs=&xqid=', reqBody) |         let resp = await sduAxios.post('/jsxsd/xsxkkc/xsxkXxxk?1=1&kcxx=&skls=&skfs=&xqid=', reqBody) | ||||||
|         JsonCourseList.xx.length = 0 |         if (resp.data.aaData.length) { | ||||||
|         JsonCourseList.xx.push(...resp.data.aaData) |             JsonCourseList.xx.length = 0 | ||||||
|  |             JsonCourseList.xx.push(...resp.data.aaData) | ||||||
|  |         } | ||||||
|     } catch (_e) { |     } catch (_e) { | ||||||
|         processError(_e as Error) |         await processError(_e as Error) | ||||||
|         console.log('获取限选JsonList失败 at ' + getTimeNow()) |         console.log('获取限选JsonList失败 at ' + getTimeNow()) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -37,10 +42,12 @@ async function updateXx() { | |||||||
| async function updateRx() { | async function updateRx() { | ||||||
|     try { |     try { | ||||||
|         let resp = await sduAxios.post('/jsxsd/xsxkkc/xsxkGgxxkxk?kcxx=&skls=&skxq=&skjc=&sfym=false&sfct=false&szjylb=&sfxx=true&skfs=&xqid=', reqBody) |         let resp = await sduAxios.post('/jsxsd/xsxkkc/xsxkGgxxkxk?kcxx=&skls=&skxq=&skjc=&sfym=false&sfct=false&szjylb=&sfxx=true&skfs=&xqid=', reqBody) | ||||||
|         JsonCourseList.rx.length = 0 |         if (resp.data.aaData.length) { | ||||||
|         JsonCourseList.rx.push(...resp.data.aaData) |             JsonCourseList.rx.length = 0 | ||||||
|  |             JsonCourseList.rx.push(...resp.data.aaData) | ||||||
|  |         } | ||||||
|     } catch (_e) { |     } catch (_e) { | ||||||
|         processError(_e as Error) |         await processError(_e as Error) | ||||||
|         console.log('获取任选JsonList失败 at ' + getTimeNow()) |         console.log('获取任选JsonList失败 at ' + getTimeNow()) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -61,12 +68,11 @@ export async function poll() { | |||||||
|     setImmediate(poll) |     setImmediate(poll) | ||||||
| } | } | ||||||
|  |  | ||||||
| function processError(e: Error) { | async function processError(e: Error) { | ||||||
|     if (errorCount != -1) { |     errorCount++ | ||||||
|         errorCount++ |     if (errorCount >= 30) { | ||||||
|         if (errorCount >= 30) { |         logAndNotifyUser('[error]出现轮询error过多,进行重新登录。上一次异常信息:' + e.message) | ||||||
|             logAndNotifyUser('[error]出现轮询error过多,请检查。上一次异常信息:' + e.message) |         await resetCookie() | ||||||
|             errorCount = -1 |         errorCount = 0 | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,3 +1,6 @@ | |||||||
|  | import {scalarOptions} from "yaml" | ||||||
|  | import Str = scalarOptions.Str | ||||||
|  |  | ||||||
| export type TChannel = 'bx' | 'xx' | 'rx' | export type TChannel = 'bx' | 'xx' | 'rx' | ||||||
|  |  | ||||||
| export interface ICourse { | export interface ICourse { | ||||||
| @@ -7,6 +10,8 @@ export interface ICourse { | |||||||
| } | } | ||||||
|  |  | ||||||
| export interface IAppConfig { | export interface IAppConfig { | ||||||
|  |     username: string, | ||||||
|  |     password: string, | ||||||
|     cookie: string, |     cookie: string, | ||||||
|     qmsgKey: string, |     qmsgKey: string, | ||||||
|     tgApiDomain: string, |     tgApiDomain: string, | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user