feat:支持使用统一认证自动登录选课系统
This commit is contained in:
父節點
dbda9e648f
當前提交
99f3963d08
@ -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
檔案差異因為檔案過大而無法顯示
載入差異
@ -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,
|
||||
|
載入中…
x
新增問題並參考
Block a user