feat: add support for BUPT portal (#3290)

This commit is contained in:
RicardoMing
2019-10-21 12:03:49 +08:00
committed by DIYgod
parent bd74aa8db5
commit db9bb6dc91
4 changed files with 187 additions and 0 deletions

View File

@@ -104,6 +104,16 @@ pageClass: routes
<Route author="RicardoMing" example="/bupt/grs" path="/bupt/grs" />
### 信息门户
<Route author="RicardoMing" example="/bupt/portal" path="/bupt/portal" />
::: warning 注意
信息门户的通知需要通过统一身份认证后才能获取,因此需要在校园网或校园 VPN 环境下自建。
设置环境变量: `BUPT_USERNAME` 用户名为学号, `BUPT_PASSWORD` 统一身份认证的密码。
:::
## 常州大学
### 教务处

View File

@@ -98,6 +98,10 @@ const calculateValue = () => {
chuiniu: {
member: envs.CHUINIU_MEMBER,
},
bupt: {
username: envs.BUPT_USERNAME,
password: envs.BUPT_PASSWORD,
},
};
};
calculateValue();

View File

@@ -1309,6 +1309,7 @@ router.get('/21caijing/channel/:name', require('./routes/21caijing/channel'));
// 北京邮电大学
router.get('/bupt/yz/:type', require('./routes/universities/bupt/yz'));
router.get('/bupt/grs', require('./routes/universities/bupt/grs'));
router.get('/bupt/portal', require('./routes/universities/bupt/portal'));
// VOCUS 方格子
router.get('/vocus/publication/:id', require('./routes/vocus/publication'));

View File

@@ -0,0 +1,172 @@
const config = require('@/config').value;
const got = require('@/utils/got');
const cheerio = require('cheerio');
const url = require('url');
function isToday(time) {
return new Date().getTime() - new Date(time).getTime() < 86400000;
}
function getPubDate(time) {
return isToday(time) ? new Date() : new Date(time + ' 08:00:00');
}
const base = 'http://my.bupt.edu.cn';
const sourceTimezoneOffset = -8;
let portalCookie = null;
let authCookie = null;
let castgc = null;
module.exports = async (ctx) => {
if (!config.bupt || !config.bupt.username || !config.bupt.password) {
throw 'BUPT Portal RSS is disabled due to the lack of <a href="https://docs.rsshub.app/university.html#bei-jing-you-dian-da-xue">relevant config</a>';
}
const reqUrl = url.resolve(base, '/index.portal?.pn=p1778');
let reqRes = await got({
method: 'get',
followRedirect: false,
url: reqUrl,
headers: {
cookie: portalCookie,
},
});
// Login
if (reqRes.statusCode === 302) {
if (reqRes.headers['set-cookie'] === undefined) {
portalCookie = null;
throw 'Can not obtain portalCookie';
}
portalCookie = reqRes.headers['set-cookie'].toString().match(/JSESSIONID=.{37}/)[0];
const authRes = await got({
method: 'get',
followRedirect: false,
url: reqRes.headers.location,
headers: {
cookie: `${authCookie}; ${castgc}`,
},
});
if (authRes.statusCode === 200) {
authCookie = authRes.headers['set-cookie'].toString().match(/JSESSIONID=.{37}/)[0];
const authPage = cheerio.load(authRes.data);
const loginRes = await got({
method: 'post',
followRedirect: false,
url: reqRes.headers.location,
headers: {
cookie: authCookie,
referer: reqRes.headers.location,
},
form: true,
data: {
username: config.bupt.username,
password: config.bupt.password,
lt: authPage('[name=lt]').attr('value'),
execution: authPage('[name=execution]').attr('value'),
_eventId: authPage('[name=_eventId]').attr('value'),
rmShown: 1,
},
});
if (loginRes.headers['set-cookie'] === undefined) {
authCookie = null;
castgc = null;
throw 'Can not obtain castgc';
}
castgc = loginRes.headers['set-cookie'].toString().match(/CASTGC=.{85}/)[0];
await got({
method: 'get',
followRedirect: false,
url: loginRes.headers.location,
headers: {
cookie: portalCookie,
},
});
} else if (authRes.statusCode === 302) {
await got({
method: 'get',
followRedirect: false,
url: authRes.headers.location,
headers: {
cookie: portalCookie,
},
});
} else {
throw 'BUPT auth login failed';
}
reqRes = await got({
method: 'get',
followRedirect: false,
url: reqUrl,
headers: {
cookie: portalCookie,
},
});
}
const $ = cheerio.load(reqRes.data);
const list = $('.newslist li').get();
const out = await Promise.all(
list.map(async (i) => {
const item = $(i);
const itemUrl = url.resolve(
base,
$(item)
.find('a')
.attr('href')
);
const cache = await ctx.cache.get(itemUrl);
if (cache) {
return Promise.resolve(JSON.parse(cache));
}
const title = $(item)
.find('a')
.text();
const author = $(item)
.find('.author')
.text();
const time = getPubDate(
$(item)
.find('.time')
.text()
);
time.setTime(time.getTime() + (sourceTimezoneOffset - time.getTimezoneOffset() / 60) * 60 * 60 * 1000);
const itemResponse = await got({
method: 'get',
url: itemUrl,
headers: {
cookie: portalCookie,
},
});
const itemElement = cheerio.load(itemResponse.data);
// Remove useless print button
itemElement('table.Noprint').remove();
const description = itemElement('.singleexpert').html();
const single = {
title: title,
author: author,
description: description,
pubDate: time.toUTCString(),
link: itemUrl,
guid: itemUrl,
};
ctx.cache.set(itemUrl, JSON.stringify(single));
return Promise.resolve(single);
})
);
ctx.state.data = {
title: '北京邮电大学信息门户',
link: reqUrl,
item: out,
};
};