diff --git a/lib/routes/taptap/changelog-cn.ts b/lib/routes/taptap/changelog-cn.ts
new file mode 100644
index 00000000000000..52c9574b0347b5
--- /dev/null
+++ b/lib/routes/taptap/changelog-cn.ts
@@ -0,0 +1,29 @@
+import { Route } from '@/types';
+import { handler } from './common/changelog';
+
+export const route: Route = {
+ path: '/changelog/:id/:lang?',
+ categories: ['game'],
+ example: '/taptap/changelog/60809/en_US',
+ parameters: {
+ id: '游戏 ID,游戏主页 URL 中获取',
+ lang: '语言,默认使用 `zh_CN`,亦可使用 `en_US`',
+ },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: [
+ {
+ source: ['www.taptap.cn/app/:id'],
+ target: '/changelog/:id',
+ },
+ ],
+ name: '游戏更新',
+ maintainers: ['hoilc', 'ETiV'],
+ handler,
+};
diff --git a/lib/routes/taptap/changelog-intl.ts b/lib/routes/taptap/changelog-intl.ts
new file mode 100644
index 00000000000000..e16f5fefa89b90
--- /dev/null
+++ b/lib/routes/taptap/changelog-intl.ts
@@ -0,0 +1,34 @@
+import { Route } from '@/types';
+import { handler } from './common/changelog';
+
+export const route: Route = {
+ path: '/intl/changelog/:id/:lang?',
+ categories: ['game'],
+ example: '/taptap/intl/changelog/191001/zh_TW',
+ parameters: {
+ id: "Game's App ID, you may find it from the URL of the Game",
+ lang: 'Language, checkout the table below for possible values, default is `en_US`',
+ },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: [
+ {
+ source: ['www.taptap.io/app/:id'],
+ target: '/intl/changelog/:id',
+ },
+ ],
+ name: "Game's Changelog",
+ maintainers: ['hoilc', 'ETiV'],
+ handler,
+ description: `Language Code
+
+ | English (US) | 繁體中文 | 한국어 | 日本語 |
+ | ------------ | -------- | ------ | ------ |
+ | en_US | zh_TW | ko_KR | ja_JP |`,
+};
diff --git a/lib/routes/taptap/changelog.ts b/lib/routes/taptap/changelog.ts
deleted file mode 100644
index 7f7d124da3c5f1..00000000000000
--- a/lib/routes/taptap/changelog.ts
+++ /dev/null
@@ -1,71 +0,0 @@
-import { Route } from '@/types';
-import got from '@/utils/got';
-import { parseDate } from '@/utils/parse-date';
-import { getRootUrl, appDetail, X_UA } from './utils';
-
-export const route: Route = {
- path: ['/changelog/:id/:lang?', '/intl/changelog/:id/:lang?'],
- categories: ['game'],
- example: '/taptap/changelog/60809/en_US',
- parameters: { id: '游戏 ID,游戏主页 URL 中获取', lang: '语言,默认使用 `zh_CN`,亦可使用 `en_US`' },
- features: {
- requireConfig: false,
- requirePuppeteer: false,
- antiCrawler: false,
- supportBT: false,
- supportPodcast: false,
- supportScihub: false,
- },
- radar: [
- {
- source: ['taptap.com/app/:id'],
- target: '/changelog/:id',
- },
- ],
- name: '游戏更新',
- maintainers: ['hoilc', 'ETiV'],
- handler,
- description: `#### 语言代码
-
- | English (US) | 繁體中文 | 한국어 | 日本語 |
- | ------------ | -------- | ------ | ------ |
- | en\_US | zh\_TW | ko\_KR | ja\_JP |`,
-};
-
-async function handler(ctx) {
- const is_intl = ctx.req.url.indexOf('/intl/') === 0;
- const id = ctx.req.param('id');
- const lang = ctx.req.param('lang') ?? (is_intl ? 'en_US' : 'zh_CN');
-
- const url = `${getRootUrl(is_intl)}/app/${id}`;
-
- const app_detail = await appDetail(id, lang, is_intl);
-
- const app_img = app_detail.app.icon.original_url;
- const app_name = app_detail.app.title;
- const app_description = `${app_name} by ${app_detail.app.developers.map((item) => item.name).join(' & ')}`;
-
- const response = await got({
- method: 'get',
- url: `${getRootUrl(is_intl)}/webapiv2/apk/v1/list-by-app?app_id=${id}&from=0&limit=10&${X_UA(lang)}`,
- headers: {
- Referer: url,
- },
- });
-
- const list = response.data.data.list;
-
- return {
- title: `TapTap 更新记录 ${app_name}`,
- description: app_description,
- link: url,
- image: app_img,
- item: list.map((item) => ({
- title: `${app_name} / ${item.version_label}`,
- description: item.whatsnew.text,
- pubDate: parseDate(item.update_date * 1000),
- link: url,
- guid: item.version_label,
- })),
- };
-}
diff --git a/lib/routes/taptap/common/changelog.ts b/lib/routes/taptap/common/changelog.ts
new file mode 100644
index 00000000000000..fd320e3a0a35bc
--- /dev/null
+++ b/lib/routes/taptap/common/changelog.ts
@@ -0,0 +1,40 @@
+import ofetch from '@/utils/ofetch';
+import { parseDate } from '@/utils/parse-date';
+import { getRootUrl, appDetail, X_UA } from '../utils';
+
+export async function handler(ctx) {
+ const requestPath = ctx.req.path.replace('/taptap', '');
+ const isIntl = requestPath.startsWith('/intl/');
+ const id = ctx.req.param('id');
+ const lang = ctx.req.param('lang') ?? (isIntl ? 'en_US' : 'zh_CN');
+
+ const url = `${getRootUrl(isIntl)}/app/${id}`;
+
+ const detail = await appDetail(id, lang, isIntl);
+
+ const appImg = detail.app.icon.original_url;
+ const appName = detail.app.title;
+ const appDescription = `${appName}${detail.app.developers ? ' by' + detail.app.developers.map((item) => item.name).join(' & ') : ''}`;
+
+ const response = await ofetch(`${getRootUrl(isIntl)}/webapiv2/apk/v1/list-by-app?app_id=${id}&from=0&limit=10&${X_UA(lang)}`, {
+ headers: {
+ Referer: url,
+ },
+ });
+
+ const list = response.data.list;
+
+ return {
+ title: `TapTap 更新记录 ${appName}`,
+ description: appDescription,
+ link: url,
+ image: appImg,
+ item: list.map((item) => ({
+ title: `${appName} / ${item.version_label}`,
+ description: item.whatsnew.text,
+ pubDate: parseDate(item.update_date, 'X'),
+ link: url,
+ guid: item.version_label,
+ })),
+ };
+}
diff --git a/lib/routes/taptap/common/review.ts b/lib/routes/taptap/common/review.ts
new file mode 100644
index 00000000000000..80bb0602e6e994
--- /dev/null
+++ b/lib/routes/taptap/common/review.ts
@@ -0,0 +1,126 @@
+import ofetch from '@/utils/ofetch';
+import { parseDate } from '@/utils/parse-date';
+import { getRootUrl, appDetail, X_UA } from '../utils';
+
+/*
+const sortMap = {
+ default: {
+ en_US: 'Default',
+ zh_CN: '预设',
+ zh_TW: '預設',
+ },
+ recent: {
+ en_US: 'Latest',
+ zh_CN: '最新',
+ zh_TW: '最新',
+ },
+ hot: {
+ en_US: 'Popular',
+ zh_CN: '热门',
+ zh_TW: '熱門',
+ },
+ spent: {
+ en_US: 'Play Time',
+ zh_CN: '游戏时长',
+ zh_TW: '遊戲時長',
+ },
+};
+
+const intlSortMap = {
+ helpful: {
+ en_US: 'Most Helpful',
+ zh_TW: '最有幫助',
+ ja_JP: '最も役立つ',
+ ko_KR: '가장 도움이 된',
+ },
+ recent: {
+ en_US: 'Most Recent',
+ zh_TW: '最新',
+ ja_JP: '最も最近',
+ ko_KR: '최근순',
+ },
+};
+*/
+
+const makeSortParam = (isIntl: boolean, order: string) => {
+ if (isIntl) {
+ if (order === 'helpful' || order === 'recent') {
+ return `type=${order}`;
+ }
+ return 'type=helpful';
+ } else {
+ if (order === 'new' || order === 'hot') {
+ return `sort=${order}`;
+ }
+ return 'sort=hot';
+ }
+};
+
+const fetchMainlandItems = async (params) => {
+ const id = params.id;
+ const order = params.order ?? 'hot';
+ const lang = params.lang ?? 'zh_CN';
+
+ let url = `${getRootUrl(false)}/webapiv2/review/v2/list-by-app?app_id=${id}&limit=10`;
+ url += `&${makeSortParam(false, order)}`;
+ url += `&${X_UA(lang)}`;
+
+ const reviewListResponse = await ofetch(url);
+
+ return reviewListResponse.data.list.map((review) => {
+ const author = review.moment.author.user.name;
+ const score = review.moment.review.score;
+ return {
+ title: `${author} - ${score}星`,
+ author,
+ description: review.moment.review.contents.text + (review.moment.review.contents.images ? review.moment.review.contents.images.map((img) => ``).join('') : ''),
+ link: `${getRootUrl(false)}/review/${review.moment.review.id}`,
+ pubDate: parseDate(review.moment.publish_time, 'X'),
+ };
+ });
+};
+
+const fetchIntlItems = async (params) => {
+ const id = params.id;
+ const order = params.order ?? 'helpful';
+ const lang = params.lang ?? 'en_US';
+
+ let url = `${getRootUrl(true)}/webapiv2/feeds/v3/by-app?app_id=${id}&limit=10`;
+ url += `&${makeSortParam(true, order)}`;
+ url += `&${X_UA(lang)}`;
+
+ const reviewListResponse = await ofetch(url);
+
+ return reviewListResponse.data.list.map((review) => {
+ const author = review.post.user.name;
+ const score = review.post.list_fields.app_ratings[id].score;
+ return {
+ title: `${author} - ${'★'.repeat(score)}`,
+ author,
+ description: review.post.list_fields.summary || review.post.list_fields.title,
+ link: `${getRootUrl(true)}/post/${review.post.id_str}`,
+ pubDate: parseDate(review.post.published_time, 'X'),
+ };
+ });
+};
+
+export async function handler(ctx) {
+ const requestPath = ctx.req.path.replace('/taptap', '');
+ const isIntl = requestPath.startsWith('/intl/');
+ const id = ctx.req.param('id');
+ const order = ctx.req.param('order') ?? 'default';
+ const lang = ctx.req.param('lang') ?? (isIntl ? 'en_US' : 'zh_CN');
+
+ const detail = await appDetail(id, lang, isIntl);
+ const appImg = detail.app.icon.original_url;
+ const appName = detail.app.title;
+
+ const items = isIntl ? await fetchIntlItems({ id, order, lang }) : await fetchMainlandItems({ id, order, lang });
+
+ return {
+ title: `TapTap 评价 ${appName}`,
+ link: `${getRootUrl(isIntl)}/app/${id}/review?${makeSortParam(isIntl, order)}`,
+ image: appImg,
+ item: items,
+ };
+}
diff --git a/lib/routes/taptap/namespace.ts b/lib/routes/taptap/namespace.ts
index c13466ece33949..6af928b840b5fa 100644
--- a/lib/routes/taptap/namespace.ts
+++ b/lib/routes/taptap/namespace.ts
@@ -1,8 +1,8 @@
import type { Namespace } from '@/types';
export const namespace: Namespace = {
- name: 'TapTap 中国',
- url: 'taptap.com',
+ name: 'TapTap',
+ url: 'www.taptap.io',
description: `:::warning
由于区域限制,需要在有国内 IP 的机器上自建才能正常获取 RSS。\
而对于《TapTap 国际版》则需要部署在具有海外出口的 IP 上才可正常获取 RSS。
diff --git a/lib/routes/taptap/review-cn.ts b/lib/routes/taptap/review-cn.ts
new file mode 100644
index 00000000000000..dc38b771a21d1c
--- /dev/null
+++ b/lib/routes/taptap/review-cn.ts
@@ -0,0 +1,33 @@
+import { Route } from '@/types';
+import { handler } from './common/review';
+
+export const route: Route = {
+ path: '/review/:id/:order?/:lang?',
+ categories: ['game'],
+ example: '/taptap/review/142793/hot',
+ parameters: {
+ id: '游戏 ID,游戏主页 URL 中获取',
+ order: '排序方式,空为综合,可选如下',
+ lang: '语言,`zh-CN` 或 `zh-TW`,默认为 `zh-CN`',
+ },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: [
+ {
+ source: ['www.taptap.cn/app/:id/review', 'www.taptap.cn/app/:id'],
+ target: '/review/:id',
+ },
+ ],
+ name: '游戏评价',
+ maintainers: ['hoilc', 'TonyRL'],
+ handler,
+ description: `| 最新 | 综合 |
+| --- | --- |
+| new | hot |`,
+};
diff --git a/lib/routes/taptap/review-intl.ts b/lib/routes/taptap/review-intl.ts
new file mode 100644
index 00000000000000..88936dec4a63a1
--- /dev/null
+++ b/lib/routes/taptap/review-intl.ts
@@ -0,0 +1,41 @@
+import { Route } from '@/types';
+import { handler } from './common/review';
+
+export const route: Route = {
+ path: '/intl/review/:id/:order?/:lang?',
+ categories: ['game'],
+ example: '/taptap/intl/review/82354/recent',
+ parameters: {
+ id: "Game's App ID, you may find it from the URL of the Game",
+ order: 'Sort Method, default is `helpful`, checkout the table below for possible values',
+ lang: 'Language, checkout the table below for possible values, default is `en_US`',
+ },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: [
+ {
+ source: ['www.taptap.io/app/:id/review', 'www.taptap.io/app/:id'],
+ target: '/intl/review/:id',
+ },
+ ],
+ name: 'Ratings & Reviews',
+ maintainers: ['hoilc', 'TonyRL', 'ETiV'],
+ handler,
+ description: `Sort Method
+
+| Most Helpful | Most Recent |
+| ------------ | ----------- |
+| helpful | recent |
+
+Language Code
+
+| English (US) | 繁體中文 | 한국어 | 日本語 |
+| ------------ | -------- | ------ | ------ |
+| en_US | zh_TW | ko_KR | ja_JP |`,
+};
diff --git a/lib/routes/taptap/review.ts b/lib/routes/taptap/review.ts
deleted file mode 100644
index ac3568e4ba6589..00000000000000
--- a/lib/routes/taptap/review.ts
+++ /dev/null
@@ -1,166 +0,0 @@
-import { Route } from '@/types';
-import got from '@/utils/got';
-import { parseDate } from '@/utils/parse-date';
-import { getRootUrl, appDetail, X_UA } from './utils';
-
-const sortMap = {
- default: {
- en_US: 'Default',
- zh_CN: '预设',
- zh_TW: '預設',
- },
- new: {
- en_US: 'Latest',
- zh_CN: '最新',
- zh_TW: '最新',
- },
- hot: {
- en_US: 'Popular',
- zh_CN: '热门',
- zh_TW: '熱門',
- },
- spent: {
- en_US: 'Play Time',
- zh_CN: '游戏时长',
- zh_TW: '遊戲時長',
- },
-};
-
-const intlSortMap = {
- default: {
- en_US: 'Most Relevant',
- zh_TW: '最相關',
- ja_JP: '関連性が高い',
- ko_KR: '관련도순',
- },
- new: {
- en_US: 'Most Recent',
- zh_TW: '最新',
- ja_JP: '最も最近',
- ko_KR: '최근순',
- },
-};
-
-const makeSortParam = (isIntl, order) => {
- if (isIntl) {
- if (order === 'new') {
- return `sort=${order}`;
- }
- } else {
- if (order === 'new' || order === 'hot' || order === 'spent') {
- return `sort=${order}`;
- }
- }
- return '';
-};
-
-const fetchMainlandItems = async (params) => {
- const id = params.id;
- const order = params.order ?? 'default';
- const lang = params.lang ?? 'zh_CN';
-
- let url = `${getRootUrl(false)}/webapiv2/review/v2/by-app?app_id=${id}&limit=10`;
- url += `&${makeSortParam(false, order)}`;
- url += `&${X_UA(lang)}`;
-
- const reviews_list_response = await got(url);
- const reviews_list = reviews_list_response.data.data.list;
-
- return reviews_list.map((review) => {
- const author = review.moment.author.user.name;
- const score = review.moment.extended_entities.reviews[0].score;
- return {
- title: `${author} - ${score}星`,
- author,
- description: review.moment.extended_entities.reviews[0].contents.text,
- link: `${getRootUrl(false)}/review/${review.moment.extended_entities.reviews[0].id}`,
- pubDate: parseDate(review.moment.extended_entities.reviews[0].created_time * 1000),
- };
- });
-};
-
-const fetchIntlItems = async (params) => {
- const id = params.id;
- const order = params.order ?? 'default';
- const lang = params.lang ?? 'en_US';
-
- let url = `${getRootUrl(true)}/webapiv2/feeds/v1/app-ratings?app_id=${id}&limit=10`;
- url += `&${makeSortParam(true, order)}`;
- url += `&${X_UA(lang)}`;
-
- const reviews_list_response = await got(url);
- const reviews_list = reviews_list_response.data.data.list;
-
- return reviews_list.map((review) => {
- const author = review.post.user.name;
- const score = review.post.list_fields.app_ratings[id].score;
- return {
- title: `${author} - ${score}星`,
- author,
- description: review.post.list_fields.summary || review.post.list_fields.title,
- link: `${getRootUrl(true)}/post/${review.post.id_str}`,
- pubDate: parseDate(review.post.published_time * 1000),
- };
- });
-};
-
-async function handler(ctx) {
- const is_intl = ctx.req.url.indexOf('/intl/') === 0;
- const id = ctx.req.param('id');
- const order = ctx.req.param('order') ?? 'default';
- const lang = ctx.req.param('lang') ?? (is_intl ? 'en_US' : 'zh_CN');
-
- const app_detail = await appDetail(id, lang, is_intl);
- const app_img = app_detail.app.icon.original_url;
- const app_name = app_detail.app.title;
-
- const items = is_intl ? await fetchIntlItems(ctx.params) : await fetchMainlandItems(ctx.params);
-
- const ret = {
- title: `TapTap 评价 ${app_name} - ${(is_intl ? intlSortMap : sortMap)[order][lang]}排序`,
- link: `${getRootUrl(is_intl)}/app/${id}/review?${makeSortParam(is_intl, order)}`,
- image: app_img,
- item: items,
- };
-
- ctx.set('json', ret);
- return ret;
-}
-
-export const route: Route = {
- path: ['/review/:id/:order?/:lang?', '/intl/review/:id/:order?/:lang?'],
- categories: ['game'],
- example: '/taptap/review/142793/hot',
- parameters: { id: '游戏 ID,游戏主页 URL 中获取', order: '排序方式,空为默认排序,可选如下', lang: '语言,`zh-CN`或`zh-TW`,默认为`zh-CN`' },
- features: {
- requireConfig: false,
- requirePuppeteer: false,
- antiCrawler: false,
- supportBT: false,
- supportPodcast: false,
- supportScihub: false,
- },
- radar: [
- {
- source: ['taptap.com/app/:id/review', 'taptap.com/app/:id'],
- target: '/review/:id',
- },
- ],
- name: '游戏评价',
- maintainers: ['hoilc', 'TonyRL'],
- handler,
- description: `#### 排序方式
-
- | 最相关 | 最新 |
- | ------- | ---- |
- | default | new |
-
- #### 语言代码
-
- | English (US) | 繁體中文 | 한국어 | 日本語 |
- | ------------ | -------- | ------ | ------ |
- | en\_US | zh\_TW | ko\_KR | ja\_JP |`,
- description: `| 最新 | 最热 | 游戏时长 | 默认排序 |
- | ------ | ---- | -------- | -------- |
- | update | hot | spent | default |`,
-};
diff --git a/lib/routes/taptap/templates/videoPost.art b/lib/routes/taptap/templates/videoPost.art
index 184bcecb2bebf2..89d092cdf86487 100644
--- a/lib/routes/taptap/templates/videoPost.art
+++ b/lib/routes/taptap/templates/videoPost.art
@@ -1,3 +1,2 @@
-{{ if intro }}{{@ intro }}{{ /if }}
Preview