feat:支持使用统一认证自动登录选课系统

This commit is contained in:
juzeon 2021-09-14 16:25:51 +08:00
父節點 dbda9e648f
當前提交 99f3963d08
共有 7 個檔案被更改,包括 1120 行新增28 行删除

查看文件

@ -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

檔案差異因為檔案過大而無法顯示 載入差異

查看文件

@ -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) {

查看文件

@ -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,