From ccc62e9a78733201112f2518fdad4c1c4941f7b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linull/=E6=9D=8E=E6=9E=97?= <2382963480@qq.com> Date: Mon, 25 May 2026 10:40:28 +0800 Subject: [PATCH 01/10] feat(route): add SHU BKSY and CS feeds --- lib/routes/shu/bksy.ts | 189 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 lib/routes/shu/bksy.ts diff --git a/lib/routes/shu/bksy.ts b/lib/routes/shu/bksy.ts new file mode 100644 index 000000000000..9ae952b7bc10 --- /dev/null +++ b/lib/routes/shu/bksy.ts @@ -0,0 +1,189 @@ +import { load } from 'cheerio'; + +import type { Route } from '@/types'; +import cache from '@/utils/cache'; +import got from '@/utils/got'; +import { parseDate } from '@/utils/parse-date'; +import timezone from '@/utils/timezone'; + +const rootUrl = 'https://bksy.shu.edu.cn'; +const image = 'https://www.shu.edu.cn/__local/0/08/C6/1EABE492B0CF228A5564D6E6ABE_779D1EE3_5BF7.png'; + +const categories = { + notice: { + title: '通知公告', + path: 'tzgg', + }, + news: { + title: '新闻', + path: 'xw', + }, +}; + +const alias = new Map([ + ['tzgg', 'notice'], + ['xw', 'news'], +]); + +export const route: Route = { + path: '/bksy/:type?', + categories: ['university'], + example: '/shu/bksy/notice', + parameters: { type: '分类,默认为通知公告' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: [ + { + source: ['bksy.shu.edu.cn/', 'bksy.shu.edu.cn/index/tzgg.htm', 'bksy.shu.edu.cn/index/xw.htm'], + target: '/bksy', + }, + ], + name: '本科生院', + maintainers: ['tuxinghuan', 'GhhG123'], + handler, + url: 'bksy.shu.edu.cn/', + description: `上海大学教务部已更名为本科生院,旧路由 \`/shu/jwb/:type?\` 会重定向至本路由。 + +| 通知公告 | 新闻 | +| -------- | ---- | +| notice | news |`, +}; + +async function handler(ctx) { + const routeType = ctx.req.param('type') || 'notice'; + const type = alias.get(routeType) || routeType; + const category = categories[type] || categories.notice; + const link = new URL(`index/${category.path}.htm`, rootUrl).href; + + const response = await got.get(link); + const $ = load(response.data); + + const list = $('.only-list li') + .slice(0, 10) + .toArray() + .map((element) => { + const item = $(element); + const rawLink = item.find('a').attr('href'); + + return { + title: item.find('a').text().trim(), + link: rawLink ? new URL(rawLink, link).href : link, + pubDate: timezone(parseDate(item.find('span').text().trim(), 'YYYY年MM月DD日'), +8), + }; + }); + + const items = await Promise.all(list.map((item) => cache.tryGet(item.link, async () => await getItemDetail(item)))); + + return { + title: `上海大学本科生院 - ${category.title}`, + description: `上海大学本科生院 - ${category.title}`, + link, + image, + item: items, + }; +} + +async function getItemDetail(item) { + const response = await got.get(item.link); + const $ = load(response.data); + const content = $('.v_news_content').first(); + + normalizeContentUrls($, content, item.link); + + const embeddedFiles = getEmbeddedFiles($, content, item.link); + content.find('script').remove(); + const attachments = getAttachments($, item.link); + const description = renderDescription(content.html() || item.title, embeddedFiles, attachments); + const enclosure = attachments[0] || embeddedFiles[0]; + + item.title = $('[id$=_lblTitle]').first().text().trim() || item.title; + item.author = $('[id$=_lblUser]').first().text().trim(); + const pubDate = $('[id$=_lblFB]').first().text().trim(); + if (pubDate) { + item.pubDate = timezone(parseDate(pubDate), +8); + } + item.description = description; + + if (enclosure) { + item.enclosure_url = enclosure.link; + item.enclosure_type = getEnclosureType(enclosure); + } + + return item; +} + +function normalizeContentUrls($, content, baseUrl) { + content.find('a[href]').each((_, element) => { + const href = $(element).attr('href'); + if (href) { + $(element).attr('href', new URL(href, baseUrl).href); + } + }); + + content.find('img[src]').each((_, element) => { + const src = $(element).attr('src'); + if (src) { + $(element).attr('src', new URL(src, baseUrl).href); + } + }); +} + +function getEmbeddedFiles($, content, baseUrl) { + return content + .find('script') + .toArray() + .flatMap((element) => { + const script = $(element).html() || ''; + return [...script.matchAll(/showVsbpdfIframe\(["']([^"']+)["']/g)].map((match, index) => ({ + title: index === 0 ? '正文 PDF' : `正文 PDF ${index + 1}`, + link: new URL(match[1], baseUrl).href, + })); + }); +} + +function getAttachments($, baseUrl) { + return $('form[name="_newscontent_fromname"] ul li a[href]') + .toArray() + .map((element) => { + const attachment = $(element); + + return { + title: attachment.text().trim() || attachment.attr('title') || '附件', + link: new URL(attachment.attr('href'), baseUrl).href, + }; + }); +} + +function renderDescription(description, embeddedFiles, attachments) { + const files = [...embeddedFiles, ...attachments]; + if (files.length === 0) { + return description; + } + + const fileList = files.map((file) => `
  • ${escapeHtml(file.title)}
  • `).join(''); + return `${description}

    附件

    `; +} + +function getEnclosureType(file) { + const extension = (file.title.split('.').pop() || new URL(file.link).pathname.split('.').pop() || '').toLowerCase(); + const mimeTypes = { + doc: 'application/msword', + docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + pdf: 'application/pdf', + xls: 'application/vnd.ms-excel', + xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + zip: 'application/zip', + }; + + return mimeTypes[extension] || 'application/octet-stream'; +} + +function escapeHtml(text) { + return text.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>').replaceAll('"', '"'); +} From f405e946a8166811b1eb31a577ba2c1dc6fad81b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linull/=E6=9D=8E=E6=9E=97?= <2382963480@qq.com> Date: Mon, 25 May 2026 10:40:29 +0800 Subject: [PATCH 02/10] feat(route): add SHU BKSY and CS feeds --- lib/routes/shu/cs.ts | 194 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 lib/routes/shu/cs.ts diff --git a/lib/routes/shu/cs.ts b/lib/routes/shu/cs.ts new file mode 100644 index 000000000000..74a552996f57 --- /dev/null +++ b/lib/routes/shu/cs.ts @@ -0,0 +1,194 @@ +import { load } from 'cheerio'; + +import type { Route } from '@/types'; +import cache from '@/utils/cache'; +import got from '@/utils/got'; +import { parseDate } from '@/utils/parse-date'; +import timezone from '@/utils/timezone'; + +const rootUrl = 'https://cs.shu.edu.cn'; +const image = 'https://www.shu.edu.cn/__local/0/08/C6/1EABE492B0CF228A5564D6E6ABE_779D1EE3_5BF7.png'; + +const categories = { + zytz: { + title: '重要通知', + path: 'index/zytz.htm', + }, +}; + +export const route: Route = { + path: ['/cs/:type?', '/ces/:type?'], + categories: ['university'], + example: '/shu/cs/zytz', + parameters: { type: '分类,默认为重要通知' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: [ + { + source: ['cs.shu.edu.cn/', 'cs.shu.edu.cn/index/zytz.htm'], + target: '/cs/zytz', + }, + ], + name: '计算机工程与科学学院', + maintainers: ['GhhG123', 'linull24'], + handler, + url: 'cs.shu.edu.cn/', + description: `\`/shu/ces/:type?\` 是兼容别名。 + +| 重要通知 | +| -------- | +| zytz |`, +}; + +async function handler(ctx) { + const type = ctx.req.param('type') || 'zytz'; + const category = categories[type] || categories.zytz; + const link = new URL(category.path, rootUrl).href; + + const response = await got.get(link); + const $ = load(response.data); + const list = $('ul li, .only-list li, .list li, .news_list li') + .toArray() + .map((element) => { + const item = $(element); + const anchor = item.find('a').first(); + const rawLink = anchor.attr('href'); + if (!rawLink || !rawLink.includes('/info/')) { + return null; + } + + const title = anchor.attr('title')?.trim() || item.find('h3, h4, .title, .tit, .bt').first().text().trim() || extractTitle(anchor.text()); + + if (!title) { + return null; + } + + const date = extractDate(item.text()); + + return { + title, + link: new URL(rawLink, link).href, + pubDate: timezone(parseDate(date), +8), + }; + }) + .filter(Boolean) + .slice(0, 10); + + const items = await Promise.all(list.map((item) => cache.tryGet(item.link, async () => await getItemDetail(item)))); + + return { + title: `上海大学计算机工程与科学学院 - ${category.title}`, + description: `上海大学计算机工程与科学学院 - ${category.title}`, + link, + image, + item: items, + }; +} + +async function getItemDetail(item) { + const response = await got.get(item.link); + const $ = load(response.data); + const content = $('.v_news_content, .wp_articlecontent, .article-content, #vsb_content, #vsb_content_2').first(); + + normalizeContentUrls($, content, item.link); + + const attachments = getAttachments($, item.link); + const description = renderDescription(content.html() || item.title, attachments); + + item.title = $('[id$=_lblTitle], .arti_title, .article-title, h1').first().text().trim() || item.title; + item.author = $('[id$=_lblUser], .arti_publisher, .article-author').first().text().trim(); + const pubDate = $('[id$=_lblFB], .arti_update, .article-date').first().text().trim(); + if (pubDate) { + item.pubDate = timezone(parseDate(pubDate), +8); + } + item.description = description; + + if (attachments[0]) { + item.enclosure_url = attachments[0].link; + item.enclosure_type = getEnclosureType(attachments[0]); + } + + return item; +} + +function normalizeContentUrls($, content, baseUrl) { + content.find('a[href]').each((_, element) => { + const href = $(element).attr('href'); + if (href) { + $(element).attr('href', new URL(href, baseUrl).href); + } + }); + + content.find('img[src]').each((_, element) => { + const src = $(element).attr('src'); + if (src) { + $(element).attr('src', new URL(src, baseUrl).href); + } + }); +} + +function getAttachments($, baseUrl) { + return $('form[name="_newscontent_fromname"] ul li a[href], .v_news_content a[href], .wp_articlecontent a[href], .article-content a[href]') + .toArray() + .filter((element) => { + const href = $(element).attr('href') || ''; + return /download|attach|附件|\.pdf|\.docx?|\.xlsx?|\.zip/i.test(href + $(element).text()); + }) + .map((element) => { + const attachment = $(element); + + return { + title: attachment.text().trim() || attachment.attr('title') || '附件', + link: new URL(attachment.attr('href'), baseUrl).href, + }; + }); +} + +function extractTitle(text) { + return text + .replaceAll(/\s+/g, ' ') + .replace(/^\d{1,2}\s+\d{4}[-年./]\d{1,2}(?:[-月./]\d{1,2}日?)?\s*/, '') + .trim(); +} + +function extractDate(text) { + const dayFirst = text.match(/(\d{1,2})\s+(\d{4})-(\d{1,2})/); + if (dayFirst) { + return `${dayFirst[2]}-${dayFirst[3]}-${dayFirst[1].padStart(2, '0')}`; + } + + return text.match(/\d{4}[-年./]\d{1,2}[-月./]\d{1,2}/)?.[0] || ''; +} + +function renderDescription(description, attachments) { + if (attachments.length === 0) { + return description; + } + + const fileList = attachments.map((file) => `
  • ${escapeHtml(file.title)}
  • `).join(''); + return `${description}

    附件

    `; +} + +function getEnclosureType(file) { + const extension = (file.title.split('.').pop() || new URL(file.link).pathname.split('.').pop() || '').toLowerCase(); + const mimeTypes = { + doc: 'application/msword', + docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + pdf: 'application/pdf', + xls: 'application/vnd.ms-excel', + xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + zip: 'application/zip', + }; + + return mimeTypes[extension] || 'application/octet-stream'; +} + +function escapeHtml(text) { + return text.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>').replaceAll('"', '"'); +} From 21bebb4a81db35c8bf149eda812a398ae398bbe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linull/=E6=9D=8E=E6=9E=97?= <2382963480@qq.com> Date: Mon, 25 May 2026 10:41:23 +0800 Subject: [PATCH 03/10] feat(route): add SHU BKSY and CS feeds --- lib/routes/shu/jwb.ts | 71 ++++++++++++------------------------------- 1 file changed, 19 insertions(+), 52 deletions(-) diff --git a/lib/routes/shu/jwb.ts b/lib/routes/shu/jwb.ts index 3c072ccd05ac..826a24740bec 100644 --- a/lib/routes/shu/jwb.ts +++ b/lib/routes/shu/jwb.ts @@ -1,65 +1,32 @@ -import { load } from 'cheerio'; - import type { Route } from '@/types'; -import cache from '@/utils/cache'; -import got from '@/utils/got'; -import { parseDate } from '@/utils/parse-date'; - -const host = 'https://jwb.shu.edu.cn/'; -const alias = new Map([ - ['notice', 'tzgg'], // 通知公告 - ['news', 'xw'], // 新闻动态 - /* ['policy', 'zcwj'], 政策文件 //BUG */ -]); export const route: Route = { - path: ['/jwb/:type?'], + path: '/jwb/:type?', + categories: ['university'], + example: '/shu/jwb/notice', + parameters: { type: '分类,默认为通知公告' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, radar: [ { - source: ['www.shu.edu.cn/index'], - target: '/:type?', + source: ['jwb.shu.edu.cn/', 'jwb.shu.edu.cn/index/tzgg.htm', 'jwb.shu.edu.cn/index/xw.htm'], + target: '/bksy', }, ], - name: '教务部', + name: '教务部(已更名为本科生院)', maintainers: ['tuxinghuan', 'GhhG123'], handler, - description: `| 通知通告 | 新闻 | 政策文件 (bug) | -| -------- | ---- | -------------- | -| notice | news | policy |`, + url: 'bksy.shu.edu.cn/', + description: '上海大学教务部已更名为本科生院,本路由会重定向至 `/shu/bksy/:type?`。', }; -async function handler(ctx) { +function handler(ctx) { const type = ctx.req.param('type') || 'notice'; - const link = `https://jwb.shu.edu.cn/index/${alias.get(type) || type}.htm`; - const respond = await got.get(link); - const $ = load(respond.data); - const title = $('title').text(); - const list = $('.only-list') - .find('li') - .slice(0, 10) - .toArray() - .map((ele) => ({ - title: $(ele).find('a').text(), - link: new URL($(ele).find('a').attr('href'), host).href, - date: $(ele).children('span').text(), - })); - - const all = await Promise.all( - list.map((item) => - cache.tryGet(item.link, async () => { - const response = await got.get(item.link); - const $ = load(response.data); - item.author = $('[id$=_lblUser]').text().trim(); - item.pubDate = parseDate(item.date, 'YYYY年MM月DD日'); - item.description = $('.v_news_content').html() || item.title; - return item; - }) - ) - ); - return { - title, - link, - image: 'https://www.shu.edu.cn/__local/0/08/C6/1EABE492B0CF228A5564D6E6ABE_779D1EE3_5BF7.png', - item: all, - }; + ctx.set('redirect', `/shu/bksy/${type}`); } From a69b311df890acaa2852ebc1de171e102949caca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linull/=E6=9D=8E=E6=9E=97?= <2382963480@qq.com> Date: Mon, 25 May 2026 10:41:25 +0800 Subject: [PATCH 04/10] feat(route): add SHU BKSY and CS feeds --- lib/routes/shu/namespace.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/routes/shu/namespace.ts b/lib/routes/shu/namespace.ts index 35930c2a2167..d443204c13d0 100644 --- a/lib/routes/shu/namespace.ts +++ b/lib/routes/shu/namespace.ts @@ -3,6 +3,6 @@ import type { Namespace } from '@/types'; export const namespace: Namespace = { name: '上海大学', url: 'www.shu.edu.cn', - description: '上海大学相关网网站', + description: '上海大学相关网站', lang: 'zh-CN', }; From 8c6d9ebb98cc4c900cc882db814935a91d23ae27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linull/=E6=9D=8E=E6=9E=97?= <2382963480@qq.com> Date: Mon, 25 May 2026 11:07:56 +0800 Subject: [PATCH 05/10] feat(route): add SHU BKSY and CS feeds --- lib/routes/shu/bksy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/routes/shu/bksy.ts b/lib/routes/shu/bksy.ts index 9ae952b7bc10..812d91d572cf 100644 --- a/lib/routes/shu/bksy.ts +++ b/lib/routes/shu/bksy.ts @@ -55,7 +55,7 @@ export const route: Route = { | notice | news |`, }; -async function handler(ctx) { +export async function handler(ctx) { const routeType = ctx.req.param('type') || 'notice'; const type = alias.get(routeType) || routeType; const category = categories[type] || categories.notice; From e5d3d3425fd1898b9b6cd360c5218a8fc9d1deef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linull/=E6=9D=8E=E6=9E=97?= <2382963480@qq.com> Date: Mon, 25 May 2026 11:07:58 +0800 Subject: [PATCH 06/10] feat(route): add SHU BKSY and CS feeds --- lib/routes/shu/cs.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/routes/shu/cs.ts b/lib/routes/shu/cs.ts index 74a552996f57..0dcb24cab22b 100644 --- a/lib/routes/shu/cs.ts +++ b/lib/routes/shu/cs.ts @@ -17,7 +17,7 @@ const categories = { }; export const route: Route = { - path: ['/cs/:type?', '/ces/:type?'], + path: '/cs/:type?', categories: ['university'], example: '/shu/cs/zytz', parameters: { type: '分类,默认为重要通知' }, @@ -39,14 +39,12 @@ export const route: Route = { maintainers: ['GhhG123', 'linull24'], handler, url: 'cs.shu.edu.cn/', - description: `\`/shu/ces/:type?\` 是兼容别名。 - -| 重要通知 | + description: `| 重要通知 | | -------- | | zytz |`, }; -async function handler(ctx) { +export async function handler(ctx) { const type = ctx.req.param('type') || 'zytz'; const category = categories[type] || categories.zytz; const link = new URL(category.path, rootUrl).href; From 86dea8781dd42c17b077fabbd49013e915e1d8c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linull/=E6=9D=8E=E6=9E=97?= <2382963480@qq.com> Date: Mon, 25 May 2026 11:08:00 +0800 Subject: [PATCH 07/10] feat(route): add SHU BKSY and CS feeds --- lib/routes/shu/ces.ts | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 lib/routes/shu/ces.ts diff --git a/lib/routes/shu/ces.ts b/lib/routes/shu/ces.ts new file mode 100644 index 000000000000..e5a18e3d6b68 --- /dev/null +++ b/lib/routes/shu/ces.ts @@ -0,0 +1,33 @@ +import type { Route } from '@/types'; + +import { handler } from './cs'; + +export const route: Route = { + path: '/ces/:type?', + categories: ['university'], + example: '/shu/ces/zytz', + parameters: { type: '分类,默认为重要通知' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: [ + { + source: ['cs.shu.edu.cn/', 'cs.shu.edu.cn/index/zytz.htm'], + target: '/ces/zytz', + }, + ], + name: '计算机工程与科学学院(CES 兼容)', + maintainers: ['GhhG123', 'linull24'], + handler, + url: 'cs.shu.edu.cn/', + description: `本路由与 \`/shu/cs/:type?\` 使用同一后端。 + +| 重要通知 | +| -------- | +| zytz |`, +}; From fcb8b5adf6cda3e117fe8f3ba929030e0ef5b285 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linull/=E6=9D=8E=E6=9E=97?= <2382963480@qq.com> Date: Mon, 25 May 2026 11:08:01 +0800 Subject: [PATCH 08/10] feat(route): add SHU BKSY and CS feeds --- lib/routes/shu/jwb.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/lib/routes/shu/jwb.ts b/lib/routes/shu/jwb.ts index 826a24740bec..3df227459a05 100644 --- a/lib/routes/shu/jwb.ts +++ b/lib/routes/shu/jwb.ts @@ -1,5 +1,7 @@ import type { Route } from '@/types'; +import { handler } from './bksy'; + export const route: Route = { path: '/jwb/:type?', categories: ['university'], @@ -16,17 +18,12 @@ export const route: Route = { radar: [ { source: ['jwb.shu.edu.cn/', 'jwb.shu.edu.cn/index/tzgg.htm', 'jwb.shu.edu.cn/index/xw.htm'], - target: '/bksy', + target: '/jwb', }, ], name: '教务部(已更名为本科生院)', maintainers: ['tuxinghuan', 'GhhG123'], handler, url: 'bksy.shu.edu.cn/', - description: '上海大学教务部已更名为本科生院,本路由会重定向至 `/shu/bksy/:type?`。', + description: '上海大学教务部已更名为本科生院,本路由与 `/shu/bksy/:type?` 使用同一后端。', }; - -function handler(ctx) { - const type = ctx.req.param('type') || 'notice'; - ctx.set('redirect', `/shu/bksy/${type}`); -} From 049863b5828b1f225fdf282caeaabc07862e5836 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linull/=E6=9D=8E=E6=9E=97?= <2382963480@qq.com> Date: Mon, 25 May 2026 11:08:04 +0800 Subject: [PATCH 09/10] feat(route): add SHU BKSY and CS feeds --- lib/routes/shu/namespace.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/routes/shu/namespace.ts b/lib/routes/shu/namespace.ts index d443204c13d0..aaa6089c6c62 100644 --- a/lib/routes/shu/namespace.ts +++ b/lib/routes/shu/namespace.ts @@ -1,5 +1,3 @@ -import type { Namespace } from '@/types'; - export const namespace: Namespace = { name: '上海大学', url: 'www.shu.edu.cn', From 8bbc744b9baa26f65e5a03413bc7b87ed79dde08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linull/=E6=9D=8E=E6=9E=97?= <2382963480@qq.com> Date: Mon, 25 May 2026 11:16:49 +0800 Subject: [PATCH 10/10] fix(route): restore SHU namespace type import --- lib/routes/shu/namespace.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/routes/shu/namespace.ts b/lib/routes/shu/namespace.ts index aaa6089c6c62..d443204c13d0 100644 --- a/lib/routes/shu/namespace.ts +++ b/lib/routes/shu/namespace.ts @@ -1,3 +1,5 @@ +import type { Namespace } from '@/types'; + export const namespace: Namespace = { name: '上海大学', url: 'www.shu.edu.cn',