From 8a21eadcf2c397de629221325c6144e89006f0f1 Mon Sep 17 00:00:00 2001 From: naremloa Date: Sat, 4 Jan 2025 20:23:45 +0800 Subject: [PATCH 1/5] feat(add route): Add steam curator reviews route --- lib/routes/steam/curator.ts | 73 +++++++++++++++++++ .../steam/templates/curator-description.art | 4 + 2 files changed, 77 insertions(+) create mode 100644 lib/routes/steam/curator.ts create mode 100644 lib/routes/steam/templates/curator-description.art diff --git a/lib/routes/steam/curator.ts b/lib/routes/steam/curator.ts new file mode 100644 index 00000000000000..3e3ce8163766fc --- /dev/null +++ b/lib/routes/steam/curator.ts @@ -0,0 +1,73 @@ +import { Route } from '@/types'; +import ofetch from '@/utils/ofetch'; +import { load } from 'cheerio'; +import { parseDate } from '@/utils/parse-date'; +import { getCurrentPath } from '@/utils/helpers'; +import path from 'node:path'; +import { art } from '@/utils/render'; + +const __dirname = getCurrentPath(import.meta.url); + +export const route: Route = { + path: '/curator/:id', + categories: ['game'], + example: '/steam/curator/34646096-80-Days', + parameters: { + id: "Steam curator id. It usually consists of a sereis of numbers and the curator's name.", + }, + radar: [ + { + title: 'Latest Curator Reviews', + source: ['store.steampowered.com/curator/:id'], + target: '/curator/:id', + }, + ], + description: 'The Latest reviews from a Steam Curator.', + name: 'Latest Curator Reviews', + maintainers: ['naremloa', 'fenx'], + handler: async (ctx) => { + const { id } = ctx.req.param(); + + const url = `https://store.steampowered.com/curator/${id}`; + const response = await ofetch(url); + const $ = load(response); + + const items = await Promise.all( + $('#RecommendationsRows .recommendation') + .toArray() + .slice(0, 1) + .map((item) => { + const el = $(item); + const appTitle = el.find('img').attr('alt')!; + const appLink = el.find('.recommendation_link').first().attr('href'); + const appImage = el.find('img').attr('src') ?? ''; + const reviewContent = el.find('.recommendation_desc').text().trim(); + const reviewDateText = el.find('.curator_review_date').text().trim(); + + const currentYearPattern = /,\s\b\d{4}\b$/; + const reviewPubDate = currentYearPattern.test(reviewDateText) ? parseDate(`${reviewDateText}, ${new Date().getFullYear()}`) : parseDate(reviewDateText); + + const description = art(path.join(__dirname, 'templates/curator-description.art'), { image: appImage, description: reviewContent }); + + return { + title: appTitle, + link: appLink, + description, + pubDate: reviewPubDate, + media: { + content: { + url: appImage, + medium: 'image', + }, + }, + }; + }) + ); + + return { + title: `Steam Curator ${id} Reviews`, + link: url, + item: items, + }; + }, +}; diff --git a/lib/routes/steam/templates/curator-description.art b/lib/routes/steam/templates/curator-description.art new file mode 100644 index 00000000000000..e77f842b8e8fb6 --- /dev/null +++ b/lib/routes/steam/templates/curator-description.art @@ -0,0 +1,4 @@ +{{ if image }} + +{{ /if }} +

{{ description }}

From cc8e4809dcff8a4f083293b0f621b499442f44ef Mon Sep 17 00:00:00 2001 From: naremloa Date: Sat, 4 Jan 2025 20:52:23 +0800 Subject: [PATCH 2/5] fix: modify the maintainer's name to fenxer --- lib/routes/steam/curator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/routes/steam/curator.ts b/lib/routes/steam/curator.ts index 3e3ce8163766fc..bf434e8b9977d2 100644 --- a/lib/routes/steam/curator.ts +++ b/lib/routes/steam/curator.ts @@ -24,7 +24,7 @@ export const route: Route = { ], description: 'The Latest reviews from a Steam Curator.', name: 'Latest Curator Reviews', - maintainers: ['naremloa', 'fenx'], + maintainers: ['naremloa', 'fenxer'], handler: async (ctx) => { const { id } = ctx.req.param(); From 6e5922ac2b00d8ea21a85d42e28db4164bed7984 Mon Sep 17 00:00:00 2001 From: naremloa Date: Sun, 5 Jan 2025 23:20:45 +0800 Subject: [PATCH 3/5] fix: replace API to `/ajaxgetfilteredrecommendations` fix: something unnecessary --- lib/routes/steam/curator.ts | 93 ++++++++++++++++++++++++------------- 1 file changed, 60 insertions(+), 33 deletions(-) diff --git a/lib/routes/steam/curator.ts b/lib/routes/steam/curator.ts index bf434e8b9977d2..9b4b198872696b 100644 --- a/lib/routes/steam/curator.ts +++ b/lib/routes/steam/curator.ts @@ -9,11 +9,26 @@ import { art } from '@/utils/render'; const __dirname = getCurrentPath(import.meta.url); export const route: Route = { - path: '/curator/:id', + path: '/curator/:id/:routeParams?', categories: ['game'], example: '/steam/curator/34646096-80-Days', parameters: { id: "Steam curator id. It usually consists of a sereis of numbers and the curator's name.", + routeParams: { + description: `Extra parameters to filter the reviews. The following parameters are supported: +| Key | Description | Accepts | Defaults to | +| --------------- | --------------------------------------------------------------------------------------------- | ------------------------------------------ | ----------- | +| \`curations\` | Review type to filter by. \`0\`: Recommended, \`1\`: Not Recommended, \`2\`: Informational | \`0\`/\`1\`/\`2\`/\`0,1\`/\`0,2\`/\`1,2\` | | +| \`tagids\` | Tag to filter by. Details are provided below. | use comma to separate multiple tagid | | + +Note: There is a [‘Popular Tags’](https://store.steampowered.com/tag/browse) page where you can find many but not all of the tags. The tag’s ID is in the \`data-tagid\` attribute of the element.Steam does not currently provide a page that comprehensively lists all tags, and you may need to explore alternative ways to find them. + +Examples: +* \`/steam/curator/34646096-80-Days/curations=&tagids=\` +* \`/steam/curator/34646096-80-Days/curations=0&tagids=19\` +* \`/steam/curator/34646096-80-Days/curations=0,2&tagids=19,21\` +`, + }, }, radar: [ { @@ -26,47 +41,59 @@ export const route: Route = { name: 'Latest Curator Reviews', maintainers: ['naremloa', 'fenxer'], handler: async (ctx) => { - const { id } = ctx.req.param(); + const { id, routeParams } = ctx.req.param(); + const params = new URLSearchParams(routeParams); + + const url = new URL(`https://store.steampowered.com/curator/${id}/ajaxgetfilteredrecommendations/?query&start=0&count=10&dynamic_data=&sort=recent&app_types=&reset=false&curations=&tagids=`); + for (const [key, value] of params) { + let setValue = value; + switch (key) { + case 'curations': + case 'tagids': + setValue = value || ''; + break; + default: + continue; + } + url.searchParams.set(key, setValue); + } - const url = `https://store.steampowered.com/curator/${id}`; - const response = await ofetch(url); - const $ = load(response); + const response = await ofetch(url.toString()); + const $ = load(response.results_html ?? ''); - const items = await Promise.all( - $('#RecommendationsRows .recommendation') - .toArray() - .slice(0, 1) - .map((item) => { - const el = $(item); - const appTitle = el.find('img').attr('alt')!; - const appLink = el.find('.recommendation_link').first().attr('href'); - const appImage = el.find('img').attr('src') ?? ''; - const reviewContent = el.find('.recommendation_desc').text().trim(); - const reviewDateText = el.find('.curator_review_date').text().trim(); + const items = $('.recommendation') + .toArray() + .map((item) => { + const el = $(item); + const appImageEl = el.find('a.store_capsule img'); + const appTitle = appImageEl.attr('alt')!; + const appImage = appImageEl.attr('src') ?? ''; + const appLink = el.find('.recommendation_link').first().attr('href'); + const reviewContent = el.find('.recommendation_desc').text().trim(); + const reviewDateText = el.find('.curator_review_date').text().trim(); - const currentYearPattern = /,\s\b\d{4}\b$/; - const reviewPubDate = currentYearPattern.test(reviewDateText) ? parseDate(`${reviewDateText}, ${new Date().getFullYear()}`) : parseDate(reviewDateText); + const notCurrentYearPattern = /,\s\b\d{4}\b$/; + const reviewPubDate = notCurrentYearPattern.test(reviewDateText) ? parseDate(reviewDateText) : parseDate(`${reviewDateText}, ${new Date().getFullYear()}`); - const description = art(path.join(__dirname, 'templates/curator-description.art'), { image: appImage, description: reviewContent }); + const description = art(path.join(__dirname, 'templates/curator-description.art'), { image: appImage, description: reviewContent }); - return { - title: appTitle, - link: appLink, - description, - pubDate: reviewPubDate, - media: { - content: { - url: appImage, - medium: 'image', - }, + return { + title: appTitle, + link: appLink, + description, + pubDate: reviewPubDate, + media: { + content: { + url: appImage, + medium: 'image', }, - }; - }) - ); + }, + }; + }); return { title: `Steam Curator ${id} Reviews`, - link: url, + link: url.toString(), item: items, }; }, From 4da246e016e4a4b2166e9f9129842959a5755ed2 Mon Sep 17 00:00:00 2001 From: naremloa Date: Mon, 6 Jan 2025 14:18:09 +0800 Subject: [PATCH 4/5] fix: DeepScan error: value assigned to variable 'setValue' at this point is not used before it is overwritten --- lib/routes/steam/curator.ts | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/lib/routes/steam/curator.ts b/lib/routes/steam/curator.ts index 9b4b198872696b..3bd71d621fc72a 100644 --- a/lib/routes/steam/curator.ts +++ b/lib/routes/steam/curator.ts @@ -46,16 +46,9 @@ Examples: const url = new URL(`https://store.steampowered.com/curator/${id}/ajaxgetfilteredrecommendations/?query&start=0&count=10&dynamic_data=&sort=recent&app_types=&reset=false&curations=&tagids=`); for (const [key, value] of params) { - let setValue = value; - switch (key) { - case 'curations': - case 'tagids': - setValue = value || ''; - break; - default: - continue; + if (['curations', 'tagids'].includes(key)) { + url.searchParams.set(key, value || ''); } - url.searchParams.set(key, setValue); } const response = await ofetch(url.toString()); From c21ab07c829091adc8a6a24cad6d43859d344b39 Mon Sep 17 00:00:00 2001 From: Tony Date: Tue, 7 Jan 2025 00:50:03 +0800 Subject: [PATCH 5/5] Update lib/routes/steam/curator.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- lib/routes/steam/curator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/routes/steam/curator.ts b/lib/routes/steam/curator.ts index 3bd71d621fc72a..a99329bc54edd2 100644 --- a/lib/routes/steam/curator.ts +++ b/lib/routes/steam/curator.ts @@ -13,7 +13,7 @@ export const route: Route = { categories: ['game'], example: '/steam/curator/34646096-80-Days', parameters: { - id: "Steam curator id. It usually consists of a sereis of numbers and the curator's name.", + id: "Steam curator id. It usually consists of a series of numbers and the curator's name.", routeParams: { description: `Extra parameters to filter the reviews. The following parameters are supported: | Key | Description | Accepts | Defaults to |