webvpn登录预约系统
// ==UserScript==
// @name webvpn登录预约系统
// @namespace https://bbs.tampermonkey.net.cn/
// @version 0.1.0
// @description try to take over the world!
// @author tfsn20
// @crontab 1 7 * * *
// @grant GM_xmlhttpRequest
// @grant GM_log
// @grant GM_notification
// @grant GM_getValue
// ==/UserScript==
/* ==UserConfig==
账号和密码:
studentIDs:
title: 账号
default: 多个使用英文逗号分割, 如332218391, 342912393
type: textarea
IDPasswords:
title: 密码
default: 和账号一一对应, 多个使用英文逗号分割, 如332218391, {gilight}_NSskdf
password: true
时间和场地:
time:
title: 开始时间
default: 多个使用英文逗号分割, 如15, 16
type: textarea
priorChoices:
title: 优先场地
default: 多个使用英文逗号分割, 不想选择某个场地就不要填写哪个场地的序号, 比如想让按照6, 5, 4, 8, 9, 7的顺序选择, 但不想选1, 2, 3则填入6, 5, 4, 8, 9, 7
type: textarea
less:
title: 至少场地位数
default: 填入2表示场地的可用位数少于2则不预约
type: textarea
推送通知:
AccessKey:
title: AccessKey
default: 在sct.icodef.com/push/key获取
password: true
tags:
title: 设备标签名称
default: 在sct.icodef.com/push/device获取, 如有多个使用英文逗号分割, 如体育馆预约, 核酸结果
type: textarea
==/UserConfig== */
return new Promise(async (resolve, reject) => {
function g_(str) { return str.replace(/,\s*$/, '').replace(/,\s*/g, ',').split(',') }
let studentIDs = g_(GM_getValue('账号和密码.studentIDs'));
let IDPasswords = g_(GM_getValue('账号和密码.IDPasswords'));
IDPasswords = IDPasswords.map(function (str) {
return str.includes('{gilight}_') ? str : `{gilight}_${btoa(encodeURIComponent(str))}`;
});
let time = GM_getValue('时间和场地.time').replace(/,\s*$/, '').replace(/,\s*/g, ',').split(',').map(Number);
let priorChoices = GM_getValue('时间和场地.priorChoices').replace(/,\s*$/, '').replace(/,\s*/g, ',').split(',').map(Number);
let less = GM_getValue('时间和场地.less') - 0;
let access_key = GM_getValue('推送通知.AccessKey');
function buildQueryStringFromArray(tagsArray) {
// 使用数组的reduce方法来构建查询字符串
return tagsArray.reduce((queryString, tag, index) => {
// 根据索引判断当前标签是不是第一个,如果不是第一个,则前面加上"&tags="
const prefix = index === 0 ? '' : '&tags=';
return `${queryString}${prefix}${tag}`;
}, '');
}
let tags = g_(GM_getValue('推送通知.tags'));
tags = buildQueryStringFromArray(tags)
console.log(studentIDs, IDPasswords, time, priorChoices, less)
const tools = {
sleep: t => new Promise(res => setTimeout(res, t)),// 箭头函数体只有一句,可以省略return
net: {
change: (str, withCookie) => {
str = str.trim(); //去除文本首位\s
let method = str.match(/(.*?)\s/)[1];
let url = str.match(/\s(.*?)\s/)[1];
str = str.replace(/.*?\n/, ''); //去除第一行
let data = /\n\s*\n\s*(.*)$/m.test(str) ? str.slice(str.match(/\n\s*\n\s*(.*)$/m).index).trim() : ''; //获取请求体,没有则返回'', 用slice截取防止请求体含有\n时出错
str = str.replace(new RegExp(data), '').trim(); //去除data
str = str.replace(/^(\s*?)(\S.*)/gm, `$2`); //去除每行行首的\s
str = str.replace(/\sContent-Length:.*/im, ''); //去除Content-Length所在行,不用/^\sContent-Length:./im, 这样做会多一个空白行避免一些问题
str = withCookie ? str : str.replace(/\s*cookie:.*/im, '') //去除cookie所在行,不用/^\s*cookie.*/im, 这样做会多一个空白行
let headers = {}, h = str.match(/(\S*?):\s*(\S.*)/mg);
// GM_log(h)
h.forEach(e => {
let t = e.match(/(\S*?):\s*(.*)/);
headers[t[1]] = t[2].replace(/\s*$/, '');//去除行尾\s,避免一下总没错吧
});
//去除浏览器默认携带的请求头
if (!/origin:/i.test(str)) headers.origin = '';//脚本猫默认有一个拓展origin,这里去掉
if (!/dnt/i.test(str)) headers.dnt = '';
if (!/referer/i.test(str)) headers.referer = '';
//if (!/accept/i.test(str)) headers.accept='';
if (!/user-agent/i.test(str)) headers['user-agent'] = '';
if (!/sec-ch-ua/i.test(str)) headers['sec-ch-ua'] = '';
if (!/sec-ch-ua-mobile/i.test(str)) headers['sec-ch-ua-mobile'] = '';
if (!/sec-ch-ua-platform/i.test(str)) headers['sec-ch-ua-platform'] = '';
if (!/sec-fetch-dest/i.test(str)) headers['sec-fetch-dest'] = '';
if (!/sec-fetch-mode/i.test(str)) headers['sec-fetch-mode'] = '';
if (!/sec-fetch-site/i.test(str)) headers['sec-fetch-site'] = '';
//if (!/accept-language/i.test(str)) headers['accept-language']='';
if (!/accept-encoding/i.test(str)) headers['accept-encoding'] = '';
return { url, method, data, headers }
},
}
};
tools.net.send = (str, onload = (xhr => xhr), anonymous = false, withCookie = true) => {
let t = tools.net.change(str, withCookie);
return new Promise((resolve, reject) => {
t.anonymous = anonymous;
t.onload = (xhr) => {
resolve(onload(xhr))
};
t.onerror = _ => { GM_log('some errors:' + _) };
t.onreadystatechange = _ => {
if (_.status == 0) resolve('联系服务器之前发生了错误')
}
GM_xmlhttpRequest(t)
})
};
// 账号登录和预约系统认证
async function loginWebvpnAndReservation(studentID = '332211020770', IDPassword = '{gilight}_MjgyOTk5MjU1MEFiYy4u') {
// 获取登录参数
let loginPara = await tools.net.send(`
GET https://webvpn.zzuli.edu.cn/http/77726476706e69737468656265737421fbee52d23d2a7d5c77468ca88d1b203b/cas/login?service=https%3A%2F%2Fwebvpn.zzuli.edu.cn%2Flogin%3Fcas_login%3Dtrue HTTP/1.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: zh-CN,zh;q=0.9
Connection: keep-alive
DNT: 1
Host: webvpn.zzuli.edu.cn
Referer: https://webvpn.zzuli.edu.cn/login
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0
sec-ch-ua: "Microsoft Edge";v="125", "Chromium";v="125", "Not.A/Brand";v="24"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
`)
if (loginPara == '联系服务器之前发生了错误') {
return '联系服务器之前发生了错误'
} else if (loginPara.finalUrl == 'https://webvpn.zzuli.edu.cn/') {
return '已经登录'
}
let lt = loginPara.response.match(/value="(LT-.*-jinghua)"/)[1], execution = loginPara.response.match(/name="execution" value="(.*)"/)[1];
console.log('成功获取登录参数');
// 登录重定向
let login302 = await tools.net.send(`
POST https://webvpn.zzuli.edu.cn/http/77726476706e69737468656265737421fbee52d23d2a7d5c77468ca88d1b203b/cas/login?service=https%3A%2F%2Fwebvpn.zzuli.edu.cn%2Flogin%3Fcas_login%3Dtrue HTTP/1.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,ja;q=0.5,de-DE;q=0.4,de;q=0.3
Cache-Control: max-age=0
Connection: keep-alive
Content-Length: 169
Content-Type: application/x-www-form-urlencoded
DNT: 1
Host: webvpn.zzuli.edu.cn
Origin: https://webvpn.zzuli.edu.cn
Referer: https://webvpn.zzuli.edu.cn/http/77726476706e69737468656265737421fbee52d23d2a7d5c77468ca88d1b203b/cas/login?service=https%3A%2F%2Fwebvpn.zzuli.edu.cn%2Flogin%3Fcas_login%3Dtrue
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0
sec-ch-ua: "Microsoft Edge";v="125", "Chromium";v="125", "Not.A/Brand";v="24"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
username=${studentID}&password=${IDPassword}=&secret=&accountLogin=<=${lt}&execution=${execution}&_eventId=submit
`)
console.log('成功登录webvpn');
// SetSource
await tools.net.send(`
GET https://webvpn.zzuli.edu.cn/http/77726476706e69737468656265737421f3f05885692a72457201c7a99c406d3651/User/SetSource?vpn-12-o1-cgyy.zzuli.edu.cn&Source=2&_=${Date.now()} HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,ja;q=0.5,de-DE;q=0.4,de;q=0.3
Cache-Control: no-cache
Connection: keep-alive
DNT: 1
Host: webvpn.zzuli.edu.cn
Pragma: no-cache
Referer: https://webvpn.zzuli.edu.cn/http/77726476706e69737468656265737421f3f05885692a72457201c7a99c406d3651/Views/User/UserChoose.html?wrdrecordvisit=1714397297000
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0
X-Requested-With: XMLHttpRequest
sec-ch-ua: "Microsoft Edge";v="125", "Chromium";v="125", "Not.A/Brand";v="24"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
`)
// 体育管重定向
let sport302 = await tools.net.send(`
GET https://webvpn.zzuli.edu.cn/http/77726476706e69737468656265737421f3f05885692a72457201c7a99c406d3651/User/UserChoose?LoginType=1 HTTP/1.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,ja;q=0.5,de-DE;q=0.4,de;q=0.3
Cache-Control: no-cache
Connection: keep-alive
DNT: 1
Host: webvpn.zzuli.edu.cn
Pragma: no-cache
Referer: https://webvpn.zzuli.edu.cn/http/77726476706e69737468656265737421f3f05885692a72457201c7a99c406d3651/Views/User/UserChoose.html?wrdrecordvisit=1714376686000
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0
sec-ch-ua: "Microsoft Edge";v="125", "Chromium";v="125", "Not.A/Brand";v="24"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
`);
console.log('成功登录体育馆预约系统')
return true
}
// 场地预约
function reserve(fieldName, beginTime, fieldNumber = 1) {
let fieldNo = (fieldName - 1) * 4 + fieldNumber, endTime = beginTime + 1;
fieldNo = fieldNo < 10 ? '0' + fieldNo : '' + fieldNo
return new Promise((resolve, reject) => {
resolve(tools.net.send(`
GET https://webvpn.zzuli.edu.cn/http/77726476706e69737468656265737421f3f05885692a72457201c7a99c406d3651/Field/OrderField?vpn-12-o1-cgyy.zzuli.edu.cn&checkdata=[{"FieldNo":"YMQ0${fieldNo}","FieldTypeNo":"03","FieldName":"羽毛球0${fieldName}-${fieldNumber}","BeginTime":"${beginTime}:30","Endtime":"${endTime}:30","Price":"0.00"}]&dateadd=0&VenueNo=001 HTTP/1.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,ja;q=0.5,de-DE;q=0.4,de;q=0.3
Cache-Control: no-cache
Connection: keep-alive
DNT: 1
Host: webvpn.zzuli.edu.cn
Pragma: no-cache
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0
sec-ch-ua: "Microsoft Edge";v="125", "Chromium";v="125", "Not.A/Brand";v="24"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
`))
})
}
// 场地支付
function pay(oid, studentID = '332211020770') {
return new Promise((resolve, reject) => {
resolve(tools.net.send(`
GET https://webvpn.zzuli.edu.cn/http/77726476706e69737468656265737421f3f05885692a72457201c7a99c406d3651/Field/CardPay?vpn-12-o1-cgyy.zzuli.edu.cn&PayNo=02&Money=0&CardMoney=1&Count=0.00&MemberNo=&CardNo=${studentID}&BillType=100&Password=&IsCheckPassword=0&OID=${oid}&VenueNo=001&PayDiscount=100&IsUseMemberType=1&EWMNum=1&_=${Date.now()} HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,ja;q=0.5,de-DE;q=0.4,de;q=0.3
Cache-Control: no-cache
Connection: keep-alive
DNT: 1
Host: webvpn.zzuli.edu.cn
Pragma: no-cache
Referer: https://webvpn.zzuli.edu.cn/http/77726476706e69737468656265737421f3f05885692a72457201c7a99c406d3651/Views/Pay/PayField.html?OID=${oid}&VenueNo=001
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0
X-Requested-With: XMLHttpRequest
sec-ch-ua: "Microsoft Edge";v="125", "Chromium";v="125", "Not.A/Brand";v="24"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
`))
})
}
// 登出账号
function logout() {
return new Promise((resolve, reject) => {
resolve(tools.net.send(`
GET https://webvpn.zzuli.edu.cn/logout HTTP/1.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,ja;q=0.5,de-DE;q=0.4,de;q=0.3
Cache-Control: no-cache
Connection: keep-alive
DNT: 1
Host: webvpn.zzuli.edu.cn
Pragma: no-cache
Referer: https://webvpn.zzuli.edu.cn/
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0
sec-ch-ua: "Microsoft Edge";v="125", "Chromium";v="125", "Not.A/Brand";v="24"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
`))
})
}
// TimePeriod=0,1,2分为上午下午(11)晚上(18)
function venueState(TimePeriod = 1) {
return new Promise((resolve, reject) => {
resolve(tools.net.send(`
GET https://webvpn.zzuli.edu.cn/http/77726476706e69737468656265737421f3f05885692a72457201c7a99c406d3651/Field/GetVenueState?vpn-12-o1-cgyy.zzuli.edu.cn&dateadd=0&TimePeriod=${TimePeriod}&VenueNo=001&FieldTypeNo=03&_=${Date.now()} HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,ja;q=0.5,de-DE;q=0.4,de;q=0.3
Cache-Control: no-cache
Connection: keep-alive
DNT: 1
Host: webvpn.zzuli.edu.cn
Pragma: no-cache
Referer: https://webvpn.zzuli.edu.cn/http/77726476706e69737468656265737421f3f05885692a72457201c7a99c406d3651/Views/Field/FieldOrder.html?VenueNo=001&FieldTypeNo=03
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0
X-Requested-With: XMLHttpRequest
sec-ch-ua: "Microsoft Edge";v="125", "Chromium";v="125", "Not.A/Brand";v="24"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
`))
})
}
// 获取时间段每个场地的可用位置个数
async function getAvailableAmount(beginTime, TimePeriod = 1, less = 3) {
let data = JSON.parse(JSON.parse((await venueState(TimePeriod)).response).resultdata);
// 过滤出所有 BeginTime 为 beginTime 的场地
const filteredData = data.filter(item => item.BeginTime === `${beginTime}:30`);
// 将数据按照场地编号进行分组
const groupedData = filteredData.reduce((acc, item) => {
const fieldNumber = item.FieldName.split('-')[0]; // 取FieldName的第一部分作为场地编号
if (!acc[fieldNumber]) {
acc[fieldNumber] = [];
}
acc[fieldNumber].push(item);
return acc;
}, {});
// 计算每个组中FieldState为0的个数
const groupCounts = Object.values(groupedData).map(group => ({
group: group[0].FieldName.split('-')[0], // 取FieldName的第一部分作为组的标识
count: group.filter(item => item.FieldState === '0').length
}));
// 找出FieldState为0的个数最多的组
const maxCount = Math.max(...groupCounts.map(item => item.count));
const bestGroups = groupCounts.filter(item => item.count === maxCount);
return maxCount > (less - 1) ? bestGroups : []; // 返回最佳组, 没有则返回空数组
}
// 查询位置可用情况
async function getIsAvailable(beginTime, fieldFullName) {
let data = JSON.parse(JSON.parse((await venueState(TimePeriod)).response).resultdata);
// 过滤出所有 BeginTime 为 beginTime 的场地
const filteredData = data.filter(item => item.BeginTime === beginTime);
// 查找并输出符合条件的场地的 FieldState
const field = filteredData.find(item =>
item.BeginTime === beginTime && item.FieldName === fieldFullName
);
// 如果找到场地,返回 FieldState,否则返回未找到的提示
return field ? field.FieldState : '未找到对应场地或时间';
}
// 获取策略 没有时返回false,否则根据priorChoices返回最佳值
function getTactics(availableAmount, priorChoices = [6, 5, 4, 8, 9, 2, 3, 1, 7], less = 2) {
for (let i = 4; i >= less; i--) {
// 过滤出所有 BeginTime 为 beginTime 的场地
const filteredData = availableAmount.filter(item => item.count === i);
if (filteredData.length == 0) {
continue
} else {
return priorChoices.find(bItem => filteredData.some(aItem => aItem.group.match(/羽毛球0(.*)/)[1] == bItem))
}
}
return false
}
function notify(title = '体育馆预约结果', content, tags, access_key) {
return new Promise((resolve, reject) => {
resolve(tools.net.send(`
GET https://sct.icodef.com/openapi/v1/message/send?access_key=${access_key}&title=${title}&content=${content}&tags=${tags} HTTP/1.1
Host: sct.icodef.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Referer: https://sct.icodef.com/push/key
Connection: keep-alive
Cookie: access_token=ANyIf54O1Rpuw1lmWMrB2AsI1R6thn0k
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
DNT: 1
Sec-GPC: 1
Pragma: no-cache
Cache-Control: no-cache
`))
})
}
(async () => {
let flag = '';
let studentFlag = 0;
for (let i = 0, len = time.length; i < len; i++) {
let beginTime = time[i];
if (await loginWebvpnAndReservation(studentIDs[studentFlag], IDPasswords[studentFlag]) == '联系服务器之前发生了错误') {
flag += `${studentIDs[studentFlag]}账号联系服务器之前发生了错误`;
break
};
let tactics = getTactics(await getAvailableAmount(beginTime, beginTime > 17 ? 2 : 1, less), priorChoices, less);
console.log(`第${i + 1}轮的预约场地为${tactics}`);
if (!tactics) { continue };
for (let j = 1; j < 5; j++) {
if (studentFlag + 1 > studentIDs.length) {
if (flag == '') {
flag = '未完成任何预约'
}
break
}
if (await loginWebvpnAndReservation(studentIDs[studentFlag], IDPasswords[studentFlag]) == '联系服务器之前发生了错误') {
flag += `${studentIDs[studentFlag]}账号联系服务器之前发生了错误`;
break
};
let reservation = JSON.parse((await reserve(tactics, beginTime, j)).response);
console.log(`预约信息: ${reservation.message}, 预约oid: ${reservation.resultdata}`);
if (reservation.resultdata) {
console.log(`预约结果: ${(await pay(reservation.resultdata, studentIDs[studentFlag])).response}`);
console.log((await logout()).finalUrl.includes('cas/logout') ? '成功登出账户' : '账号登出有误');
if (studentFlag >= studentIDs.length) {
if (flag == '') {
flag = '未完成任何预约'
}
} else {
flag += `${studentIDs[studentFlag]}账号预约了${beginTime}:30的${tactics}-${j}%0A`;
}
studentFlag++;
} else {
continue
}
}
}
if (flag == '') {
flag = '似乎没有预约信息'
}
console.log(decodeURIComponent(flag));
await notify('体育馆预约结果', flag, tags, access_key);
resolve(flag)
})();
});