diff --git a/docs/university.md b/docs/university.md index c0156f2e16..20eecee88a 100644 --- a/docs/university.md +++ b/docs/university.md @@ -1961,7 +1961,7 @@ type 列表: ### 软件学院通知 - + | 通知公告 | 学术动态 | 本科教育 | 研究生教育 | | ---- | ---- | ---- | ----- | @@ -1971,7 +1971,7 @@ type 列表: ### 材料科学与工程学院通知 - + | 通知公告 | 学院新闻 | 本科生教育 | 研究生教育 | 学术动态 | | ---- | ---- | ----- | ----- | ---- | @@ -1981,7 +1981,7 @@ type 列表: ### 机械工程学院通知 - + | 通知公告 | 院所新闻 | 教学信息 | 学术动态 | 学院简报 | | ---- | ---- | ---- | ---- | ---- | @@ -1991,7 +1991,7 @@ type 列表: ### 能源与动力工程学院通知 - + | 学院动态 | 通知公告 | 学术论坛 | | ---- | ---- | ---- | @@ -2001,14 +2001,26 @@ type 列表: ### 计算机科学与技术学院通知 - + -| 学院公告 | 学术报告 | 新闻动态 | +| 学院公告 | 学术报告 | 科技简讯 | | ---- | ---- | ---- | | 0 | 1 | 2 | +## 山东大学(威海) + +### 新闻网 + + + +| 校园要闻 | 学生动态 | 综合新闻 | 山大视点 | 菁菁校园 | 校园简讯 | 玛珈之窗 | 热点专题 | 媒体视角 | 高教视野 | 理论学习 | +| ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | +| xyyw | xsdt | zhxw | sdsd | jjxy | xyjx | mjzc | rdzt | mtsj | gjsy | llxx | + + + ## 上海大学 ### 上海大学官网信息 diff --git a/lib/router.js b/lib/router.js index 7a83292f45..e740efec2c 100644 --- a/lib/router.js +++ b/lib/router.js @@ -716,12 +716,12 @@ router.get('/jgsu/jwc', lazyloadRouteHandler('./routes/universities/jgsu/jwc')); // 中南大学 router.get('/csu/job/:type?', lazyloadRouteHandler('./routes/universities/csu/job')); -// 山东大学 -router.get('/sdu/sc/:type?', lazyloadRouteHandler('./routes/universities/sdu/sc')); -router.get('/sdu/cs/:type?', lazyloadRouteHandler('./routes/universities/sdu/cs')); -router.get('/sdu/cmse/:type?', lazyloadRouteHandler('./routes/universities/sdu/cmse')); -router.get('/sdu/mech/:type?', lazyloadRouteHandler('./routes/universities/sdu/mech')); -router.get('/sdu/epe/:type?', lazyloadRouteHandler('./routes/universities/sdu/epe')); +// 山东大学 migrated to v2 +// router.get('/sdu/sc/:type?', lazyloadRouteHandler('./routes/universities/sdu/sc')); +// router.get('/sdu/cs/:type?', lazyloadRouteHandler('./routes/universities/sdu/cs')); +// router.get('/sdu/cmse/:type?', lazyloadRouteHandler('./routes/universities/sdu/cmse')); +// router.get('/sdu/mech/:type?', lazyloadRouteHandler('./routes/universities/sdu/mech')); +// router.get('/sdu/epe/:type?', lazyloadRouteHandler('./routes/universities/sdu/epe')); // 中国海洋大学 router.get('/ouc/it/:type?', lazyloadRouteHandler('./routes/universities/ouc/it')); diff --git a/lib/routes/universities/sdu/cmse.js b/lib/routes/universities/sdu/cmse.js deleted file mode 100644 index bafd5a67d4..0000000000 --- a/lib/routes/universities/sdu/cmse.js +++ /dev/null @@ -1,55 +0,0 @@ -const got = require('@/utils/got'); -const cheerio = require('cheerio'); -const url = require('url'); - -const host = 'http://www.cmse.sdu.edu.cn/'; -const typelist = ['通知公告', '学院新闻', '本科生教育', '研究生教育', '学术动态']; -const urlList = ['zxzx/tzgg.htm', 'zxzx/xyxw.htm', 'zxzx/bksjy.htm', 'zxzx/yjsjy.htm', 'zxzx/xsdt.htm']; - -module.exports = async (ctx) => { - const type = parseInt(ctx.params.type) || 0; - const link = url.resolve(host, urlList[type]); - const response = await got.get(link); - - const $ = cheerio.load(response.data); - - const list = $('#list_right_list a') - .slice(0, 10) - .map((i, e) => $(e).attr('href')) - .get(); - - const out = await Promise.all( - list - .filter((e) => e.startsWith('../info')) - .map(async (itemUrl) => { - itemUrl = url.resolve(host, itemUrl.slice('3')); - const cache = await ctx.cache.get(itemUrl); - if (cache) { - return Promise.resolve(JSON.parse(cache)); - } - - const response = await got.get(itemUrl); - const $ = cheerio.load(response.data); - - const rawDate = $('#show_info').text().split(/\s{4}/); - let date = rawDate[0].split(':')[1]; - date = date.slice(0, 4) + '-' + date.slice(5, 7) + '-' + date.slice(8, 10) + ' ' + date.slice(12); - - const single = { - title: $('#show_title').text().trim(), - link: itemUrl, - author: '山东大学材料科学与工程学院', - description: $('#show_content').html(), - pubDate: new Date(date), - }; - ctx.cache.set(itemUrl, JSON.stringify(single)); - return Promise.resolve(single); - }) - ); - - ctx.state.data = { - title: `山东大学材料科学与工程学院${typelist[type]}`, - link, - item: out, - }; -}; diff --git a/lib/routes/universities/sdu/cs.js b/lib/routes/universities/sdu/cs.js deleted file mode 100644 index e6b1321729..0000000000 --- a/lib/routes/universities/sdu/cs.js +++ /dev/null @@ -1,55 +0,0 @@ -const got = require('@/utils/got'); -const cheerio = require('cheerio'); -const url = require('url'); - -const host = 'http://www.cs.sdu.edu.cn/'; -const typelist = ['学院公告', '学术报告', '新闻动态']; -const urlList = ['index/xygg.htm', 'xwgg/xsbg.htm', 'xwgg/xyxw.htm']; - -module.exports = async (ctx) => { - const type = parseInt(ctx.params.type) || 0; - const link = url.resolve(host, urlList[type]); - - const response = await got.get(link); - - const $ = cheerio.load(response.data); - - const dateDict = {}; - const list = $('.sub_text .news-list') - .slice(0, 10) - .map((i, e) => { - const divs = $(e).children(); - const tlink = 'http://www.cs.sdu.edu.cn/' + $('a', divs[1]).attr('href').substring(3); - dateDict[tlink] = new Date($(divs[2]).text()).toUTCString(); - return tlink; - }) - .get(); - - const out = await Promise.all( - list.map(async (itemUrl) => { - const cache = await ctx.cache.get(itemUrl); - if (cache) { - return Promise.resolve(JSON.parse(cache)); - } - - const response = await got.get(itemUrl); - const $ = cheerio.load(response.data); - - const single = { - title: $('#newsTitle').text().trim(), - author: '山东大学计算机科学与技术学院', - description: $('.v_news_content').html(), - pubDate: dateDict[itemUrl], - link: itemUrl, - }; - ctx.cache.set(itemUrl, JSON.stringify(single)); - return Promise.resolve(single); - }) - ); - - ctx.state.data = { - title: `山东大学计算机科学与技术学院${typelist[type]}通知`, - link, - item: out, - }; -}; diff --git a/lib/routes/universities/sdu/epe.js b/lib/routes/universities/sdu/epe.js deleted file mode 100644 index efb870e073..0000000000 --- a/lib/routes/universities/sdu/epe.js +++ /dev/null @@ -1,54 +0,0 @@ -const got = require('@/utils/got'); -const cheerio = require('cheerio'); -const url = require('url'); - -const host = 'http://www.epe.sdu.edu.cn/'; -const typelist = ['学院动态', '通知公告', '学术论坛']; -const urlList = ['zxzx/xydt.htm', 'zxzx/tzgg.htm', 'zxzx/xslt.htm']; - -module.exports = async (ctx) => { - const type = parseInt(ctx.params.type) || 0; - const link = url.resolve(host, urlList[type]); - const response = await got.get(link); - - const $ = cheerio.load(response.data); - - const list = $('#page_right_main li a') - .slice(0, 10) - .map((i, e) => $(e).attr('href')) - .get(); - - const out = await Promise.all( - list - .filter((e) => e.startsWith('../info')) - .map(async (itemUrl) => { - itemUrl = url.resolve(host, itemUrl.slice('3')); - const cache = await ctx.cache.get(itemUrl); - if (cache) { - return Promise.resolve(JSON.parse(cache)); - } - - const response = await got.get(itemUrl); - const $ = cheerio.load(response.data); - - const rawDate = $('#show_info').text().split(/\s{4}/); - const date = rawDate[0].split(':')[1]; - - const single = { - title: $('#show_title').text().trim(), - link: itemUrl, - author: '山东大学能源与动力工程学院', - description: $('#show_content').html(), - pubDate: new Date(date), - }; - ctx.cache.set(itemUrl, JSON.stringify(single)); - return Promise.resolve(single); - }) - ); - - ctx.state.data = { - title: `山东大学能源与动力工程学院${typelist[type]}`, - link, - item: out, - }; -}; diff --git a/lib/routes/universities/sdu/mech.js b/lib/routes/universities/sdu/mech.js deleted file mode 100644 index bf823d7f54..0000000000 --- a/lib/routes/universities/sdu/mech.js +++ /dev/null @@ -1,58 +0,0 @@ -const got = require('@/utils/got'); -const cheerio = require('cheerio'); -const url = require('url'); - -const typelist = ['通知公告', '院所新闻', '教学信息', '学术动态', '学院简报']; -const urlList = ['xwdt/tzgg.htm', 'xwdt/ysxw.htm', 'xwdt/jxxx.htm', 'xwdt/xsdt.htm', 'xwdt/xyjb.htm']; -const host = 'http://www.mech.sdu.edu.cn/'; - -module.exports = async (ctx) => { - const type = parseInt(ctx.params.type) || 0; - const link = url.resolve(host, urlList[type]); - const response = await got.get(link); - - const $ = cheerio.load(response.data); - - const list = $('#page_list li a') - .slice(0, 10) - .map((i, e) => $(e).attr('href')) - .get(); - - const out = await Promise.all( - list - .filter((e) => e.startsWith('../info') || e.startsWith('http://www.rd.sdu.edu.cn/')) - .map(async (itemUrl) => { - const isFromMech = itemUrl.startsWith('../info'); - if (isFromMech) { - itemUrl = url.resolve(host, itemUrl.slice('3')); - } - const cache = await ctx.cache.get(itemUrl); - if (cache) { - return Promise.resolve(JSON.parse(cache)); - } - - const response = await got.get(itemUrl); - const $ = cheerio.load(response.data); - - const rawDate = $('#show_info').text().split(/\s{4}/); - let date = rawDate[0].split(':')[1]; - date = date.slice(0, 4) + '-' + date.slice(5, 7) + '-' + date.slice(8, 10) + ' ' + date.slice(11); - - const single = { - title: $('#show_title').text().trim(), - link: itemUrl, - author: '山东大学机械工程学院', - description: $('#show_content').html(), - pubDate: new Date(date), - }; - ctx.cache.set(itemUrl, JSON.stringify(single)); - return Promise.resolve(single); - }) - ); - - ctx.state.data = { - title: `山东大学机械工程学院${typelist[type]}`, - link, - item: out, - }; -}; diff --git a/lib/routes/universities/sdu/sc.js b/lib/routes/universities/sdu/sc.js deleted file mode 100644 index 6e11ca7638..0000000000 --- a/lib/routes/universities/sdu/sc.js +++ /dev/null @@ -1,56 +0,0 @@ -const got = require('@/utils/got'); -const cheerio = require('cheerio'); -const url = require('url'); - -const host = 'http://www.sc.sdu.edu.cn/'; -const typelist = ['通知公告', '学术动态', '本科教育', '研究生教育']; -const urlList = ['tzgg.htm', 'kxyj/xsdt/2.htm', 'rcpy/bkjy.htm', 'rcpy/yjsjy.htm']; - -module.exports = async (ctx) => { - const type = parseInt(ctx.params.type) || 0; - const link = url.resolve(host, urlList[type]); - const response = await got.get(link); - - const $ = cheerio.load(response.data); - - const dateDict = {}; - const list = $('.lm_list li') - .slice(0, 10) - .map((i, e) => { - let aLink = $(e).children('a').attr('href'); - // aLink = aLink.slice(3); - aLink = url.resolve(host, aLink); - const date = $(e).children('span').text().trim(); - dateDict[aLink] = date; - return aLink; - }) - .get(); - - const out = await Promise.all( - list.map(async (itemUrl) => { - const cache = await ctx.cache.get(itemUrl); - if (cache) { - return Promise.resolve(JSON.parse(cache)); - } - - const response = await got.get(itemUrl); - const $ = cheerio.load(response.data); - - const single = { - title: $('h1.c-title').text().trim(), - link: itemUrl, - author: '山东大学软件学院', - description: $('.v_news_content').html(), - pubDate: new Date(dateDict[itemUrl]), - }; - ctx.cache.set(itemUrl, JSON.stringify(single)); - return Promise.resolve(single); - }) - ); - - ctx.state.data = { - title: `山东大学软件学院${typelist[type]}`, - link, - item: out, - }; -}; diff --git a/lib/utils/wechat-mp.js b/lib/utils/wechat-mp.js index 96723029c3..e24b64db07 100644 --- a/lib/utils/wechat-mp.js +++ b/lib/utils/wechat-mp.js @@ -5,6 +5,7 @@ * lib/route/tencent/wechat * lib/v2/wechat * lib/v2/gzh360 + * lib/v2/sdu/cs * * If your new route is not in the above folders, please add it to the list. * diff --git a/lib/v2/sdu/cmse.js b/lib/v2/sdu/cmse.js new file mode 100644 index 0000000000..98bb737db3 --- /dev/null +++ b/lib/v2/sdu/cmse.js @@ -0,0 +1,59 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { parseDate } = require('@/utils/parse-date'); + +const host = 'http://www.cmse.sdu.edu.cn/'; +const typelist = ['通知公告', '学院新闻', '本科生教育', '研究生教育', '学术动态']; +const urlList = ['zxzx/tzgg.htm', 'zxzx/xyxw.htm', 'zxzx/bksjy.htm', 'zxzx/yjsjy.htm', 'zxzx/xsdt.htm']; + +module.exports = async (ctx) => { + const type = ctx.params.type ? parseInt(ctx.params.type) : 0; + const link = new URL(urlList[type], host).href; + const response = await got(link); + + const $ = cheerio.load(response.data); + + let item = $('.article_list li') + .map((_, e) => { + e = $(e); + const a = e.find('a'); + return { + title: a.text().trim(), + link: a.attr('href'), + pubDate: parseDate(e.find('.date').text(), 'YYYY/MM/DD'), + }; + }) + .get(); + + item = await Promise.all( + item + .filter((e) => e.link.startsWith('../info')) + .map((item) => { + item.link = new URL(item.link.slice('3'), host).href; + return ctx.cache.tryGet(item.link, async () => { + const response = await got(item.link); + const $ = cheerio.load(response.data); + + item.title = $('.contentTitle').text(); + item.author = + $('.contentTitle2') + .find('span') + .eq(1) + .text() + .trim() + .match(/作者:(.*)/)[1] || '山东大学材料科学与工程学院'; + $('.contentTitle, .contentTitle2').remove(); + item.description = $('.content_detail').html(); + + return item; + }); + }) + ); + + ctx.state.data = { + title: `山东大学材料科学与工程学院${typelist[type]}`, + description: $('title').text(), + link, + item, + }; +}; diff --git a/lib/v2/sdu/cs.js b/lib/v2/sdu/cs.js new file mode 100644 index 0000000000..dac8f3a10e --- /dev/null +++ b/lib/v2/sdu/cs.js @@ -0,0 +1,57 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { parseDate } = require('@/utils/parse-date'); +const { finishArticleItem } = require('@/utils/wechat-mp'); + +const host = 'https://www.cs.sdu.edu.cn/'; +const typelist = ['学院公告', '学术报告', '科技简讯']; +const urlList = ['xygg.htm', 'xsbg.htm', 'kjjx.htm']; + +module.exports = async (ctx) => { + const type = ctx.params.type ? parseInt(ctx.params.type) : 0; + const link = new URL(urlList[type], host).href; + + const response = await got(link); + + const $ = cheerio.load(response.data); + + let item = $('.dqlb ul li') + .map((_, e) => { + e = $(e); + const a = e.find('a'); + return { + title: a.text().trim(), + link: a.attr('href').startsWith('info/') ? host + a.attr('href') : a.attr('href'), + pubDate: parseDate(e.find('.fr').text().trim(), 'YYYY-MM-DD'), + }; + }) + .get(); + + item = await Promise.all( + item.map((item) => + ctx.cache.tryGet(item.link, async () => { + if (new URL(item.link).hostname === 'mp.weixin.qq.com') { + return finishArticleItem(ctx, item); + } else if (new URL(item.link).hostname !== 'www.cs.sdu.edu.cn') { + return item; + } + const response = await got(item.link); + const $ = cheerio.load(response.data); + + item.title = $('.xqnr_tit h2').text().trim(); + item.author = $('.xqnr_tit span').eq(1).text().trim().replace('编辑:', '') || '山东大学计算机科学与技术学院'; + $('.xqnr_tit').remove(); + item.description = $('form[name=_newscontent_fromname]').html(); + + return item; + }) + ) + ); + + ctx.state.data = { + title: `山东大学计算机科学与技术学院${typelist[type]}通知`, + description: $('title').text(), + link, + item, + }; +}; diff --git a/lib/v2/sdu/data.js b/lib/v2/sdu/data.js new file mode 100644 index 0000000000..c9c1da9534 --- /dev/null +++ b/lib/v2/sdu/data.js @@ -0,0 +1,61 @@ +module.exports = { + wh: { + news: { + name: '山东大学(威海)新闻网', + route: '/news', + source: ['/'], + titlePrefix: '(威海)新闻网|', + docs: 'https://docs.rsshub.app/university.html#shan-dong-da-xue-wei-hai', + getTarget(url) { + return this.route + '/' + url.replace(/\.htm$/, ''); + }, + url: 'https://xinwen.wh.sdu.edu.cn/', + columns: { + xyyw: { + name: '校园要闻', + url: 'xyyw.htm', + }, + xsdt: { + name: '学生动态', + url: 'xsdt.htm', + }, + zhxw: { + name: '综合新闻', + url: 'zhxw.htm', + }, + sdsd: { + name: '山大视点', + url: 'sdsd.htm', + }, + jjxy: { + name: '菁菁校园', + url: 'jjxy.htm', + }, + xyjx: { + name: '校园简讯', + url: 'xyjx.htm', + }, + mjzc: { + name: '玛珈之窗', + url: 'mjzc.htm', + }, + rdzt: { + name: '热点专题', + url: 'rdzt.htm', + }, + mtsj: { + name: '媒体视角', + url: 'mtsj.htm', + }, + gjsy: { + name: '高教视野', + url: 'gjsy.htm', + }, + llxx: { + name: '理论学习', + url: 'llxx.htm', + }, + }, + }, + }, +}; diff --git a/lib/v2/sdu/epe.js b/lib/v2/sdu/epe.js new file mode 100644 index 0000000000..f77a03ef4b --- /dev/null +++ b/lib/v2/sdu/epe.js @@ -0,0 +1,55 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { parseDate } = require('@/utils/parse-date'); +const timezone = require('@/utils/timezone'); + +const host = 'https://www.epe.sdu.edu.cn/'; +const typelist = ['学院动态', '通知公告', '学术论坛']; +const urlList = ['zxzx/xydt.htm', 'zxzx/tzgg.htm', 'zxzx/xslt.htm']; + +module.exports = async (ctx) => { + const type = ctx.params.type ? parseInt(ctx.params.type) : 0; + const link = new URL(urlList[type], host).href; + const response = await got(link); + + const $ = cheerio.load(response.data); + + let item = $('#page_right_main li a') + .map((_, e) => { + e = $(e); + return { + title: e.attr('title'), + link: e.attr('href'), + }; + }) + .get(); + + item = await Promise.all( + item + .filter((e) => e.link.startsWith('../info')) + .map((item) => { + item.link = new URL(item.link.slice('3'), host).href; + return ctx.cache.tryGet(item.link, async () => { + const response = await got(item.link); + const $ = cheerio.load(response.data); + + const info = $('#show_info').text().split(/\s{4}/); + const date = info[0].split(':')[1]; + + item.title = $('#show_title').text().trim(); + item.author = info[1].replace('编辑:', '') || '山东大学能源与动力工程学院'; + item.description = $('#show_content').html(); + item.pubDate = timezone(parseDate(date), +8); + + return item; + }); + }) + ); + + ctx.state.data = { + title: `山东大学能源与动力工程学院${typelist[type]}`, + description: $('title').text(), + link, + item, + }; +}; diff --git a/lib/v2/sdu/extractor/index.js b/lib/v2/sdu/extractor/index.js new file mode 100644 index 0000000000..de1fc19793 --- /dev/null +++ b/lib/v2/sdu/extractor/index.js @@ -0,0 +1,12 @@ +module.exports = async (link, ctx) => { + if (link.startsWith('https://xinwen.wh.sdu.edu.cn/')) { + return await require('./wh/news')(link, ctx); + } + if (link.startsWith('https://www.view.sdu.edu.cn/')) { + return await require('./view')(link, ctx); + } + if (link.startsWith('https://www.sdrj.sdu.edu.cn/')) { + return await require('./sdrj')(link, ctx); + } + return {}; +}; diff --git a/lib/v2/sdu/extractor/sdrj.js b/lib/v2/sdu/extractor/sdrj.js new file mode 100644 index 0000000000..0734841131 --- /dev/null +++ b/lib/v2/sdu/extractor/sdrj.js @@ -0,0 +1,21 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { parseDate } = require('@/utils/parse-date'); +const timezone = require('@/utils/timezone'); + +module.exports = async (link, ctx) => + await ctx.cache.tryGet(link, async () => { + let content, author, exactDate; + try { + const result = await got(link); + const $ = cheerio.load(result.data); + content = $('#vsb_content').html(); + author = $("form[name='_newscontent_fromname'] > h1").text(); + const exactDateLine = $("form[name='_newscontent_fromname'] > p.info").text().trim(); + const exactDateText = exactDateLine.match(/^发布时间:(?\d+\/\d+\/\d+\s\d{2}:\d{2}:\d{2})/).groups.date; + exactDate = timezone(parseDate(exactDateText, 'YYYY/MM/DD HH:mm:ss'), +8); + return { description: content, author, exactDate }; + } catch (e) { + return { description: content, author, exactDate }; + } + }); diff --git a/lib/v2/sdu/extractor/view.js b/lib/v2/sdu/extractor/view.js new file mode 100644 index 0000000000..35647e247b --- /dev/null +++ b/lib/v2/sdu/extractor/view.js @@ -0,0 +1,21 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { parseDate } = require('@/utils/parse-date'); +const timezone = require('@/utils/timezone'); + +module.exports = async (link, ctx) => + await ctx.cache.tryGet(link, async () => { + let content, author, exactDate; + try { + const result = await got(link); + const $ = cheerio.load(result.data); + content = $('#vsb_content').html(); + author = $("form[name='_newscontent_fromname'] > div > p:last-of-type").text(); + const exactDateLine = $('.news_tit > p:last-child').text(); + const exactDateText = exactDateLine.match(/^发布日期:(?\d+年\d+月\d+日\s\d{2}:\d{2})/).groups.date; + exactDate = timezone(parseDate(exactDateText, 'YYYY年MM月DD日 HH:mm'), +8); + return { description: content, author, exactDate }; + } catch (e) { + return { description: content, author, exactDate }; + } + }); diff --git a/lib/v2/sdu/extractor/wh/news.js b/lib/v2/sdu/extractor/wh/news.js new file mode 100644 index 0000000000..d7e6041688 --- /dev/null +++ b/lib/v2/sdu/extractor/wh/news.js @@ -0,0 +1,21 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { parseDate } = require('@/utils/parse-date'); +const timezone = require('@/utils/timezone'); + +module.exports = async (link, ctx) => + await ctx.cache.tryGet(link, async () => { + let content, author, exactDate; + try { + const result = await got(link); + const $ = cheerio.load(result.data); + content = $('#main-content').html(); + author = $('#source').text(); + const exactDateLine = $('.news_tit > p:last-child').text(); + const exactDateText = exactDateLine.match(/^发布日期:(?\d+年\d+月\d+日\s\d{2}:\d{2})/).groups.date; + exactDate = timezone(parseDate(exactDateText, 'YYYY年MM月DD日 HH:mm'), +8); + return { description: content, author, exactDate }; + } catch (e) { + return { description: content, author, exactDate }; + } + }); diff --git a/lib/v2/sdu/maintainer.js b/lib/v2/sdu/maintainer.js new file mode 100644 index 0000000000..db0b8798de --- /dev/null +++ b/lib/v2/sdu/maintainer.js @@ -0,0 +1,8 @@ +module.exports = { + '/cmse/:type?': ['Ji4n1ng'], + '/cs/:type?': ['Ji4n1ng'], + '/epe/:type?': ['Ji4n1ng'], + '/mech/:type?': ['Ji4n1ng'], + '/sc/:type?': ['Ji4n1ng'], + '/wh/news/:column?': ['kxxt'], +}; diff --git a/lib/v2/sdu/mech.js b/lib/v2/sdu/mech.js new file mode 100644 index 0000000000..428b7422fb --- /dev/null +++ b/lib/v2/sdu/mech.js @@ -0,0 +1,60 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { parseDate } = require('@/utils/parse-date'); +const timezone = require('@/utils/timezone'); + +const typelist = ['通知公告', '院所新闻', '教学信息', '学术动态', '学院简报']; +const urlList = ['xwdt/tzgg.htm', 'xwdt/ysxw.htm', 'xwdt/jxxx.htm', 'xwdt/xsdt.htm', 'xwdt/xyjb.htm']; +const host = 'https://www.mech.sdu.edu.cn/'; + +module.exports = async (ctx) => { + const type = ctx.params.type ? parseInt(ctx.params.type) : 0; + const link = new URL(urlList[type], host).href; + const response = await got(link); + + const $ = cheerio.load(response.data); + + let item = $('#page_list li a') + .slice(0, 1) + .map((_, e) => { + e = $(e); + return { + title: e.attr('title'), + link: e.attr('href'), + }; + }) + .get(); + + item = await Promise.all( + item + .filter((e) => e.link.startsWith('../info') || e.link.startsWith('https://www.rd.sdu.edu.cn/')) + .map((item) => { + const isFromMech = item.link.startsWith('../info'); + if (isFromMech) { + item.link = new URL(item.link.slice('3'), host).href; + } + return ctx.cache.tryGet(item.link, async () => { + const response = await got(item.link); + const $ = cheerio.load(response.data); + + const info = $('#show_info').text().split(/\s{4}/); + const date = info[0].split(':')[1]; + + item.title = $('#show_title').text().trim(); + item.author = info[1].replace('作者:', '') || '山东大学机械工程学院'; + $('#show_title, #show_info').remove(); + item.description = $('form[name=_newscontent_fromname] div').html(); + item.pubDate = timezone(parseDate(date), +8); + + return item; + }); + }) + ); + + ctx.state.data = { + title: `山东大学机械工程学院${typelist[type]}`, + description: $('title').text(), + link, + item, + }; +}; diff --git a/lib/v2/sdu/radar.js b/lib/v2/sdu/radar.js new file mode 100644 index 0000000000..0056e5ef4d --- /dev/null +++ b/lib/v2/sdu/radar.js @@ -0,0 +1,153 @@ +const { wh } = require('./data'); + +module.exports = { + 'sdu.edu.cn': { + _name: '山东大学', + 'xinwen.wh': Object.entries(wh.news.columns).map(([, value]) => ({ + title: wh.news.titlePrefix + value.name, + docs: wh.news.docs, + source: wh.news.source, + target: '/sdu/wh' + wh.news.getTarget(value.url), + })), + 'www.cmse': [ + { + title: '材料科学与工程学院通知', + docs: 'https://docs.rsshub.app/university.html#shan-dong-da-xue', + source: ['/*path', '/'], + target: (params) => { + let type; + switch (params.path) { + case 'zxzx/tzgg.htm': + type = '0'; + break; + case 'zxzx/xyxw.htm': + type = '1'; + break; + case 'zxzx/bksjy.htm': + type = '2'; + break; + case 'zxzx/yjsjy.htm': + type = '3'; + break; + case 'zxzx/xsdt.htm': + type = '4'; + break; + default: + type = '0'; + break; + } + return `/sdu/cmse/${type}`; + }, + }, + ], + 'www.cs': [ + { + title: '计算机科学与技术学院通知', + docs: 'https://docs.rsshub.app/university.html#shan-dong-da-xue', + source: ['/*path', '/'], + target: (params) => { + let type; + switch (params.path) { + case 'xygg.htm': + type = '0'; + break; + case 'xsbg.htm': + type = '1'; + break; + case 'kjjx.htm': + type = '2'; + break; + default: + type = '0'; + break; + } + return `/sdu/cs/${type}`; + }, + }, + ], + 'www.epe': [ + { + title: '能源与动力工程学院通知', + docs: 'https://docs.rsshub.app/university.html#shan-dong-da-xue', + source: ['/*path', '/'], + target: (params) => { + let type; + switch (params.path) { + case 'zxzx/xydt.htm': + type = '0'; + break; + case 'zxzx/tzgg.htm': + type = '1'; + break; + case 'zxzx/xslt.htm': + type = '2'; + break; + default: + type = '0'; + break; + } + return `/sdu/epe/${type}`; + }, + }, + ], + 'www.mech': [ + { + title: '机械工程学院通知', + docs: 'https://docs.rsshub.app/university.html#shan-dong-da-xue', + source: ['/*path', '/'], + target: (params) => { + let type; + switch (params.path) { + case 'xwdt/tzgg.htm': + type = '0'; + break; + case 'xwdt/ysxw.htm': + type = '1'; + break; + case 'xwdt/jxxx.htm': + type = '2'; + break; + case 'xwdt/xsdt.htm': + type = '3'; + break; + case 'xwdt/xyjb.htm': + type = '4'; + break; + default: + type = '0'; + break; + } + return `/sdu/mech/${type}`; + }, + }, + ], + 'www.sc': [ + { + title: '软件学院通知', + docs: 'https://docs.rsshub.app/university.html#shan-dong-da-xue', + source: ['/*path', '/'], + target: (params) => { + let type; + switch (params.path) { + case 'tzgg.htm': + type = '0'; + break; + case 'kxyj/xsyg.htm': + type = '1'; + break; + case 'rcpy/bkjy.htm': + type = '2'; + break; + case 'rcpy/yjsjy.htm': + type = '3'; + break; + default: + type = '0'; + break; + } + return `/sdu/sc/${type}`; + }, + }, + ], + }, +}; diff --git a/lib/v2/sdu/router.js b/lib/v2/sdu/router.js new file mode 100644 index 0000000000..6913d656cb --- /dev/null +++ b/lib/v2/sdu/router.js @@ -0,0 +1,8 @@ +module.exports = function (router) { + router.get('/cmse/:type?', require('./cmse')); + router.get('/cs/:type?', require('./cs')); + router.get('/epe/:type?', require('./epe')); + router.get('/mech/:type?', require('./mech')); + router.get('/sc/:type?', require('./sc')); + router.get('/wh/news/:column?', require('./wh/news')); +}; diff --git a/lib/v2/sdu/sc.js b/lib/v2/sdu/sc.js new file mode 100644 index 0000000000..89cd9f2b8d --- /dev/null +++ b/lib/v2/sdu/sc.js @@ -0,0 +1,61 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { parseDate } = require('@/utils/parse-date'); + +const host = 'https://www.sc.sdu.edu.cn/'; +const typelist = ['通知公告', '学术动态', '本科教育', '研究生教育']; +const urlList = ['tzgg.htm', 'kxyj/xsyg.htm', 'rcpy/bkjy.htm', 'rcpy/yjsjy.htm']; + +module.exports = async (ctx) => { + const type = ctx.params.type ? parseInt(ctx.params.type) : 0; + const link = new URL(urlList[type], host).href; + const response = await got(link); + + const $ = cheerio.load(response.data); + + let item = $('.newlist01 li') + .map((_, e) => { + e = $(e); + const a = e.find('a'); + let link = a.attr('href'); + link = new URL(link, host).href; + return { + title: a.text().trim(), + link, + pubDate: parseDate(e.find('.date').text().trim()), + }; + }) + .get(); + + item = await Promise.all( + item.map((item) => + ctx.cache.tryGet(item.link, async () => { + try { + const response = await got(item.link); + const $ = cheerio.load(response.data); + + item.title = $('h3').text(); + item.author = + $('.pr') + .text() + .trim() + .match(/作者:(.*)/)[1] || '山东大学软件学院'; + $('h3, .pr').remove(); + item.description = $('.content').html(); + + return item; + } catch (e) { + // intranet oa.sdu.edu.cn + return item; + } + }) + ) + ); + + ctx.state.data = { + title: `山东大学软件学院${typelist[type]}`, + description: $('title').text(), + link, + item, + }; +}; diff --git a/lib/v2/sdu/wh/news.js b/lib/v2/sdu/wh/news.js new file mode 100644 index 0000000000..b1511f5165 --- /dev/null +++ b/lib/v2/sdu/wh/news.js @@ -0,0 +1,38 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const data = require('../data').wh.news; +const extractor = require('../extractor'); +const { parseDate } = require('@/utils/parse-date'); + +module.exports = async (ctx) => { + const column = ctx.params.column ?? 'xyyw'; + const baseUrl = data.url; + const response = await got(baseUrl + data.columns[column].url); + const $ = cheerio.load(response.data); + const items = $('.n_newslist li'); + const out = await Promise.all( + items.map(async (index, item) => { + item = $(item); + const anchor = item.find('a'); + const title = anchor.attr('title'); + const href = anchor.attr('href'); + const link = href.startsWith('http') ? href : baseUrl + href; + const { description, author, exactDate } = await ctx.cache.tryGet(link, async () => await extractor(link, ctx)); + const span = item.find('span'); + const pubDate = exactDate ?? parseDate(span.text(), 'YYYY/MM/DD'); + return { + title, + link, + description, + pubDate, + author, + }; + }) + ); + + ctx.state.data = { + title: `${data.name} ${data.columns[column].name}`, + link: baseUrl + data.columns[column].url, + item: out, + }; +};