mirror of
https://github.com/DIYgod/RSSHub.git
synced 2026-10-28 03:28:35 +08:00
feat(route): add 英雄联盟新闻 (#18100)
* feat(route): add 英雄联盟新闻 * fix: remove deprecated entry ---------
This commit is contained in:
@@ -693,9 +693,6 @@ router.get('/huodongxing/explore', lazyloadRouteHandler('./routes/hdx/explore'))
|
||||
// LWN.net Alerts
|
||||
router.get('/lwn/alerts/:distributor', lazyloadRouteHandler('./routes/lwn/alerts'));
|
||||
|
||||
// 英雄联盟
|
||||
router.get('/lol/newsindex/:type', lazyloadRouteHandler('./routes/lol/newsindex'));
|
||||
|
||||
// 掌上英雄联盟
|
||||
router.get('/lolapp/recommend', lazyloadRouteHandler('./routes/lolapp/recommend'));
|
||||
router.get('/lolapp/article/:uuid', lazyloadRouteHandler('./routes/lolapp/article'));
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
const got = require('@/utils/got');
|
||||
const map = new Map([
|
||||
['zh', { name: '综合', channelid: '23' }],
|
||||
['gg', { name: '公告', channelid: '24' }],
|
||||
['ss', { name: '赛事', channelid: '25' }],
|
||||
['gl', { name: '攻略', channelid: '27' }],
|
||||
['sq', { name: '社区', channelid: '1934' }],
|
||||
]);
|
||||
const refererUrl = 'https://lol.qq.com/news/index.shtml';
|
||||
const apiUrl = 'https://apps.game.qq.com/cmc/zmMcnTargetContentList?r0=jsonp&page=1&num=16&target=';
|
||||
module.exports = async (ctx) => {
|
||||
const type = ctx.params.type || 'all';
|
||||
if (type === 'all') {
|
||||
const tasks = [];
|
||||
for (const value of map.values()) {
|
||||
tasks.push(getPage(value.channelid, value.name));
|
||||
}
|
||||
const results = await Promise.all(tasks);
|
||||
let items = [];
|
||||
for (const result of results) {
|
||||
items = [...items, ...result];
|
||||
}
|
||||
ctx.state.data = {
|
||||
title: `【全部】 - 英雄联盟 - 新闻列表`,
|
||||
link: `https://lol.qq.com/news/index.shtml`,
|
||||
description: `英雄联盟官方网站,海量风格各异的英雄,丰富、便捷的物品合成系统,游戏内置的匹配、排行和竞技系统,独创的“召唤师”系统及技能、符文、天赋等系统组合,必将带你进入一个崭新而又丰富多彩的游戏世界。`,
|
||||
item: items,
|
||||
};
|
||||
} else {
|
||||
const OutName = map.get(type).name;
|
||||
const OutId = map.get(type).channelid;
|
||||
ctx.state.data = {
|
||||
title: `【${OutName}】 - 英雄联盟 - 新闻列表`,
|
||||
link: `https://lol.qq.com/news/index.shtml`,
|
||||
description: `英雄联盟官方网站,海量风格各异的英雄,丰富、便捷的物品合成系统,游戏内置的匹配、排行和竞技系统,独创的“召唤师”系统及技能、符文、天赋等系统组合,必将带你进入一个崭新而又丰富多彩的游戏世界。`,
|
||||
item: await getPage(OutId, OutName),
|
||||
};
|
||||
}
|
||||
async function getPage(id, typeName) {
|
||||
let list;
|
||||
if (id === '1934') {
|
||||
// id=1934,社区的数据是另一个api
|
||||
const response = await got({
|
||||
method: 'get',
|
||||
url: 'https://apps.game.qq.com/cmc/cross?serviceId=3&source=zm&tagids=1934&typeids=1,2&withtop=yes&start=0&limit=16',
|
||||
headers: {
|
||||
Referer: refererUrl,
|
||||
},
|
||||
});
|
||||
list = response.data.data.items;
|
||||
} else {
|
||||
// 非社区的数据处理,多了callback需要截取
|
||||
const response = (
|
||||
await got({
|
||||
method: 'get',
|
||||
url: apiUrl + id,
|
||||
headers: {
|
||||
Referer: refererUrl,
|
||||
},
|
||||
})
|
||||
).data.trim();
|
||||
try {
|
||||
const jsonString = response.slice(9, -2);
|
||||
list = JSON.parse(jsonString).data.result;
|
||||
} catch {
|
||||
// console.error(error);
|
||||
}
|
||||
}
|
||||
function getUrl(sRedirectURL, iDocID, sVID) {
|
||||
// 由于数据源太多,具体的URL返回逻辑可以参考news/index.html页面里面的handleData方法
|
||||
|
||||
if (sRedirectURL) {
|
||||
sRedirectURL = sRedirectURL.indexOf('?') > 0 ? sRedirectURL + '&docid=' + iDocID : sRedirectURL + '?docid=' + iDocID;
|
||||
} else {
|
||||
sRedirectURL = sVID ? 'http://lol.qq.com/v/v2/detail.shtml?docid=' + iDocID : 'http://lol.qq.com/news/detail.shtml?docid=' + iDocID;
|
||||
}
|
||||
|
||||
return sRedirectURL;
|
||||
}
|
||||
return list.map((item) => ({
|
||||
title: `【${typeName}】` + item.sTitle,
|
||||
link: getUrl(item.sRedirectURL, item.iDocID, item.sVID),
|
||||
pubDate: new Date(`${item.sCreated} UTC+8`).toUTCString(),
|
||||
guid: item.iDocID,
|
||||
}));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,206 @@
|
||||
import { type Data, type DataItem, type Route, ViewType } from '@/types';
|
||||
|
||||
import cache from '@/utils/cache';
|
||||
import iconv from 'iconv-lite';
|
||||
import ofetch from '@/utils/ofetch';
|
||||
import { parseDate } from '@/utils/parse-date';
|
||||
import timezone from '@/utils/timezone';
|
||||
|
||||
import { type CheerioAPI, load } from 'cheerio';
|
||||
import { type Context } from 'hono';
|
||||
|
||||
export const handler = async (ctx: Context): Promise<Data> => {
|
||||
const { category = 23 } = ctx.req.param();
|
||||
const limit: number = Number.parseInt(ctx.req.query('limit') ?? '30', 10);
|
||||
|
||||
const baseUrl: string = 'https://lol.qq.com';
|
||||
const apiBaseUrl: string = 'https://apps.game.qq.com';
|
||||
const targetUrl: string = new URL('news/index.shtml', baseUrl).href;
|
||||
const apiListUrl: string = new URL('cmc/zmMcnTargetContentList', apiBaseUrl).href;
|
||||
const apiInfoUrl: string = new URL('cmc/zmMcnContentInfo', apiBaseUrl).href;
|
||||
|
||||
const response = await ofetch(apiListUrl, {
|
||||
query: {
|
||||
page: 1,
|
||||
num: limit,
|
||||
target: category,
|
||||
},
|
||||
});
|
||||
const targetResponse = await ofetch(targetUrl, {
|
||||
responseType: 'arrayBuffer',
|
||||
});
|
||||
|
||||
const $: CheerioAPI = load(iconv.decode(Buffer.from(targetResponse), 'gbk'));
|
||||
const language = $('html').attr('lang') ?? 'zh-CN';
|
||||
|
||||
let items: DataItem[] = [];
|
||||
|
||||
items = response.data.result.slice(0, limit).map((item): DataItem => {
|
||||
const title: string = item.sTitle;
|
||||
const pubDate: number | string = item.sCreated;
|
||||
const linkUrl: string | undefined = item.iDocID ? `${item.iVideoId ? 'v/v2' : 'news'}/detail.shtml?docid=${item.iDocID}` : undefined;
|
||||
const authors: DataItem['author'] = item.sAuthor
|
||||
? [
|
||||
{
|
||||
name: item.sAuthor,
|
||||
avatar: item.sCreaterHeader,
|
||||
},
|
||||
]
|
||||
: undefined;
|
||||
const guid: string = item.iDocID;
|
||||
const image: string | undefined = item.sIMG ? (item.sIMG.startsWith('http') ? item.sIMG : `https:${item.sIMG}`) : undefined;
|
||||
const updated: number | string = item.updated ?? pubDate;
|
||||
|
||||
const processedItem: DataItem = {
|
||||
title,
|
||||
pubDate: pubDate ? timezone(parseDate(pubDate), +8) : undefined,
|
||||
link: linkUrl ? new URL(linkUrl, baseUrl).href : undefined,
|
||||
author: authors,
|
||||
guid,
|
||||
id: guid,
|
||||
image,
|
||||
banner: image,
|
||||
updated: updated ? timezone(parseDate(updated), +8) : undefined,
|
||||
language,
|
||||
};
|
||||
|
||||
return processedItem;
|
||||
});
|
||||
|
||||
items = (
|
||||
await Promise.all(
|
||||
items.map((item) => {
|
||||
if (!item.link) {
|
||||
return item;
|
||||
}
|
||||
|
||||
return cache.tryGet(item.link, async (): Promise<DataItem> => {
|
||||
const detailResponse = await ofetch(apiInfoUrl, {
|
||||
query: {
|
||||
type: 0,
|
||||
docid: item.guid,
|
||||
},
|
||||
});
|
||||
|
||||
const result = detailResponse?.data?.result ?? undefined;
|
||||
|
||||
if (!result) {
|
||||
return item;
|
||||
}
|
||||
|
||||
const title: string = result.sTitle;
|
||||
const description: string = result.sContent;
|
||||
const pubDate: number | string = result.sCreated;
|
||||
const linkUrl: string | undefined = result.iDocID ? `${result.iVideoId ? 'v/v2' : 'news'}/detail.shtml?docid=${result.iDocID}` : undefined;
|
||||
const authors: DataItem['author'] = result.sAuthor
|
||||
? [
|
||||
{
|
||||
name: result.sAuthor,
|
||||
avatar: result.sCreaterHeader,
|
||||
},
|
||||
]
|
||||
: undefined;
|
||||
const guid: string = `qq-lol-${result.iDocID}`;
|
||||
const image: string | undefined = result.sIMG ? (result.sIMG.startsWith('http') ? result.sIMG : `https:${result.sIMG}`) : undefined;
|
||||
const updated: number | string = result.sIdxTime ?? pubDate;
|
||||
|
||||
const processedItem: DataItem = {
|
||||
title,
|
||||
description,
|
||||
pubDate: pubDate ? timezone(parseDate(pubDate), +8) : undefined,
|
||||
link: linkUrl ? new URL(linkUrl, baseUrl).href : undefined,
|
||||
author: authors,
|
||||
guid,
|
||||
id: guid,
|
||||
content: {
|
||||
html: description,
|
||||
text: description,
|
||||
},
|
||||
image,
|
||||
banner: image,
|
||||
updated: updated ? timezone(parseDate(updated), +8) : undefined,
|
||||
language,
|
||||
};
|
||||
|
||||
return {
|
||||
...item,
|
||||
...processedItem,
|
||||
};
|
||||
});
|
||||
})
|
||||
)
|
||||
).filter((_): _ is DataItem => true);
|
||||
|
||||
return {
|
||||
title: `${$('div.website-path a')
|
||||
.toArray()
|
||||
.map((a) => $(a).text())
|
||||
.join('')} - ${$(`li[data-newsId="${category}"]`).text()}`,
|
||||
description: $('meta[name="Description"]').attr('content'),
|
||||
link: targetUrl,
|
||||
item: items,
|
||||
allowEmpty: true,
|
||||
image: `https:${$('a.logo img').attr('src')}`,
|
||||
author: $('meta[name="author"]').attr('content'),
|
||||
language,
|
||||
id: targetUrl,
|
||||
};
|
||||
};
|
||||
|
||||
export const route: Route = {
|
||||
path: '/lol/news/:category?',
|
||||
name: '英雄联盟新闻',
|
||||
url: 'lol.qq.com',
|
||||
maintainers: ['nczitzk'],
|
||||
handler,
|
||||
example: '/qq/lol/news',
|
||||
parameters: {
|
||||
category: '分类,默认为 `23`,即综合,见下表',
|
||||
},
|
||||
description: `:::tip
|
||||
若订阅 [英雄联盟首页新闻列表 - 公告](https://lol.qq.com/news/index.shtml),网址为 \`https://lol.qq.com/news/index.shtml\`,请选择 \`24\` 作为 \`category\` 参数填入,此时目标路由为 [\`/qq/lol/news/24\`](https://rsshub.app/qq/lol/news/24)。
|
||||
:::
|
||||
|
||||
| 综合 | 公告 | 赛事 | 攻略 | 社区 |
|
||||
| ---- | ---- | ---- | ---- | ---- |
|
||||
| 23 | 24 | 25 | 27 | 28 |
|
||||
`,
|
||||
categories: ['game'],
|
||||
features: {
|
||||
requireConfig: false,
|
||||
requirePuppeteer: false,
|
||||
antiCrawler: false,
|
||||
supportRadar: true,
|
||||
supportBT: false,
|
||||
supportPodcast: false,
|
||||
supportScihub: false,
|
||||
},
|
||||
radar: [
|
||||
{
|
||||
title: '综合',
|
||||
source: ['lol.qq.com/news/index.shtml'],
|
||||
target: '/lol/news/23',
|
||||
},
|
||||
{
|
||||
title: '公告',
|
||||
source: ['lol.qq.com/news/index.shtml'],
|
||||
target: '/lol/news/24',
|
||||
},
|
||||
{
|
||||
title: '赛事',
|
||||
source: ['lol.qq.com/news/index.shtml'],
|
||||
target: '/lol/news/25',
|
||||
},
|
||||
{
|
||||
title: '攻略',
|
||||
source: ['lol.qq.com/news/index.shtml'],
|
||||
target: '/lol/news/27',
|
||||
},
|
||||
{
|
||||
title: '社区',
|
||||
source: ['lol.qq.com/news/index.shtml'],
|
||||
target: '/lol/news/28',
|
||||
},
|
||||
],
|
||||
view: ViewType.Articles,
|
||||
};
|
||||
Reference in New Issue
Block a user