From 02826c6d9ced88468bb32e331773bfe2bf7c0b8a Mon Sep 17 00:00:00 2001 From: Tony Date: Thu, 22 Jan 2026 07:02:53 +0800 Subject: [PATCH] feat(route): add slashdot (#20942) * feat(route): add slashdot * chore: apply changes --- lib/routes/slashdot/index.ts | 87 ++++++++++++++++++++++++++++++++ lib/routes/slashdot/namespace.ts | 7 +++ 2 files changed, 94 insertions(+) create mode 100644 lib/routes/slashdot/index.ts create mode 100644 lib/routes/slashdot/namespace.ts diff --git a/lib/routes/slashdot/index.ts b/lib/routes/slashdot/index.ts new file mode 100644 index 000000000..dee62999f --- /dev/null +++ b/lib/routes/slashdot/index.ts @@ -0,0 +1,87 @@ +import { load } from 'cheerio'; + +import { InvalidParameterError } from '@/errors/types/invalid-parameter'; +import type { Route } from '@/types'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; +import { isValidHost } from '@/utils/valid-host'; + +export const route: Route = { + path: '/:section?', + categories: ['new-media'], + example: '/slashdot', + parameters: { section: 'Section name, can be found in the URL host, leave empty for the main page' }, + radar: [ + { + source: ['slashdot.org'], + }, + { + source: ['devices.slashdot.org'], + target: '/devices', + }, + { + source: ['build.slashdot.org'], + target: '/build', + }, + { + source: ['entertainment.slashdot.org'], + target: '/entertainment', + }, + { + source: ['technology.slashdot.org'], + target: '/technology', + }, + { + source: ['science.slashdot.org'], + target: '/science', + }, + { + source: ['yro.slashdot.org'], + target: '/yro', + }, + ], + name: 'News', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { + const { section } = ctx.req.param(); + + if (section && !isValidHost(section)) { + throw new InvalidParameterError('Invalid section name'); + } + + const link = section ? `https://${section}.slashdot.org` : 'https://slashdot.org'; + const response = await ofetch(link); + const $ = load(response); + + const list = $('.article') + .toArray() + .map((item) => { + const $item = $(item); + const a = $item.find('.story-title a').first(); + const details = $item.find('.details'); + + return { + title: a.text(), + link: a.attr('href'), + description: $item.find('.body').html(), + pubDate: parseDate( + details + .find('time') + .attr('datetime') + ?.replace(/on\s\w+?day\s/, '') + ?.replace('@', '') + ?.replace(/(\d{2}:\d{2})(\w{2})$/, '$1 $2') + ), + }; + }); + + return { + title: $('head title').text(), + description: $('meta[name="description"]').attr('content'), + link, + item: list, + }; +} diff --git a/lib/routes/slashdot/namespace.ts b/lib/routes/slashdot/namespace.ts new file mode 100644 index 000000000..cce3d7d2c --- /dev/null +++ b/lib/routes/slashdot/namespace.ts @@ -0,0 +1,7 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'Slashdot', + url: 'slashdot.org', + lang: 'en', +};