-
Notifications
You must be signed in to change notification settings - Fork 9.8k
feat: add China National Museum exhibition route #21891
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 7 commits
6bbcc4d
5ff9b51
19a0712
8795c62
6656365
93b8e70
36334e9
3787bd5
7aaf6d4
daabbb0
0d3c395
0068743
5ae90b7
4e6a74e
a2be2b4
b765327
03c2b18
e6df7dc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,140 @@ | ||||||
| import { load } from 'cheerio'; | ||||||
|
github-advanced-security[bot] marked this conversation as resolved.
Fixed
github-advanced-security[bot] marked this conversation as resolved.
Fixed
github-advanced-security[bot] marked this conversation as resolved.
Fixed
|
||||||
| import type { Context } from 'hono'; | ||||||
|
|
||||||
| import type { Data, DataItem, Route } from '@/types'; | ||||||
| import cache from '@/utils/cache'; | ||||||
| import got from '@/utils/got'; | ||||||
| import { parseDate } from '@/utils/parse-date'; | ||||||
|
github-advanced-security[bot] marked this conversation as resolved.
Fixed
|
||||||
|
|
||||||
| // Formatting Function: Returns YYYY-MM-DD only if there are 3 valid numeric segments; otherwise, returns undefined. | ||||||
| const formatExhibitionDate = (dateStr: string | undefined): string | undefined => { | ||||||
| if (!dateStr) return undefined; | ||||||
Check failureCode scanning / oxlint eslint(curly) Error
Expected { after 'if' condition.
Replace return undefined; with {return undefined;}. |
||||||
|
github-advanced-security[bot] marked this conversation as resolved.
Fixed
|
||||||
| const normalized = dateStr.replace(/年|月/g, '-').replace(/日/g, '').replace(/\//g, '-').replace(/\./g, '-'); | ||||||
Check failureCode scanning / oxlint eslint-plugin-unicorn(prefer-string-replace-all) Error
Prefer String#replaceAll() over String#replace() when using a regex with the global flag.
Replace replace with replaceAll. Check failureCode scanning / oxlint eslint-plugin-unicorn(prefer-string-replace-all) Error
Prefer String#replaceAll() over String#replace() when using a regex with the global flag.
Replace replace with replaceAll. Check failureCode scanning / oxlint eslint-plugin-unicorn(prefer-string-replace-all) Error
Prefer String#replaceAll() over String#replace() when using a regex with the global flag.
Replace replace with replaceAll. Check failureCode scanning / oxlint eslint-plugin-unicorn(prefer-string-replace-all) Error
Prefer String#replaceAll() over String#replace() when using a regex with the global flag.
Replace replace with replaceAll. |
||||||
|
github-advanced-security[bot] marked this conversation as resolved.
Fixed
github-advanced-security[bot] marked this conversation as resolved.
Fixed
github-advanced-security[bot] marked this conversation as resolved.
Fixed
github-advanced-security[bot] marked this conversation as resolved.
Fixed
|
||||||
| const parts = normalized.split('-').filter(Boolean); | ||||||
| if (parts.length === 3) { | ||||||
| return `${parts[0]}-${parts[1].padStart(2, '0')}-${parts[2].padStart(2, '0')}`; | ||||||
| } | ||||||
| return undefined; | ||||||
| }; | ||||||
|
|
||||||
| const parseExhibitionDuration = (duration: string) => { | ||||||
| // Remove all spaces and parentheses to prevent regex matching from breaking | ||||||
| const cleanStr = duration.replace(/\s+/g, '').replace(/([^)]*)/g, '').trim(); | ||||||
Check failureCode scanning / oxlint eslint-plugin-unicorn(prefer-string-replace-all) Error
Prefer String#replaceAll() over String#replace() when using a regex with the global flag.
Replace replace with replaceAll. Check failureCode scanning / oxlint eslint-plugin-unicorn(prefer-string-replace-all) Error
Prefer String#replaceAll() over String#replace() when using a regex with the global flag.
Replace replace with replaceAll. |
||||||
|
github-advanced-security[bot] marked this conversation as resolved.
Fixed
github-advanced-security[bot] marked this conversation as resolved.
Fixed
|
||||||
|
|
||||||
| // Match YYYY-MM-DD or MM-DD | ||||||
| const dateRegex = /(\d{4}[./-年]\d{1,2}[./-月]\d{1,2}日?)|(\d{1,2}[./-月]\d{1,2}日?)/g; | ||||||
Check warningCode scanning / CodeQL Overly permissive regular expression range Medium
Suspicious character range that is equivalent to \[\/0-9:;<=>?@A-Z\\[\\\\]^_`a-z{{|}}~\u007f\u0080\u0081....
Check warningCode scanning / CodeQL Overly permissive regular expression range Medium
Suspicious character range that is equivalent to \[\/0-9:;<=>?@A-Z\\[\\\\]^_`a-z{{|}}~\u007f\u0080\u0081....
Check warningCode scanning / CodeQL Overly permissive regular expression range Medium
Suspicious character range that is equivalent to \[\/0-9:;<=>?@A-Z\\[\\\\]^_`a-z{{|}}~\u007f\u0080\u0081....
|
||||||
|
github-advanced-security[bot] marked this conversation as resolved.
Fixed
github-advanced-security[bot] marked this conversation as resolved.
Fixed
github-advanced-security[bot] marked this conversation as resolved.
Fixed
|
||||||
| const allDates = cleanStr.match(dateRegex) || []; | ||||||
|
|
||||||
| let startDateRaw: string | undefined; | ||||||
| let endDateRaw: string | undefined; | ||||||
|
|
||||||
| if (cleanStr.startsWith('展至') && allDates.length > 0) { | ||||||
| endDateRaw = allDates[0]; | ||||||
| } else if (allDates.length >= 2) { | ||||||
| startDateRaw = allDates[0]; | ||||||
| let rawEnd = allDates[1]; | ||||||
| // Logic to complete the year | ||||||
| if (!/\d{4}/.test(rawEnd) && startDateRaw) { | ||||||
| const startYear = startDateRaw.match(/^\d{4}/); | ||||||
| if (startYear) { | ||||||
| const sep = startDateRaw.includes('年') ? '年' : (startDateRaw.includes('/') ? '/' : '-'); | ||||||
| rawEnd = `${startYear[0]}${sep}${rawEnd}`; | ||||||
| } | ||||||
| } | ||||||
| endDateRaw = rawEnd; | ||||||
| } else if (allDates.length === 1) { | ||||||
| if (cleanStr.includes('展至')) { | ||||||
| endDateRaw = allDates[0]; | ||||||
| } else { | ||||||
| startDateRaw = allDates[0]; | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| return { | ||||||
| startDate: formatExhibitionDate(startDateRaw), | ||||||
| endDate: formatExhibitionDate(endDateRaw) | ||||||
| }; | ||||||
| }; | ||||||
|
|
||||||
| export const route: Route = { | ||||||
| path: '/zl', | ||||||
| categories: ['travel'], | ||||||
| example: '/chnmuseum/zl', | ||||||
| // Use Chnmuseum English version channel name | ||||||
| name: 'Current Exhibitions', | ||||||
| maintainers: ['magazian'], | ||||||
| radar: [ | ||||||
| { | ||||||
| source: ['www.chnmuseum.cn/zl/'], | ||||||
| target: '/zl', | ||||||
| }, | ||||||
| ], | ||||||
| handler: async (_ctx: Context): Promise<Data> => { | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
github-advanced-security[bot] marked this conversation as resolved.
Fixed
|
||||||
| const baseUrl = 'https://www.chnmuseum.cn'; | ||||||
| const url = `${baseUrl}/zl/`; | ||||||
|
|
||||||
| const response = await got(url); | ||||||
| const $ = load(response.data); | ||||||
|
|
||||||
| const list: DataItem[] = await Promise.all( | ||||||
| $('ul#div li').toArray().map(async (item) => { | ||||||
| const $item = $(item); | ||||||
|
|
||||||
| const rawLink = $item.find('a.recurl').attr('href') || ''; | ||||||
| const itemLink = rawLink.startsWith('.') ? new URL(rawLink, url).href : `${baseUrl}${rawLink}`; | ||||||
|
|
||||||
| const title = $item.find('div.cj_zxx3 p').text(); | ||||||
| const imgUrl = new URL($item.find('img').first().attr('src') || '', url).href; | ||||||
| const location = $item.find('div.cj_zxx1').text().trim(); | ||||||
|
|
||||||
| let fullDuration = $item.find('div.cj_zxx2 p').text().trim(); | ||||||
|
|
||||||
| // --- Check if the text ends with "..." If so, proceed to fetch the full version. --- | ||||||
| if (fullDuration.endsWith('...')) { | ||||||
| fullDuration = await cache.tryGet(itemLink, async () => { | ||||||
| const detailResponse = await got(itemLink); | ||||||
| const html = detailResponse.data; | ||||||
| const dateRegex = /var\s+qtxszq\s*=\s*"(.*?)";/; | ||||||
| const match = html.match(dateRegex); | ||||||
| return match && match[1] ? match[1] : fullDuration; | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi Tony, thanks for your suggestion, but here I would like to keep as it is. I cannot use |
||||||
| }) as string; | ||||||
| } | ||||||
|
|
||||||
| const { startDate, endDate } = parseExhibitionDuration(fullDuration); | ||||||
|
|
||||||
| // Variable Handling: If the value is `undefined`, it remains `undefined` to facilitate subsequent processing by the Calendar component. | ||||||
| const pubDate = startDate ? parseDate(startDate) : undefined; | ||||||
|
|
||||||
| const $desc = load('<div></div>', null, false); | ||||||
|
|
||||||
| if (imgUrl) { | ||||||
| $desc('div').append(`<img src="${imgUrl}">`); | ||||||
| $desc('div').append('<br>'); | ||||||
| } | ||||||
| $desc('div').append(`<p><b>地点:</b>${location}</p>`); | ||||||
| $desc('div').append(`<p><b>开展:</b>${startDate ?? '未定/常设'}</p>`); | ||||||
| $desc('div').append(`<p><b>闭展:</b>${endDate ?? '未定/常设'}</p>`); | ||||||
| $desc('div').append(`<p><small>原始展期:${fullDuration}</small></p>`); | ||||||
|
|
||||||
| return { | ||||||
| title, | ||||||
| link: itemLink, | ||||||
| pubDate, | ||||||
| description: $desc.html(), | ||||||
| // For further processing in Calendar, keep the raw dates in _extra | ||||||
| _extra: { | ||||||
| startDate, | ||||||
| endDate | ||||||
| } | ||||||
| }; | ||||||
| }) | ||||||
| ); | ||||||
|
|
||||||
| return { | ||||||
| title: '中国国家博物馆 - 正在展出', | ||||||
| link: url, | ||||||
| language: 'zh-CN', | ||||||
| item: list, | ||||||
| }; | ||||||
| }, | ||||||
| }; | ||||||
Uh oh!
There was an error while loading. Please reload this page.