rsshub 1.0.0-master.f9b85f5 → 1.0.0-master.f9c381a

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/lib/config.ts +10 -0
  2. package/lib/middleware/cache.ts +4 -0
  3. package/lib/middleware/parameter.ts +1 -1
  4. package/lib/registry.ts +6 -2
  5. package/lib/routes/51cto/utils.ts +1 -1
  6. package/lib/routes/abc/index.ts +1 -1
  7. package/lib/routes/acgvinyl/namespace.ts +6 -0
  8. package/lib/routes/acgvinyl/news.ts +86 -0
  9. package/lib/routes/ally/rail.ts +1 -1
  10. package/lib/routes/anthropic/news.ts +13 -11
  11. package/lib/routes/apnews/mobile-api.ts +1 -1
  12. package/lib/routes/apnews/sitemap.ts +1 -1
  13. package/lib/routes/apple/apps.ts +2 -2
  14. package/lib/routes/apple/podcast.ts +58 -25
  15. package/lib/routes/bangumi.tv/group/reply.ts +1 -1
  16. package/lib/routes/bilibili/cache.ts +18 -2
  17. package/lib/routes/bilibili/dynamic.ts +31 -7
  18. package/lib/routes/bilibili/page.ts +1 -1
  19. package/lib/routes/bilibili/ranking.ts +23 -17
  20. package/lib/routes/bilibili/wasm-exec.ts +1 -1
  21. package/lib/routes/bilibili/weekly-recommend.ts +22 -6
  22. package/lib/routes/bjp/apod.ts +1 -1
  23. package/lib/routes/buaa/jiaowu.ts +1 -1
  24. package/lib/routes/bullionvault/gold-news.ts +3 -3
  25. package/lib/routes/cast/index.ts +1 -1
  26. package/lib/routes/coolapk/utils.ts +5 -4
  27. package/lib/routes/coolbuy/index.ts +106 -0
  28. package/lib/routes/{xinhuanet → coolbuy}/namespace.ts +3 -3
  29. package/lib/routes/coolbuy/templates/description.art +48 -0
  30. package/lib/routes/coolidge/film-guide.ts +60 -0
  31. package/lib/routes/coolidge/namespace.ts +7 -0
  32. package/lib/routes/coolidge/news.ts +65 -0
  33. package/lib/routes/coolidge/templates/description.art +4 -0
  34. package/lib/routes/copymanga/comic.ts +1 -1
  35. package/lib/routes/cpta/handler.ts +1 -1
  36. package/lib/routes/creative-comic/book.ts +1 -1
  37. package/lib/routes/daum/potplayer.ts +1 -1
  38. package/lib/routes/dockerhub/utils.ts +1 -1
  39. package/lib/routes/dora-world/article.ts +2 -2
  40. package/lib/routes/ehentai/ehapi.ts +3 -3
  41. package/lib/routes/eventbrite/events.ts +152 -0
  42. package/lib/routes/eventbrite/namespace.ts +7 -0
  43. package/lib/routes/github/activity.ts +1 -1
  44. package/lib/routes/google/jules.ts +63 -0
  45. package/lib/routes/gov/mem/namespace.ts +7 -0
  46. package/lib/routes/gov/mem/zfxxgkpt.ts +96 -0
  47. package/lib/routes/gov/mot/index.ts +158 -53
  48. package/lib/routes/hameln/chapter.ts +1 -1
  49. package/lib/routes/hpoi/banner-item.ts +28 -11
  50. package/lib/routes/hpoi/info.ts +1 -1
  51. package/lib/routes/huggingface/daily-papers.ts +1 -1
  52. package/lib/routes/hupu/index.ts +158 -74
  53. package/lib/routes/hupu/types.ts +163 -0
  54. package/lib/routes/hust/gs.ts +1 -1
  55. package/lib/routes/hust/mse.ts +1 -1
  56. package/lib/routes/huxiu/util.ts +2 -2
  57. package/lib/routes/infoq/presentations.ts +1 -1
  58. package/lib/routes/instagram/common-utils.ts +3 -3
  59. package/lib/routes/itch/devlog.ts +7 -3
  60. package/lib/routes/javbus/index.ts +1 -1
  61. package/lib/routes/javlibrary/utils.ts +1 -1
  62. package/lib/routes/jbma/namespace.ts +17 -0
  63. package/lib/routes/jbma/report.ts +473 -0
  64. package/lib/routes/jetbrains/comments.ts +1 -1
  65. package/lib/routes/jingzhengu/utils.ts +1 -1
  66. package/lib/routes/juejin/aicoding.ts +102 -0
  67. package/lib/routes/juejin/utils.ts +36 -51
  68. package/lib/routes/kakuyomu/works.ts +1 -1
  69. package/lib/routes/kemono/index.ts +2 -2
  70. package/lib/routes/komiic/comic.ts +1 -1
  71. package/lib/routes/koyso/index.ts +338 -0
  72. package/lib/routes/koyso/namespace.ts +9 -0
  73. package/lib/routes/koyso/templates/description.art +13 -0
  74. package/lib/routes/letterboxd/index.ts +65 -0
  75. package/lib/routes/letterboxd/namespace.ts +8 -0
  76. package/lib/routes/maccms/index.ts +1 -1
  77. package/lib/routes/mercari/util.ts +1 -1
  78. package/lib/routes/mingpao/index.ts +1 -1
  79. package/lib/routes/nankai/ai-notice.ts +142 -0
  80. package/lib/routes/nankai/graduate-notice.ts +162 -0
  81. package/lib/routes/natgeo/natgeo.ts +1 -0
  82. package/lib/routes/nhk/news-web-easy.ts +1 -1
  83. package/lib/routes/nicovideo/mylist.ts +39 -0
  84. package/lib/routes/nicovideo/types.ts +27 -0
  85. package/lib/routes/nicovideo/utils.ts +27 -1
  86. package/lib/routes/nikkei/cn/index.ts +1 -4
  87. package/lib/routes/now/news.ts +1 -1
  88. package/lib/routes/nytimes/index.ts +1 -1
  89. package/lib/routes/pixiv/novel-api/user-novels/sfw.ts +1 -1
  90. package/lib/routes/pixivision/utils.ts +1 -1
  91. package/lib/routes/pku/hr.ts +1 -1
  92. package/lib/routes/pku/scc/recruit.ts +1 -1
  93. package/lib/routes/producthunt/templates/description.art +2 -2
  94. package/lib/routes/producthunt/today.ts +17 -8
  95. package/lib/routes/ps/trophy.ts +1 -1
  96. package/lib/routes/pubscholar/utils.ts +14 -1
  97. package/lib/routes/qweather/3days.ts +14 -14
  98. package/lib/routes/qweather/now.ts +12 -10
  99. package/lib/routes/qweather/util.tsx +89 -0
  100. package/lib/routes/qwenlm/blog.ts +75 -0
  101. package/lib/routes/qwenlm/namespace.ts +6 -0
  102. package/lib/routes/radio-canada/latest.ts +30 -17
  103. package/lib/routes/ruc/ai.ts +1 -1
  104. package/lib/routes/ruc/hr.ts +1 -1
  105. package/lib/routes/samrdprc/index.ts +241 -0
  106. package/lib/routes/samrdprc/namespace.ts +1 -1
  107. package/lib/routes/sdo/ff14risingstones/api.ts +78 -0
  108. package/lib/routes/sdo/ff14risingstones/constant.ts +338 -0
  109. package/lib/routes/sdo/ff14risingstones/posts.ts +80 -0
  110. package/lib/routes/sdo/ff14risingstones/strats.ts +75 -0
  111. package/lib/routes/sdo/ff14risingstones/templates/duties-party.art +41 -0
  112. package/lib/routes/sdo/ff14risingstones/templates/fc-party.art +26 -0
  113. package/lib/routes/sdo/ff14risingstones/templates/novice-network-party.art +9 -0
  114. package/lib/routes/sdo/ff14risingstones/templates/rp-party.art +15 -0
  115. package/lib/routes/sdo/ff14risingstones/timeline.ts +31 -0
  116. package/lib/routes/sdo/ff14risingstones/types/dynamic.ts +50 -0
  117. package/lib/routes/sdo/ff14risingstones/types/index.ts +3 -0
  118. package/lib/routes/sdo/ff14risingstones/types/other.ts +57 -0
  119. package/lib/routes/sdo/ff14risingstones/types/party.ts +111 -0
  120. package/lib/routes/sdo/ff14risingstones/user-dynamics.ts +32 -0
  121. package/lib/routes/sdo/ff14risingstones/user-posts.ts +32 -0
  122. package/lib/routes/sdo/ff14risingstones/user-resently.ts +38 -0
  123. package/lib/routes/sdo/ff14risingstones/user-strats.ts +32 -0
  124. package/lib/routes/sdo/ff14risingstones/utils.ts +215 -0
  125. package/lib/routes/sdo/namespace.ts +7 -0
  126. package/lib/routes/showstart/utils.ts +1 -1
  127. package/lib/routes/sohu/mp.ts +1 -1
  128. package/lib/routes/sotwe/user.ts +1 -1
  129. package/lib/routes/surfshark/blog.ts +273 -77
  130. package/lib/routes/surfshark/templates/description.art +16 -4
  131. package/lib/routes/sustainabilitymag/articles.ts +1 -1
  132. package/lib/routes/syosetu/dev.ts +1 -1
  133. package/lib/routes/syosetu/ranking-isekai.ts +1 -1
  134. package/lib/routes/szse/disclosure/listed-notice.ts +44 -6
  135. package/lib/routes/telegram/channel-media.ts +249 -0
  136. package/lib/routes/telegram/channel.ts +5 -4
  137. package/lib/routes/telegram/stories.ts +130 -0
  138. package/lib/routes/telegram/tglib/channel.ts +136 -118
  139. package/lib/routes/telegram/tglib/client.ts +37 -139
  140. package/lib/routes/tesla/cx.ts +1 -1
  141. package/lib/routes/theverge/index.ts +20 -6
  142. package/lib/routes/threads/utils.ts +7 -3
  143. package/lib/routes/tidb/blog.ts +1 -1
  144. package/lib/routes/toutiao/user.ts +2 -2
  145. package/lib/routes/twitter/api/mobile-api/api.ts +1 -1
  146. package/lib/routes/txrjy/fornumtopic.ts +2 -2
  147. package/lib/routes/typst/universe.ts +1 -1
  148. package/lib/routes/uber/blog.ts +87 -46
  149. package/lib/routes/weibo/utils.ts +17 -9
  150. package/lib/routes/xiaohongshu/user.ts +1 -1
  151. package/lib/routes/xjtu/ee-jzxx.ts +1 -1
  152. package/lib/routes/yahoo/news/utils.ts +1 -1
  153. package/lib/routes/ymgal/article.ts +1 -1
  154. package/lib/routes/yoasobi-music/media.ts +1 -1
  155. package/lib/routes/youtube/api/youtubei.ts +1 -1
  156. package/lib/routes/youtube/community.ts +4 -4
  157. package/lib/routes/zaker/utils.ts +1 -1
  158. package/lib/routes/zaobao/util.tsx +1 -1
  159. package/lib/server.ts +1 -1
  160. package/lib/types.ts +1 -1
  161. package/lib/utils/puppeteer-utils.test.ts +2 -2
  162. package/lib/views/index.tsx +4 -4
  163. package/package.json +40 -40
  164. package/lib/routes/qweather/templates/3days.art +0 -22
  165. package/lib/routes/qweather/templates/now.art +0 -16
  166. package/lib/routes/xinhuanet/app.ts +0 -109
@@ -0,0 +1,50 @@
1
+ import type { DynamicSource } from '../constant';
2
+ import type { DateTimeFormat, UserPost } from './other';
3
+ import type { DutiesParty, NoviceNetworkParty, FreeCompanyParty, OtherParty, RolePlayParty } from './party';
4
+
5
+ interface BaseUserDynamic {
6
+ character_name: string;
7
+ area_name: string;
8
+ group_name: string;
9
+ created_at: DateTimeFormat;
10
+ from: DynamicSource;
11
+ from_id: string;
12
+ id: number;
13
+ mask_content: string;
14
+ }
15
+
16
+ export interface GeneralDynamic extends BaseUserDynamic {
17
+ from: DynamicSource.General;
18
+ }
19
+
20
+ export interface PostDynamic extends BaseUserDynamic {
21
+ from: DynamicSource.Post | DynamicSource.Strat;
22
+ from_info?: UserPost;
23
+ }
24
+
25
+ export interface NoviceNetworkRecruitDynamic extends BaseUserDynamic {
26
+ from: DynamicSource.NoviceNetwork;
27
+ from_info?: Omit<NoviceNetworkParty, 'styleInfo' | 'weekday_time' | 'weekend_time'>;
28
+ }
29
+
30
+ export interface DutiesRecruitDynamic extends BaseUserDynamic {
31
+ from: DynamicSource.Duty;
32
+ from_info?: DutiesParty;
33
+ }
34
+
35
+ export interface FreeCompanyRecruitDynamic extends BaseUserDynamic {
36
+ from: DynamicSource.FreeCompany;
37
+ from_info?: FreeCompanyParty;
38
+ }
39
+
40
+ export interface RolePlayRecruitDynamic extends BaseUserDynamic {
41
+ from: DynamicSource.RolePlay;
42
+ from_info?: RolePlayParty;
43
+ }
44
+
45
+ export interface OtherRecruitDynamic extends BaseUserDynamic {
46
+ from: DynamicSource.Other;
47
+ from_info?: OtherParty;
48
+ }
49
+
50
+ export type UserDynamic = GeneralDynamic | PostDynamic | NoviceNetworkRecruitDynamic | FreeCompanyRecruitDynamic | RolePlayRecruitDynamic | DutiesRecruitDynamic | OtherRecruitDynamic;
@@ -0,0 +1,3 @@
1
+ export type * from './dynamic';
2
+ export type * from './other';
3
+ export type * from './party';
@@ -0,0 +1,57 @@
1
+ export type TimeStamp = `${number}`;
2
+
3
+ export type DateFormat = `${number}-${number}-${number}`;
4
+
5
+ export type DateTimeFormat = `${number}-${number}-${number} ${number}:${number}:${number}`;
6
+
7
+ export type BaseResponse<T = any> = {
8
+ code: number;
9
+ data: T;
10
+ msg: string;
11
+ };
12
+
13
+ export interface Resently {
14
+ detail: string;
15
+ event_type: string;
16
+ event_type_id: string;
17
+ log_time: DateTimeFormat;
18
+ part_date: DateFormat;
19
+ }
20
+
21
+ export interface UserInfo {
22
+ avatar: string;
23
+ character_name: string;
24
+ group_name: string;
25
+ }
26
+
27
+ export interface UserPost {
28
+ created_at: DateTimeFormat;
29
+ posts_id: string;
30
+ part_name: string;
31
+ title: string;
32
+ character_name: string;
33
+ group_name: string;
34
+ area_name: string;
35
+ }
36
+
37
+ export interface PostDetail {
38
+ contentInfo: {
39
+ content: string;
40
+ created_at: DateTimeFormat;
41
+ id: string;
42
+ posts_id: string;
43
+ };
44
+ created_at: DateTimeFormat;
45
+ updated_at: DateTimeFormat;
46
+ }
47
+
48
+ export interface TeamPosition {
49
+ D1: string;
50
+ D2: string;
51
+ D3: string;
52
+ D4: string;
53
+ H1: string;
54
+ H2: string;
55
+ MT: string;
56
+ ST: string;
57
+ }
@@ -0,0 +1,111 @@
1
+ import type { NoviceNetworkIdentity } from '../constant';
2
+ import type { DateFormat, DateTimeFormat, TeamPosition, TimeStamp } from './other';
3
+
4
+ export interface BaseParty {
5
+ area_name: string;
6
+ begin_time: TimeStamp;
7
+ character_name: string;
8
+ end_time: TimeStamp;
9
+ group_name: string;
10
+ id: number;
11
+ status: number; // 1: 招募中, !1: 已结束
12
+ target_area_name: string;
13
+ target_group_name: string | null;
14
+ }
15
+
16
+ // 副本招募
17
+ export interface DutiesParty extends BaseParty {
18
+ D1: number;
19
+ D2: number;
20
+ D3: number;
21
+ D4: number;
22
+ H: number;
23
+ H1: number;
24
+ H2: number;
25
+ MT: number;
26
+ ST: number;
27
+ T: number;
28
+ fb_name: string;
29
+ fb_time: string;
30
+ fb_type: string;
31
+ labelInfo: {
32
+ name: string;
33
+ }[];
34
+ progress: string;
35
+ strategy: string;
36
+ team_composition: string;
37
+ team_position: {
38
+ A: TeamPosition;
39
+ B: TeamPosition;
40
+ C: TeamPosition;
41
+ } | null;
42
+ }
43
+
44
+ export interface DutiesPartyDetail extends DutiesParty {
45
+ team_detail_mask: string | null;
46
+ recruit_require_mask: string | null;
47
+ strategy_desc_mask: string | null;
48
+ updated_at: DateTimeFormat | null;
49
+ need_job: string[];
50
+ }
51
+
52
+ // 导芽招募
53
+ export interface NoviceNetworkParty extends BaseParty {
54
+ identity: NoviceNetworkIdentity; // 1: 导师, 2: 豆芽
55
+ title: string;
56
+ weekday_time: string;
57
+ weekend_time: string;
58
+ style: string[];
59
+ styleInfo: { style: string; pic_url: string }[];
60
+ detail_mask: string;
61
+ }
62
+
63
+ // 角色扮演招募
64
+ export interface RolePlayParty extends BaseParty {
65
+ address: string;
66
+ custom_label: string;
67
+ detail_mask: string;
68
+ open_time: string;
69
+ profile: string;
70
+ rp_area_name: string;
71
+ rp_group_name: string;
72
+ rp_name: string;
73
+ rp_type: ('0' | '1' | '2' | '3')[]; // 0: 无 RP 元素, 1: 轻 RP 元素, 2: 中 RP 元素, 3: 重 RP 元素
74
+ create_time: DateFormat;
75
+ cover_pic: string;
76
+ }
77
+
78
+ export interface RolePlayPartyDetail extends RolePlayParty {
79
+ updated_at: DateTimeFormat | null;
80
+ }
81
+
82
+ // 部队招募
83
+ export interface FreeCompanyParty extends BaseParty {
84
+ created_at: TimeStamp;
85
+ guild_id: string;
86
+ guild_name: string;
87
+ guild_tag: string;
88
+ labelInfo: {
89
+ name: string;
90
+ }[];
91
+ detail_mask: string;
92
+ active_member_num: number;
93
+ target_recruit_num: number;
94
+ cover_pic: string;
95
+ weekday_time: string;
96
+ weekend_time: string;
97
+ }
98
+
99
+ export interface FreeCompanyPartyDetail extends FreeCompanyParty {
100
+ updated_at: DateTimeFormat | null;
101
+ create_time: DateTimeFormat | null;
102
+ guild_address: string | null;
103
+ foot_pic: string | null;
104
+ }
105
+
106
+ // 其它招募
107
+ export interface OtherParty extends BaseParty {
108
+ category_name: string;
109
+ title: string;
110
+ detail_mask: string;
111
+ }
@@ -0,0 +1,32 @@
1
+ import type { Data, Route } from '@/types';
2
+ import { INDEX_URL, REQUIRE_CONFIG } from './constant';
3
+ import type { Context } from 'hono';
4
+ import { checkConfig, generateDynamicFeeds } from './utils';
5
+ import { getUserDynamic, getUserInfo } from './api';
6
+
7
+ export const route: Route = {
8
+ path: '/ff14risingstones/user-dynamics/:uid',
9
+ example: '/sdo/ff14risingstones/user-dynamics/10001226',
10
+ name: '用户动态',
11
+ categories: ['bbs'],
12
+ maintainers: ['KarasuShin'],
13
+ features: {
14
+ requireConfig: REQUIRE_CONFIG,
15
+ },
16
+ handler,
17
+ };
18
+
19
+ async function handler(ctx: Context) {
20
+ checkConfig();
21
+
22
+ const uid = ctx.req.param('uid');
23
+
24
+ const [dynamics, userInfo] = await Promise.all([getUserDynamic(uid), getUserInfo(uid)]);
25
+
26
+ return {
27
+ title: `石之家 - ${userInfo.character_name}@${userInfo.group_name} 的动态`,
28
+ link: `${INDEX_URL}#/me/dynamics?uuid=${uid}`,
29
+ image: userInfo.avatar,
30
+ item: await generateDynamicFeeds(dynamics),
31
+ } as Data;
32
+ }
@@ -0,0 +1,32 @@
1
+ import type { Data, Route } from '@/types';
2
+ import { INDEX_URL, REQUIRE_CONFIG } from './constant';
3
+ import type { Context } from 'hono';
4
+ import { checkConfig, generatePostFeeds } from './utils';
5
+ import { getUserInfo, getUserPosts } from './api';
6
+
7
+ export const route: Route = {
8
+ path: '/ff14risingstones/user-posts/:uid',
9
+ example: '/sdo/ff14risingstones/user-posts/10001226',
10
+ name: '用户发帖',
11
+ categories: ['bbs'],
12
+ maintainers: ['KarasuShin'],
13
+ features: {
14
+ requireConfig: REQUIRE_CONFIG,
15
+ },
16
+ handler,
17
+ };
18
+
19
+ async function handler(ctx: Context) {
20
+ checkConfig();
21
+
22
+ const uid = ctx.req.param('uid');
23
+
24
+ const [posts, userInfo] = await Promise.all([getUserPosts(uid, 1), getUserInfo(uid)]);
25
+
26
+ return {
27
+ title: `石之家 - ${userInfo.character_name}@${userInfo.group_name} 发布的帖子`,
28
+ link: `${INDEX_URL}#/me/posts?uuid=${uid}`,
29
+ image: userInfo.avatar,
30
+ item: await generatePostFeeds(posts),
31
+ } as Data;
32
+ }
@@ -0,0 +1,38 @@
1
+ import type { Data, Route } from '@/types';
2
+ import { INDEX_URL, REQUIRE_CONFIG } from './constant';
3
+ import type { Context } from 'hono';
4
+ import { checkConfig } from './utils';
5
+ import { getResently, getUserInfo } from './api';
6
+ import { parseDate } from '@/utils/parse-date';
7
+ import timezone from '@/utils/timezone';
8
+
9
+ export const route: Route = {
10
+ path: '/ff14risingstones/user-resently/:uid',
11
+ categories: ['bbs'],
12
+ example: '/sdo/ff14risingstones/user-resently/10008214',
13
+ name: '游戏近况',
14
+ maintainers: ['KarasuShin'],
15
+ features: {
16
+ requireConfig: REQUIRE_CONFIG,
17
+ },
18
+ handler,
19
+ };
20
+
21
+ async function handler(ctx: Context) {
22
+ checkConfig();
23
+
24
+ const uid = ctx.req.param('uid');
25
+
26
+ const [resently, userInfo] = await Promise.all([getResently(uid), getUserInfo(uid)]);
27
+
28
+ return {
29
+ title: `石之家 - ${userInfo.character_name}@${userInfo.group_name} 的游戏近况`,
30
+ link: `${INDEX_URL}#/me/info?uuid=${uid}`,
31
+ image: userInfo.avatar,
32
+ item: resently.map((i) => ({
33
+ title: `${i.event_type} - ${i.detail}`,
34
+ pubDate: timezone(parseDate(i.log_time), +8),
35
+ guid: `sdo/ff14risingstones/resently:${uid}-${i.detail}`,
36
+ })),
37
+ } as Data;
38
+ }
@@ -0,0 +1,32 @@
1
+ import type { Data, Route } from '@/types';
2
+ import { INDEX_URL, REQUIRE_CONFIG } from './constant';
3
+ import type { Context } from 'hono';
4
+ import { checkConfig, generatePostFeeds } from './utils';
5
+ import { getUserInfo, getUserPosts } from './api';
6
+
7
+ export const route: Route = {
8
+ path: '/ff14risingstones/user-strats/:uid',
9
+ example: '/sdo/ff14risingstones/user-strats/10001226',
10
+ name: '用户攻略',
11
+ categories: ['bbs'],
12
+ maintainers: ['KarasuShin'],
13
+ features: {
14
+ requireConfig: REQUIRE_CONFIG,
15
+ },
16
+ handler,
17
+ };
18
+
19
+ async function handler(ctx: Context) {
20
+ checkConfig();
21
+
22
+ const uid = ctx.req.param('uid');
23
+
24
+ const [posts, userInfo] = await Promise.all([getUserPosts(uid, 2), getUserInfo(uid)]);
25
+
26
+ return {
27
+ title: `石之家 - ${userInfo.character_name}@${userInfo.group_name} 发布的攻略`,
28
+ link: `${INDEX_URL}#/me/posts?uuid=${uid}`,
29
+ image: userInfo.avatar,
30
+ item: await generatePostFeeds(posts),
31
+ } as Data;
32
+ }
@@ -0,0 +1,215 @@
1
+ import { config } from '@/config';
2
+ import ConfigNotFoundError from '@/errors/types/config-not-found';
3
+ import ofetch from '@/utils/ofetch';
4
+ import type { BaseResponse, DutiesPartyDetail, FreeCompanyPartyDetail, NoviceNetworkParty, PostDetail, UserDynamic, UserPost } from './types';
5
+ import { DataItem } from '@/types';
6
+ import { DynamicSource, INDEX_URL, JOB, NoviceNetworkIdentity, PLAY_STYLE } from './constant';
7
+ import { getDutiesRecruitDetail, getFreeCompanyRecruitDetail, getNoviceNetworkRecruitDetail, getPostsDetail } from './api';
8
+ import timezone from '@/utils/timezone';
9
+ import { parseDate } from '@/utils/parse-date';
10
+ import { art } from '@/utils/render';
11
+ import path from 'node:path';
12
+
13
+ export function checkConfig() {
14
+ if (!config.sdo.ff14risingstones || !config.sdo.ua) {
15
+ throw new ConfigNotFoundError('ff14risingstones RSS is disabled due to the lack of relevant config');
16
+ }
17
+ }
18
+
19
+ export function request(url: string, options?: RequestInit) {
20
+ return ofetch(url, {
21
+ ...options,
22
+ headers: {
23
+ Cookie: `ff14risingstones=${config.sdo.ff14risingstones}`,
24
+ 'User-Agent': config.sdo.ua!,
25
+ ...options?.headers,
26
+ },
27
+ });
28
+ }
29
+
30
+ export async function requestAPI<T = any>(url: string, options?: RequestInit) {
31
+ const response = (await request(url, options)) as BaseResponse<T>;
32
+
33
+ if (response.code !== 10000) {
34
+ throw new Error(response.msg);
35
+ }
36
+ return response.data;
37
+ }
38
+
39
+ export async function generatePostFeeds(posts: UserPost[]) {
40
+ return await Promise.all(
41
+ posts.map(async (post) => {
42
+ const detail = await getPostsDetail(post.posts_id);
43
+ return {
44
+ title: `[${post.part_name}] ${post.title}`,
45
+ link: `${INDEX_URL}#/post/detail/${post.posts_id}`,
46
+ description: detail?.contentInfo.content,
47
+ pubDate: timezone(parseDate(post.created_at), +8),
48
+ updated: detail?.updated_at ? timezone(parseDate(detail.updated_at), +8) : undefined,
49
+ guid: `sdo/ff14risingstones/posts:${post.posts_id}`,
50
+ author: `${post.character_name}@${post.group_name}`,
51
+ } as DataItem;
52
+ })
53
+ );
54
+ }
55
+
56
+ export async function generateDynamicFeeds(dynamics: UserDynamic[]) {
57
+ return await Promise.all(
58
+ dynamics.map(async (dynamic) => {
59
+ let title = `${dynamic.character_name}@${dynamic.group_name} ${dynamic.mask_content}`;
60
+ let link: string | undefined;
61
+ let description: string | undefined;
62
+ let detail: PostDetail | DutiesPartyDetail | FreeCompanyPartyDetail | NoviceNetworkParty | null = null;
63
+
64
+ switch (dynamic.from) {
65
+ case DynamicSource.Post:
66
+ case DynamicSource.Strat:
67
+ if (!dynamic.from_info) {
68
+ break;
69
+ }
70
+ title += dynamic.from_info.title;
71
+ link = `${INDEX_URL}#/post/detail/${dynamic.from_info.posts_id}`;
72
+ detail = await getPostsDetail(dynamic.from_info.posts_id);
73
+ description = detail?.contentInfo.content;
74
+ break;
75
+
76
+ case DynamicSource.NoviceNetwork:
77
+ if (!dynamic.from_info) {
78
+ break;
79
+ }
80
+ title += `[找${dynamic.from_info.identity === NoviceNetworkIdentity.Mentor ? '豆芽' : '导师'}] ${dynamic.from_info.title}`;
81
+ link = `${INDEX_URL}#/recruit/beginner?id=${dynamic.from_info.id}`;
82
+ detail = await getNoviceNetworkRecruitDetail(dynamic.from_info.id);
83
+ description = art(path.join(__dirname, 'templates/novice-network-party.art'), {
84
+ detail_mask: dynamic.from_info.detail_mask,
85
+ styles: dynamic.from_info.style.map((i) => PLAY_STYLE[i]).join(','),
86
+ target: `${dynamic.from_info.target_area_name} ${dynamic.from_info.target_group_name ?? '全区'}`,
87
+ weekday_time: detail?.weekday_time,
88
+ weekend_time: detail?.weekend_time,
89
+ });
90
+ break;
91
+
92
+ case DynamicSource.Duty:
93
+ if (!dynamic.from_info) {
94
+ break;
95
+ }
96
+ title += `[${dynamic.from_info.fb_type}] ${dynamic.from_info.fb_name}`;
97
+ link = `${INDEX_URL}#/recruit/party?id=${dynamic.from_info.id}`;
98
+ detail = await getDutiesRecruitDetail(dynamic.from_info.id);
99
+
100
+ description = art(path.join(__dirname, 'templates/duties-party.art'), {
101
+ progress: dynamic.from_info.progress,
102
+ strategy: dynamic.from_info.strategy,
103
+ fb_name: dynamic.from_info.fb_name,
104
+ fb_time: dynamic.from_info.fb_time,
105
+ labelInfo: dynamic.from_info.labelInfo,
106
+ team_composition: dynamic.from_info.team_composition,
107
+ team_position: dynamic.from_info.team_position
108
+ ? {
109
+ A: Object.keys(dynamic.from_info.team_position.A)
110
+ .filter((i) => dynamic.from_info!.team_position!.A[i] !== '0')
111
+ .map((i) => `${i}: ${JOB[dynamic.from_info!.team_position!.A[i]]}`)
112
+ .join(','),
113
+ B: Object.keys(dynamic.from_info.team_position.B)
114
+ .filter((i) => dynamic.from_info!.team_position!.B[i] !== '0')
115
+ .map((i) => `${i}: ${JOB[dynamic.from_info!.team_position!.B[i]]}`)
116
+ .join(','),
117
+ C: Object.keys(dynamic.from_info.team_position.C)
118
+ .filter((i) => dynamic.from_info!.team_position!.C[i] !== '0')
119
+ .map((i) => `${i}: ${JOB[dynamic.from_info!.team_position!.C[i]]}`)
120
+ .join(','),
121
+ }
122
+ : null,
123
+ MT: JOB[dynamic.from_info.MT],
124
+ ST: JOB[dynamic.from_info.ST],
125
+ T: JOB[dynamic.from_info.T],
126
+ H: JOB[dynamic.from_info.H],
127
+ H1: JOB[dynamic.from_info.H1],
128
+ H2: JOB[dynamic.from_info.H2],
129
+ D1: JOB[dynamic.from_info.D1],
130
+ D2: JOB[dynamic.from_info.D2],
131
+ D3: JOB[dynamic.from_info.D3],
132
+ D4: JOB[dynamic.from_info.D4],
133
+ need_job: detail?.need_job.map((i) => JOB[i]).join(','),
134
+ team_detail_mask: detail?.team_detail_mask,
135
+ recruit_require_mask: detail?.recruit_require_mask,
136
+ strategy_desc_mask: detail?.strategy_desc_mask,
137
+ });
138
+ break;
139
+
140
+ case DynamicSource.FreeCompany:
141
+ if (!dynamic.from_info) {
142
+ break;
143
+ }
144
+ title += `[部队招待] ${dynamic.from_info.guild_name} <${dynamic.from_info.guild_tag}>`;
145
+ link = `${INDEX_URL}#/recruit/guild/detail/${dynamic.from_info.id}`;
146
+ detail = await getFreeCompanyRecruitDetail(dynamic.from_info.id);
147
+
148
+ description = art(path.join(__dirname, 'templates/fc-party.art'), {
149
+ cover_pic: dynamic.from_info.cover_pic,
150
+ guild_name: dynamic.from_info.guild_name,
151
+ guild_tag: dynamic.from_info.guild_tag,
152
+ labelInfo: dynamic.from_info.labelInfo,
153
+ area_name: dynamic.from_info.target_area_name,
154
+ group_name: dynamic.from_info.target_group_name,
155
+ active_member_num: dynamic.from_info.active_member_num,
156
+ target_recruit_num: dynamic.from_info.target_recruit_num,
157
+ weekday_time: dynamic.from_info.weekday_time,
158
+ weekend_time: dynamic.from_info.weekend_time,
159
+ guild_address: detail?.guild_address ?? '',
160
+ create_time: detail?.create_time ?? '',
161
+ foot_pic: detail?.foot_pic ?? '',
162
+ detail_mask: dynamic.from_info.detail_mask,
163
+ });
164
+ break;
165
+
166
+ case DynamicSource.RolePlay:
167
+ if (!dynamic.from_info) {
168
+ break;
169
+ }
170
+ title += dynamic.from_info.rp_name;
171
+ link = `${INDEX_URL}#/recruit/roleplay/detail/${dynamic.from_info.id}`;
172
+ description = art(path.join(__dirname, 'templates/rp-party.art'), {
173
+ cover_pic: dynamic.from_info.cover_pic,
174
+ open_time: dynamic.from_info.open_time,
175
+ rp_type: `${dynamic.from_info.rp_type
176
+ .map(
177
+ (i) =>
178
+ ({
179
+ '0': '无',
180
+ '1': '轻',
181
+ '2': '中',
182
+ '3': '重',
183
+ })[i]
184
+ )
185
+ .join('/')}RP 元素`,
186
+ create_time: dynamic.from_info.create_time,
187
+ area: `${dynamic.from_info.rp_area_name} ${dynamic.from_info.rp_group_name}`,
188
+ address: dynamic.from_info.address,
189
+ custom_label: dynamic.from_info.custom_label,
190
+ profile: dynamic.from_info.profile,
191
+ detail_mask: dynamic.from_info.detail_mask,
192
+ });
193
+ break;
194
+ case DynamicSource.Other:
195
+ if (!dynamic.from_info) {
196
+ break;
197
+ }
198
+ title += `[${dynamic.from_info.category_name}] ${dynamic.from_info.title}`;
199
+ link = `${INDEX_URL}#/recruit/others?id=${dynamic.from_info.id}`;
200
+ description = dynamic.from_info.detail_mask;
201
+ break;
202
+ default:
203
+ // do nothing
204
+ }
205
+ return {
206
+ title,
207
+ link,
208
+ pubDate: timezone(parseDate(dynamic.created_at), +8),
209
+ guid: `sdo/ff14risingstones/dynamics:${dynamic.id}`,
210
+ author: `${dynamic.character_name}@${dynamic.group_name}`,
211
+ description,
212
+ } as DataItem;
213
+ })
214
+ );
215
+ }
@@ -0,0 +1,7 @@
1
+ import type { Namespace } from '@/types';
2
+
3
+ export const namespace: Namespace = {
4
+ name: '盛趣游戏在线',
5
+ url: 'sdo.com',
6
+ lang: 'zh-CN',
7
+ };
@@ -62,7 +62,7 @@ const post = async (requestPath: string, accessToken = md5(Date.now().toString()
62
62
  };
63
63
 
64
64
  function sortBy(items: any[], key: string) {
65
- return items.sort((a, b) => {
65
+ return items.toSorted((a, b) => {
66
66
  if (a[key] < b[key]) {
67
67
  return -1;
68
68
  }
@@ -128,7 +128,7 @@ async function handler(ctx) {
128
128
  .text()
129
129
  .match(/contentData = (.*)/)?.[1]
130
130
  )
131
- .sort((a: any, b: any) => b.length - a.length)[0] || '{}'
131
+ .toSorted((a: any, b: any) => b.length - a.length)[0] || '{}'
132
132
  );
133
133
  const blockRenderData = JSON.parse(
134
134
  $('script:contains("column_2_text")')
@@ -41,7 +41,7 @@ const renderMedia = (mediaEntities) =>
41
41
  case 'photo':
42
42
  return `<img src="${e.mediaURL}">`;
43
43
  case 'video': {
44
- const video = e.videoInfo.variants.filter((v) => v.type === 'video/mp4').sort((a, b) => b.bitrate - a.bitrate)[0];
44
+ const video = e.videoInfo.variants.filter((v) => v.type === 'video/mp4').toSorted((a, b) => b.bitrate - a.bitrate)[0];
45
45
  return `<video controls preload="metadata" poster="${e.mediaURL}"><source src="${video.url}" type="video/mp4"></video>`;
46
46
  }
47
47
  default: