rsshub
Version:
Make RSS Great Again!
198 lines (186 loc) • 5.87 kB
text/typescript
import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
export const route: Route = {
path: '/news/:language?/:category?',
categories: ['game'],
example: '/blizzard/news',
parameters: { language: 'Language code, see below, en-US by default', category: 'Category, see below, All News by default' },
features: {
requireConfig: false,
requirePuppeteer: false,
antiCrawler: false,
supportBT: false,
supportPodcast: false,
supportScihub: false,
},
name: 'News',
maintainers: ['nczitzk'],
handler,
description: `Categories
| Category | Slug |
| ---------------------- | ------------------- |
| All News | |
| Diablo II: Resurrected | diablo2 |
| Diablo III | diablo3 |
| Diablo IV | diablo4 |
| Diablo Immortal | diablo-immortal |
| Hearthstone | hearthstone |
| Heroes of the Storm | heroes-of-the-storm |
| Overwatch 2 | overwatch |
| StarCraft: Remastered | starcraft |
| StarCraft II | starcraft2 |
| World of Warcraft | world-of-warcraft |
| Warcraft 3: Reforged | warcraft3 |
| Warcraft Rumble | warcraft-rumble |
| Battle.net | battlenet |
| BlizzCon | blizzcon |
| Inside Blizzard | blizzard |
Language codes
| Language | Code |
| ------------------ | ----- |
| Deutsch | de-de |
| English (US) | en-us |
| English (EU) | en-gb |
| Español (EU) | es-es |
| Español (Latino) | es-mx |
| Français | fr-fr |
| Italiano | it-it |
| Português (Brasil) | pt-br |
| Polski | pl-pl |
| Русский | ru-ru |
| 한국어 | ko-kr |
| ภาษาไทย | th-th |
| 日本語 | ja-jp |
| 繁體中文 | zh-tw |`,
};
const GAME_MAP = {
diablo2: {
key: 'diablo2',
value: 'diablo-2-resurrected',
id: 'blt54fbd3787a705054',
},
diablo3: {
key: 'diablo3',
value: 'diablo-3',
id: 'blt2031aef34200656d',
},
diablo4: {
key: 'diablo4',
value: 'diablo-4',
id: 'blt795c314400d7ded9',
},
'diablo-immortal': {
key: 'diablo-immortal',
value: 'diablo-immortal',
id: 'blt525c436e4a1b0a97',
},
hearthstone: {
key: 'hearthstone',
value: 'hearthstone',
id: 'blt5cfc6affa3ca0638',
},
'heroes-of-the-storm': {
key: 'heroes-of-the-storm',
value: 'heroes-of-the-storm',
id: 'blt2e50e1521bb84dc6',
},
overwatch: {
key: 'overwatch',
value: 'overwatch',
id: 'blt376fb94931906b6f',
},
starcraft: {
key: 'starcraft',
value: 'starcraft',
id: 'blt81d46fcb05ab8811',
},
starcraft2: {
key: 'starcraft2',
value: 'starcraft-2',
id: 'bltede2389c0a8885aa',
},
'world-of-warcraft': {
key: 'world-of-warcraft',
value: 'world-of-warcraft',
id: 'blt2caca37e42f19839',
},
warcraft3: {
key: 'warcraft3',
value: 'warcraft-3',
id: 'blt24859ba8086fb294',
},
'warcraft-rumble': {
key: 'warcraft-rumble',
value: 'warcraft-rumble',
id: 'blte27d02816a8ff3e1',
},
battlenet: {
key: 'battlenet',
value: 'battle-net',
id: 'blt90855744d00cd378',
},
blizzcon: {
key: 'blizzcon',
value: 'blizzcon',
id: 'bltec70ad0ea4fd6d1d',
},
blizzard: {
key: 'blizzard',
value: 'blizzard',
id: 'blt500c1f8b5470bfdb',
},
};
function getSearchParams(category = 'all') {
return category === 'all'
? Object.values(GAME_MAP)
.map((item) => `feedCxpProductIds[]=${item.id}`)
.join('&')
: `feedCxpProductIds[]=${GAME_MAP[category].id}`;
}
async function handler(ctx) {
const category = GAME_MAP[ctx.req.param('category')]?.key || 'all';
const language = ctx.req.param('language') || 'en-us';
const rootUrl = `https://news.blizzard.com/${language}`;
const currentUrl = category === 'all' ? rootUrl : `${rootUrl}/?filter=${GAME_MAP[category].value}`;
const apiUrl = `${rootUrl}/api/news/blizzard`;
let rssTitle = '';
const {
data: {
feed: { contentItems: response },
},
} = await got(`${apiUrl}?${getSearchParams(category)}`);
const list = response.map((item) => {
const content = item.properties;
rssTitle = category === 'all' ? 'All News' : content.cxpProduct.title; // 这个是用来填充 RSS 订阅源频道级别 title,没别的地方能拿到了(而且会根据语言切换)
return {
title: content.title,
link: content.newsUrl,
author: content.author,
category: content.category,
guid: content.newsId,
description: content.summary,
pubDate: content.lastUpdated,
};
});
const items = await Promise.all(
list.map((item) =>
cache.tryGet(item.link, async () => {
try {
const { data: response } = await got(item.link);
const $ = load(response);
item.description = $('.Content').html();
return item;
} catch {
return item;
}
})
)
);
return {
title: rssTitle,
link: currentUrl,
item: items,
};
}