Compare commits

...

8 Commits

Author SHA1 Message Date
9567fbb889 fix:将logAndNotifyUser用try包裹 2022-09-12 22:43:18 +08:00
eb39fb1c04 feat:resetCookie脚本 2022-02-28 08:35:03 +08:00
a0ef04285e perf:resetCookie加锁 2022-01-04 17:34:57 +08:00
5884aadc13 fix:调整顺序 2022-01-04 17:21:51 +08:00
3a927408c4 fix:别处登录cookie问题 2022-01-04 17:19:22 +08:00
baff19e775 refactor:使用telegram通知 2022-01-04 09:38:07 +08:00
f3ff4cc95e fix:登录检测问题 2021-09-16 18:52:49 +08:00
e6c922b881 perf:优化重新登录异常提醒逻辑 2021-09-14 16:36:33 +08:00
9 changed files with 213 additions and 71 deletions

4
.gitignore vendored
View File

@@ -2,4 +2,6 @@
/config.yaml /config.yaml
node_modules/* node_modules/*
/pnpm-lock.yaml /pnpm-lock.yaml
/arr.json /arr.json
backup
resetCookieRequest.txt

View File

@@ -5,7 +5,9 @@
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"test": "ts-node src/test.ts", "test": "ts-node src/test.ts",
"start": "ts-node src/index.ts" "start": "ts-node src/index.ts",
"draw": "ts-node src/draw.ts",
"resetCookie": "ts-node src/resetCookieRequest.ts"
}, },
"author": "", "author": "",
"license": "ISC", "license": "ISC",

46
src/draw.ts Normal file
View File

@@ -0,0 +1,46 @@
import {countStr, getTimeNow, logAndNotifyUser, resetCookie, sduAxios, sleep} from "./includes"
import * as fs from "fs"
export async function draw() {
while (true) {
let html = ''
try {
let resp = await sduAxios.get("jsxsd/xsxkjg/comeXkjglb?isktx=true")
html = JSON.stringify(resp.data)
} catch (e) {
console.log(e)
}
if (countStr(html, 'cqOper') < 2) {
logAndNotifyUser('登录失败重设cookie ' + getTimeNow())
try {
await resetCookie()
} catch (e) {
console.log(e)
}
continue
}
if (countStr(html, 'cqOper') > 2) {
logAndNotifyUser('可以抽签,正在进行抽签')
fs.writeFileSync('draw-' + Date.now() + '.html', html)
let arr = [
'2021202222605',// 英语
'2021202225984',// 周易
'2021202225817',// 刻瓷
'2021202222614',// 众智
'2021202222613',// 移动互联网
]
for (let id of arr) {
try {
let resp = await sduAxios.get('/jsxsd/xsxkjg/xscqOper?jx0404id=' + id + '&cqqk=3')// 3抽的中2抽不中
logAndNotifyUser(JSON.stringify(resp.data))
} catch (e) {
console.log(e)
}
}
}
console.log('轮询中:' + getTimeNow())
await sleep(1000)
}
}
draw()

View File

@@ -1,6 +1,6 @@
import {IJsonCourse} from "./types" import {IJsonCourse} from "./types"
import {JsonCourseList} from "./poll" import {JsonCourseList} from "./poll"
import {findJsonCourse} from "./includes" import {findJsonCourse, getTimeNow} from "./includes"
import {appConfig} from "./config" import {appConfig} from "./config"
import {scalarOptions} from "yaml" import {scalarOptions} from "yaml"
import Str = scalarOptions.Str import Str = scalarOptions.Str
@@ -42,7 +42,7 @@ export async function health() {
} }
} }
console.log('[health]实时总退课人次:' + totalExitCount + '' + shuffle(exitNames).slice(0, 3).join(',') + '等),总选课人次:' console.log('[health]实时总退课人次:' + totalExitCount + '' + shuffle(exitNames).slice(0, 3).join(',') + '等),总选课人次:'
+ totalAcquireCount + '' + shuffle(acquireNames).slice(0, 3).join(',') + '等)') + totalAcquireCount + '' + shuffle(acquireNames).slice(0, 3).join(',') + '等) at ' + getTimeNow())
} }
lastJsonCourseList = JSON.parse(JSON.stringify(JsonCourseList)) lastJsonCourseList = JSON.parse(JSON.stringify(JsonCourseList))
setTimeout(health, 60 * 1000) setTimeout(health, 60 * 1000)

View File

@@ -36,17 +36,21 @@ axiosRetry(sduAxios, {
export async function logAndNotifyUser(text: string) { export async function logAndNotifyUser(text: string) {
console.log(text) console.log(text)
await axios.post('https://qmsg.zendee.cn/send/' + appConfig.qmsgKey, qs.stringify({ // await axios.post('https://qmsg.zendee.cn/send/' + appConfig.qmsgKey, qs.stringify({
msg: text // msg: text
}), { // }), {
headers: { // headers: {
'Content-Type': 'application/x-www-form-urlencoded' // 'Content-Type': 'application/x-www-form-urlencoded'
} // }
}) // })
// await axios.post('https://' + appConfig.tgApiDomain + '/bot' + appConfig.tgApiKey + '/sendMessage', qs.stringify({ try {
// chat_id: appConfig.tgChatId, await axios.post('https://' + appConfig.tgApiDomain + '/bot' + appConfig.tgApiKey + '/sendMessage', qs.stringify({
// text: text chat_id: appConfig.tgChatId,
// })) text: text
}))
} catch (e) {
console.log(e)
}
} }
export function sleep(ms: number) { export function sleep(ms: number) {
@@ -80,6 +84,9 @@ export async function acquireCourse(course: IJsonCourse, channel: TChannel): Pro
return true return true
} else { } else {
logAndNotifyUser(resp.data.message) logAndNotifyUser(resp.data.message)
if (resp.data.message.includes('当前账号已在别处登录')) {
await resetCookie()// TODO 加锁、优化
}
return false return false
} }
} catch (e) { } catch (e) {
@@ -94,6 +101,9 @@ export async function exitCourse(course: IJsonCourse): Promise<boolean> {
return true return true
} else { } else {
logAndNotifyUser('[exitCourse]退课失败:' + resp.data.message) logAndNotifyUser('[exitCourse]退课失败:' + resp.data.message)
if (resp.data.message.includes('当前账号已在别处登录')) {
await resetCookie()
}
return false return false
} }
} catch (e) { } catch (e) {
@@ -102,42 +112,59 @@ export async function exitCourse(course: IJsonCourse): Promise<boolean> {
} }
} }
let resetCookieLock = false
export async function resetCookie() { export async function resetCookie() {
let cookieJar = new tough.CookieJar() if (resetCookieLock) {
let cookieAxios = axios.create({ console.log('resetCookie 已被锁定')
headers: { return
'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' resetCookieLock = true
}, try {
timeout: 6000, let cookieJar = new tough.CookieJar()
maxRedirects: 10 let cookieAxios = axios.create({
}) headers: {
axiosCookieJarSupport(cookieAxios) 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
cookieAxios.defaults.jar = cookieJar 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:92.0) Gecko/20100101 Firefox/92.0'
cookieAxios.defaults.withCredentials = true },
await cookieAxios.get('http://bkzhjx.wh.sdu.edu.cn/sso.jsp') timeout: 6000,
let casUrl = 'https://pass.sdu.edu.cn/cas/login?service=http%3A%2F%2Fbkzhjx.wh.sdu.edu.cn%2Fsso.jsp' maxRedirects: 10
let resp = await cookieAxios.get(casUrl) })
let ticket = /name="lt" value="(.*?)"/.exec(resp.data)?.[1] || null axiosCookieJarSupport(cookieAxios)
let rsa = strEnc(appConfig.username + appConfig.password + ticket, '1', '2', '3') cookieAxios.defaults.jar = cookieJar
resp = await cookieAxios.post(casUrl, qs.stringify({ cookieAxios.defaults.withCredentials = true
rsa, await cookieAxios.get('http://bkzhjx.wh.sdu.edu.cn/sso.jsp')
ul: appConfig.username.length, let casUrl = 'https://pass.sdu.edu.cn/cas/login?service=http%3A%2F%2Fbkzhjx.wh.sdu.edu.cn%2Fsso.jsp'
pl: appConfig.password.length, let resp = await cookieAxios.get(casUrl)
lt: ticket, let ticket = /name="lt" value="(.*?)"/.exec(resp.data)?.[1] || null
execution: 'e1s1', let rsa = strEnc(appConfig.username + appConfig.password + ticket, '1', '2', '3')
_eventId: 'submit' resp = await cookieAxios.post(casUrl, qs.stringify({
})) rsa,
let nextUrl = 'https://bkzhjx.wh.sdu.edu.cn' + /<a href="(.*?)">进入选课/.exec(resp.data)![1] ul: appConfig.username.length,
resp = await cookieAxios.get(nextUrl) pl: appConfig.password.length,
let xkId = /jrxk\('1','(.*?)'/.exec(resp.data)![1] lt: ticket,
nextUrl = 'https://bkzhjx.wh.sdu.edu.cn/jsxsd/xsxk/newXsxkzx?jx0502zbid=' + xkId execution: 'e1s1',
await cookieAxios.get(nextUrl) _eventId: 'submit'
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') let nextUrl = 'https://bkzhjx.wh.sdu.edu.cn' + /<a href="(.*?)">进入选课/.exec(resp.data)![1]
console.log('[cookie]取得Cookie' + cookieString) resp = await cookieAxios.get(nextUrl)
appConfig.cookie = cookieString let xkId = /jrxk\('1','(.*?)'/.exec(resp.data)![1]
sduAxios.defaults.headers['Cookie'] = cookieString 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
} catch (e) {
throw e
} finally {
resetCookieLock = false
}
}
export function countStr(str: string, word: string) {
return str.split(word).length - 1
} }
export {sduAxios} export {sduAxios}

View File

@@ -3,8 +3,10 @@ import {appConfig} from "./config"
import {acquireProcess, monitProcess, replaceProcess} from "./actions" import {acquireProcess, monitProcess, replaceProcess} from "./actions"
import {resetCookie, sleep} from "./includes" import {resetCookie, sleep} from "./includes"
import {health} from "./health" import {health} from "./health"
import * as fs from "fs"
async function start() { async function start() {
fs.writeFileSync('resetCookieRequest.txt','0')
console.log('开始获取Cookie') console.log('开始获取Cookie')
await resetCookie() await resetCookie()
console.log('开始启动轮询进程') console.log('开始启动轮询进程')
@@ -22,14 +24,19 @@ async function start() {
await sleep(50) await sleep(50)
} }
console.log('轮询进程启动完毕') console.log('轮询进程启动完毕')
for (let channel of appConfig.channels) {
console.log('channel: ' + channel + ': ' + JsonCourseList[channel].length)
}
console.log('开始启动用户进程') console.log('开始启动用户进程')
for (let course of appConfig.monit?.list || []) { for (let course of appConfig.monit?.list || []) {
monitProcess(course) monitProcess(course)
} }
for (let course of appConfig.acquire?.list || []) { for (let course of appConfig.acquire?.list || []) {
console.log('添加抢课课程:' + course.kch + ' ' + course.kxh)
acquireProcess(course) acquireProcess(course)
} }
if (appConfig.replace) { if (appConfig.replace) {
console.log('添加换课课程:' + appConfig.replace.exit.kch + ' ' + appConfig.replace.exit.kxh)
replaceProcess(appConfig.replace.list, appConfig.replace.exit) replaceProcess(appConfig.replace.list, appConfig.replace.exit)
} }
console.log('用户进程启动完毕') console.log('用户进程启动完毕')

View File

@@ -2,9 +2,11 @@ import {IJsonCourse} from "./types"
import {getTimeNow, logAndNotifyUser, resetCookie, 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"
import * as fs from "fs"
export let reqBody = 'sEcho=1&iColumns=15&sColumns=&iDisplayStart=0&iDisplayLength=2000&mDataProp_0=kch&mDataProp_1=kcmc&mDataProp_2=kxhnew&mDataProp_3=jkfs&mDataProp_4=xmmc&mDataProp_5=fzmc&mDataProp_6=ktmc&mDataProp_7=xf&mDataProp_8=skls&mDataProp_9=sksj&mDataProp_10=skdd&mDataProp_11=xqmc&mDataProp_12=syrs&mDataProp_13=ctsm&mDataProp_14=czOper' export let reqBody = 'sEcho=1&iColumns=15&sColumns=&iDisplayStart=0&iDisplayLength=2000&mDataProp_0=kch&mDataProp_1=kcmc&mDataProp_2=kxhnew&mDataProp_3=jkfs&mDataProp_4=xmmc&mDataProp_5=fzmc&mDataProp_6=ktmc&mDataProp_7=xf&mDataProp_8=skls&mDataProp_9=sksj&mDataProp_10=skdd&mDataProp_11=xqmc&mDataProp_12=syrs&mDataProp_13=ctsm&mDataProp_14=czOper'
let errorCount = 0 let currentErrorCount = 0
let totalErrorCount = 0
export const JsonCourseList = <{ bx: IJsonCourse[], xx: IJsonCourse[], rx: IJsonCourse[] }>{ export const JsonCourseList = <{ bx: IJsonCourse[], xx: IJsonCourse[], rx: IJsonCourse[] }>{
bx: [], bx: [],
@@ -18,11 +20,12 @@ async function updateBx() {
if (resp.data.aaData.length) { 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)
} else {
throw new Error(resp.data)
} }
} catch (_e) { } catch (_e) {
// console.log(_e)
await processError(_e as Error)
console.log('获取必修JsonList失败 at ' + getTimeNow()) console.log('获取必修JsonList失败 at ' + getTimeNow())
await processError(_e as Error)
} }
} }
@@ -32,10 +35,12 @@ async function updateXx() {
if (resp.data.aaData.length) { 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)
} else {
throw new Error(resp.data)
} }
} catch (_e) { } catch (_e) {
await processError(_e as Error)
console.log('获取限选JsonList失败 at ' + getTimeNow()) console.log('获取限选JsonList失败 at ' + getTimeNow())
await processError(_e as Error)
} }
} }
@@ -45,14 +50,17 @@ async function updateRx() {
if (resp.data.aaData.length) { 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)
} else {
throw new Error(resp.data)
} }
} catch (_e) { } catch (_e) {
await processError(_e as Error)
console.log('获取任选JsonList失败 at ' + getTimeNow()) console.log('获取任选JsonList失败 at ' + getTimeNow())
await processError(_e as Error)
} }
} }
export async function poll() { export async function poll() {
await checkResetCookieRequest()
if (appConfig.channels.includes('bx')) { if (appConfig.channels.includes('bx')) {
await updateBx() await updateBx()
await sleep(appConfig.interval) await sleep(appConfig.interval)
@@ -68,11 +76,35 @@ export async function poll() {
setImmediate(poll) setImmediate(poll)
} }
async function processError(e: Error) { async function checkResetCookieRequest() {
errorCount++ let req = parseInt(fs.readFileSync('resetCookieRequest.txt').toString())
if (errorCount >= 30) { if (req) {
logAndNotifyUser('[error]出现轮询error过多进行重新登录。上一次异常信息' + e.message) try {
await resetCookie() await resetCookie()
errorCount = 0 logAndNotifyUser('[resetCookie]成功')
fs.writeFileSync('resetCookieRequest.txt', '0')
} catch (e) {
logAndNotifyUser('[resetCookie]失败')
}
}
}
async function processError(e: Error) {
currentErrorCount++
if (currentErrorCount >= 30) {
console.log('[error]出现轮询error过多进行重新登录。上一次异常信息' + e.message)
try {
await resetCookie()
currentErrorCount = 0
} catch (_e) {
console.log('[error]重新登录失败')
}
if (totalErrorCount != -1) {
totalErrorCount++
}
}
if (totalErrorCount >= 10) {
logAndNotifyUser('[error]重新登录失败次数过多,请检查 at ' + getTimeNow())
totalErrorCount = -1
} }
} }

View File

@@ -0,0 +1,4 @@
import * as fs from "fs"
fs.writeFileSync('resetCookieRequest.txt','1')
console.log('已请求resetCookie')

View File

@@ -1,5 +1,5 @@
import {appConfig} from "./config" import {appConfig} from "./config"
import {sduAxios} from "./includes" import {countStr, logAndNotifyUser, sduAxios, sleep} from "./includes"
import {reqBody} from "./poll" import {reqBody} from "./poll"
import * as fs from "fs" import * as fs from "fs"
import {IJsonCourse} from "./types" import {IJsonCourse} from "./types"
@@ -19,15 +19,37 @@ async function test() {
} }
} }
// sduAxios.defaults.headers['Cookie'] = 'bzb_jsxsd=91B210AA0E876E6C973D8CD52DBB9A5C; bzb_njw=705AF60821F8C9A23CB373F2A0369B85; SERVERID=121'
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)
fs.writeFileSync('arr.json', JSON.stringify(resp.data.aaData, null, 2)) // console.log(resp.data)
let courseList: IJsonCourse[] = resp.data.aaData // fs.writeFileSync('arr.json', JSON.stringify(resp.data.aaData, null, 2))
courseList = courseList.filter(single => single.kcmc.includes('稷下创新') // let courseList: IJsonCourse[] = resp.data.aaData
&& single.xf >= 2) // courseList = courseList.filter(single => single.kcmc.includes('稷下创新')
for (let course of courseList) { // && single.xf >= 2)
console.log(course.kcmc + ' ' + course.kch + ' ' + course.kxh + ' ' + course.syrs + ' ' + course.xf) // for (let course of courseList) {
} // console.log(course.kcmc + ' ' + course.kch + ' ' + course.kxh + ' ' + course.syrs + ' ' + course.xf)
// }
// let lastData = ''
// let first=true
// let count=0
// while (true) {
// let resp = await sduAxios.get("jsxsd/xsxkjg/comeXkjglb?isktx=true", {
// headers: {
// 'Cookie': 'bzb_jsxsd=329BCA4E7D8E2421309B14C02587784F; bzb_njw=55FFC1D9D9B2EF5A10D3F28BEC294AE1; SERVERID=123'
// }
// })
// if (resp.data !== lastData && !first) {
// logAndNotifyUser('可以抽签了')
// first=false
// break
// }
// lastData = resp.data
// console.log('rotate '+count)
// count++
// await sleep(1000)
// }
let str=fs.readFileSync('backup/2.html')
console.log(countStr(JSON.stringify(str.toString()),'cqOper'))
} }
test() test()