feat(route): add back matters (#15687)

This commit is contained in:
Tony
2024-05-24 16:12:42 +08:00
committed by GitHub
parent 3a58d90698
commit f93bb8cb9a
9 changed files with 259 additions and 180 deletions
-6
View File
@@ -594,12 +594,6 @@ router.get('/zucc/cssearch/latest/:webVpn/:key', lazyloadRouteHandler('./routes/
// checkee
router.get('/checkee/:dispdate', lazyloadRouteHandler('./routes/checkee/index'));
// Matters
router.get('/matters/latest/:type?', lazyloadRouteHandler('./routes/matters/latest'));
router.redirect('/matters/hot', '/matters/latest/heat'); // Deprecated
router.get('/matters/tags/:tid', lazyloadRouteHandler('./routes/matters/tags'));
router.get('/matters/author/:uid', lazyloadRouteHandler('./routes/matters/author'));
// 古诗文网
router.get('/gushiwen/recommend/:annotation?', lazyloadRouteHandler('./routes/gushiwen/recommend'));
-49
View File
@@ -1,49 +0,0 @@
const got = require('@/utils/got');
const { parseDate } = require('@/utils/parse-date');
module.exports = async (ctx) => {
const uid = ctx.params.uid;
const host = `https://matters.news`;
const url = `https://server.matters.news/graphql`;
const response = await got({
method: 'post',
url,
json: {
query: `
{
user(input: { userName: "${uid}" }) {
displayName
info {
description
}
articles(input: { first: 20 }) {
edges {
node {
slug
mediaHash
title
content
createdAt
}
}
}
}
}`,
},
});
const user = response.data.data.user;
ctx.state.data = {
title: `Matters | ${user.displayName}`,
link: `${host}/@${uid}`,
description: user.info.description,
item: user.articles.edges.map(({ node: article }) => ({
title: article.title,
author: user.displayName,
description: article.content,
link: `${host}/@${uid}/${article.slug}-${article.mediaHash}`,
pubDate: parseDate(article.createdAt),
})),
};
};
-69
View File
@@ -1,69 +0,0 @@
const got = require('@/utils/got');
const { parseDate } = require('@/utils/parse-date');
module.exports = async (ctx) => {
const type = ctx.params.type || 'latest';
const url = `https://server.matters.news/graphql`;
const options = {
latest: {
title: '最新',
apiType: 'newest',
},
heat: {
title: '熱議',
apiType: 'hottest',
},
essence: {
title: '精華',
apiType: 'icymi',
},
};
const response = await got({
method: 'post',
url,
json: {
query: `
{
viewer {
id
recommendation {
feed: ${options[type].apiType}(input: { first: 10 }) {
edges {
node {
author {
userName
displayName
}
slug
mediaHash
title
content
createdAt
}
}
}
}
}
}`,
},
});
const item = response.data.data.viewer.recommendation.feed.edges.map(({ node }) => {
const link = `https://matters.news/@${node.author.userName}/${node.slug}-${node.mediaHash}`;
const article = node.content;
return {
title: node.title,
link,
description: article,
author: node.author.displayName,
pubDate: parseDate(node.createdAt),
};
});
ctx.state.data = {
title: `Matters | ${options[type].title}`,
link: 'https://matters.news/',
item,
};
};
-56
View File
@@ -1,56 +0,0 @@
const got = require('@/utils/got');
const { parseDate } = require('@/utils/parse-date');
module.exports = async (ctx) => {
const tid = ctx.params.tid;
const host = `https://matters.news`;
const url = `https://server.matters.news/graphql`;
const response = await got({
method: 'post',
url,
json: {
query: `
{
node(input: { id: "${tid}" }) {
... on Tag {
content
articles(input:{first: 20}){
edges {
node {
id
title
slug
cover
summary
mediaHash
content
createdAt
author {
id
userName
displayName
}
}
}
}
}
}
}`,
},
});
const node = response.data.data.node;
ctx.state.data = {
title: `Matters | ${node.content}`,
link: `${host}/tags/${tid}`,
description: node.content,
item: node.articles.edges.map(({ node: article }) => ({
title: article.title,
author: article.author.displayName,
description: article.content,
link: `${host}/@${article.author.id}/${article.slug}-${article.mediaHash}`,
pubDate: parseDate(article.createdAt),
})),
};
};
+63
View File
@@ -0,0 +1,63 @@
import { Route } from '@/types';
import ofetch from '@/utils/ofetch';
import { baseUrl, gqlEndpoint, parseItem } from './utils';
const handler = async (ctx) => {
const { uid } = ctx.req.param();
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 20;
const response = await ofetch(gqlEndpoint, {
method: 'POST',
body: {
query: `{
user(input: {userName: "${uid}"}) {
displayName
avatar
info {
description
}
articles(input: {first: ${limit}}) {
edges {
node {
shortHash
title
content
createdAt
author {
displayName
}
tags {
content
}
}
}
}
}
}`,
},
});
const user = response.data.user;
return {
title: `Matters | ${user.displayName}`,
link: `${baseUrl}/@${uid}`,
description: user.info.description,
image: user.avatar,
item: user.articles.edges.map(({ node }) => parseItem(node)),
};
};
export const route: Route = {
path: '/author/:uid',
name: 'Author',
example: '/matters/author/robertu',
parameters: { uid: "Author id, can be found at author's homepage url" },
maintainers: ['Cerebrater', 'xosdy'],
handler,
radar: [
{
source: ['matters.town/:uid'],
target: (params) => `/matters/author/${params.uid.slice(1)}`,
},
],
};
+74
View File
@@ -0,0 +1,74 @@
import { Route } from '@/types';
import ofetch from '@/utils/ofetch';
import { baseUrl, gqlEndpoint, parseItem } from './utils';
const handler = async (ctx) => {
const { type = 'latest' } = ctx.req.param();
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 20;
const options = {
latest: {
title: '最新',
apiType: 'newest',
},
heat: {
title: '熱議',
apiType: 'hottest',
},
essence: {
title: '精華',
apiType: 'icymi',
},
};
const response = await ofetch(gqlEndpoint, {
method: 'POST',
body: {
query: `{
viewer {
recommendation {
feed: ${options[type].apiType}(input: {first: ${limit}}) {
edges {
node {
shortHash
title
content
createdAt
author {
displayName
}
tags {
content
}
}
}
}
}
}
}`,
},
});
const item = response.data.viewer.recommendation.feed.edges.map(({ node }) => parseItem(node));
return {
title: `Matters | ${options[type].title}`,
link: baseUrl,
item,
};
};
export const route: Route = {
path: '/latest/:type?',
name: 'Latest, heat, essence',
example: '/matters/latest/heat',
parameters: { uid: 'Defaults to latest, see table below' },
maintainers: ['xyqfer', 'Cerebrater', 'xosdy'],
handler,
radar: [
{
source: ['matters.town'],
},
],
description: `| 最新 | 热门 | 精华 |
| ------ | ---- | ------- |
| latest | heat | essence |`,
};
+7
View File
@@ -0,0 +1,7 @@
import type { Namespace } from '@/types';
export const namespace: Namespace = {
name: 'Matters',
url: 'matters.town',
categories: ['new-media'],
};
+85
View File
@@ -0,0 +1,85 @@
import { Route } from '@/types';
import ofetch from '@/utils/ofetch';
import * as cheerio from 'cheerio';
import cache from '@/utils/cache';
import { baseUrl, gqlEndpoint, parseItem } from './utils';
interface Tag {
type: string;
generated: boolean;
id: string;
typename: string;
}
const getTagId = (tid: string) =>
cache.tryGet(`matters:tags:${tid}`, async () => {
const response = await ofetch(`${baseUrl}/tags/${tid}`);
const $ = cheerio.load(response);
const nextData = JSON.parse($('script#__NEXT_DATA__').text());
const node = Object.entries(nextData.props.apolloState.data.ROOT_QUERY)
.find(([key]) => key.startsWith('node'))
?.pop() as Tag;
return node?.id.split(':')[1];
});
const handler = async (ctx) => {
const { tid } = ctx.req.param();
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 20;
const tagId = await getTagId(tid);
const gqlResponse = await ofetch(gqlEndpoint, {
method: 'POST',
body: {
query: `{
node(input: {id: "${tagId}"}) {
... on Tag {
content
description
articles(input: {first: ${limit}}) {
edges {
node {
title
shortHash
content
createdAt
author {
displayName
}
tags {
content
}
}
}
}
}
}
}`,
},
});
const node = gqlResponse.data.node;
return {
title: `Matters | ${node.content}`,
link: `${baseUrl}/tags/${tid}`,
description: node.description,
item: node.articles.edges.map(({ node }) => parseItem(node)),
};
};
export const route: Route = {
path: '/tags/:tid',
name: 'Tags',
example: '/matters/tags/972-哲學',
parameters: { tid: 'Tag id, can be found in the url of the tag page' },
maintainers: ['Cerebrater'],
handler,
radar: [
{
source: ['matters.town/tags/:tid'],
},
],
};
+30
View File
@@ -0,0 +1,30 @@
import { parseDate } from '@/utils/parse-date';
export const baseUrl = 'https://matters.town';
export const gqlEndpoint = 'https://server.matters.town/graphql';
interface Tag {
content: string;
}
interface Author {
displayName: string;
}
interface Article {
shortHash: string;
title: string;
content: string;
createdAt: string;
author: Author;
tags: Tag[];
}
export const parseItem = (node: Article) => ({
title: node.title,
description: node.content,
link: `${baseUrl}/a/${node.shortHash}`,
author: node.author.displayName,
pubDate: parseDate(node.createdAt),
category: node.tags.map((tag) => tag.content),
});