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 获取 # 在 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

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

查看文件

@ -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 => {
try {
return JSON.parse(data) return JSON.parse(data)
} catch (_e) {
return data
} }
// maxRedirects: 0 },
// 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) {

查看文件

@ -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)
if (resp.data.aaData.length) {
JsonCourseList.bx.length = 0 JsonCourseList.bx.length = 0
JsonCourseList.bx.push(...resp.data.aaData) 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)
if (resp.data.aaData.length) {
JsonCourseList.xx.length = 0 JsonCourseList.xx.length = 0
JsonCourseList.xx.push(...resp.data.aaData) 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)
if (resp.data.aaData.length) {
JsonCourseList.rx.length = 0 JsonCourseList.rx.length = 0
JsonCourseList.rx.push(...resp.data.aaData) 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)
errorCount = -1 await resetCookie()
} 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,