@@ -27,14 +27,15 @@ export const route: Route = {
27
27
| Diablo II: Resurrected | diablo2 |
28
28
| Diablo III | diablo3 |
29
29
| Diablo IV | diablo4 |
30
- | Diablo: Immortal | diablo-immortal |
30
+ | Diablo Immortal | diablo-immortal |
31
31
| Hearthstone | hearthstone |
32
32
| Heroes of the Storm | heroes-of-the-storm |
33
33
| Overwatch 2 | overwatch |
34
34
| StarCraft: Remastered | starcraft |
35
35
| StarCraft II | starcraft2 |
36
36
| World of Warcraft | world-of-warcraft |
37
- | Warcraft III: Reforged | warcraft3 |
37
+ | Warcraft 3: Reforged | warcraft3 |
38
+ | Warcraft Rumble | warcraft-rumble |
38
39
| Battle.net | battlenet |
39
40
| BlizzCon | blizzcon |
40
41
| Inside Blizzard | blizzard |
@@ -59,49 +60,137 @@ export const route: Route = {
59
60
| 繁體中文 | zh-tw |` ,
60
61
} ;
61
62
63
+ const GAME_MAP = {
64
+ diablo2 : {
65
+ key : 'diablo2' ,
66
+ value : 'diablo-2-resurrected' ,
67
+ id : 'blt54fbd3787a705054' ,
68
+ } ,
69
+ diablo3 : {
70
+ key : 'diablo3' ,
71
+ value : 'diablo-3' ,
72
+ id : 'blt2031aef34200656d' ,
73
+ } ,
74
+ diablo4 : {
75
+ key : 'diablo4' ,
76
+ value : 'diablo-4' ,
77
+ id : 'blt795c314400d7ded9' ,
78
+ } ,
79
+ 'diablo-immortal' : {
80
+ key : 'diablo-immortal' ,
81
+ value : 'diablo-immortal' ,
82
+ id : 'blt525c436e4a1b0a97' ,
83
+ } ,
84
+ hearthstone : {
85
+ key : 'hearthstone' ,
86
+ value : 'hearthstone' ,
87
+ id : 'blt5cfc6affa3ca0638' ,
88
+ } ,
89
+ 'heroes-of-the-storm' : {
90
+ key : 'heroes-of-the-storm' ,
91
+ value : 'heroes-of-the-storm' ,
92
+ id : 'blt2e50e1521bb84dc6' ,
93
+ } ,
94
+ overwatch : {
95
+ key : 'overwatch' ,
96
+ value : 'overwatch' ,
97
+ id : 'blt376fb94931906b6f' ,
98
+ } ,
99
+ starcraft : {
100
+ key : 'starcraft' ,
101
+ value : 'starcraft' ,
102
+ id : 'blt81d46fcb05ab8811' ,
103
+ } ,
104
+ starcraft2 : {
105
+ key : 'starcraft2' ,
106
+ value : 'starcraft-2' ,
107
+ id : 'bltede2389c0a8885aa' ,
108
+ } ,
109
+ 'world-of-warcraft' : {
110
+ key : 'world-of-warcraft' ,
111
+ value : 'world-of-warcraft' ,
112
+ id : 'blt2caca37e42f19839' ,
113
+ } ,
114
+ warcraft3 : {
115
+ key : 'warcraft3' ,
116
+ value : 'warcraft-3' ,
117
+ id : 'blt24859ba8086fb294' ,
118
+ } ,
119
+ 'warcraft-rumble' : {
120
+ key : 'warcraft-rumble' ,
121
+ value : 'warcraft-rumble' ,
122
+ id : 'blte27d02816a8ff3e1' ,
123
+ } ,
124
+ battlenet : {
125
+ key : 'battlenet' ,
126
+ value : 'battle-net' ,
127
+ id : 'blt90855744d00cd378' ,
128
+ } ,
129
+ blizzcon : {
130
+ key : 'blizzcon' ,
131
+ value : 'blizzcon' ,
132
+ id : 'bltec70ad0ea4fd6d1d' ,
133
+ } ,
134
+ blizzard : {
135
+ key : 'blizzard' ,
136
+ value : 'blizzard' ,
137
+ id : 'blt500c1f8b5470bfdb' ,
138
+ } ,
139
+ } ;
140
+
141
+ function getSearchParams ( category = 'all' ) {
142
+ return category === 'all'
143
+ ? Object . values ( GAME_MAP )
144
+ . map ( ( item ) => `feedCxpProductIds[]=${ item . id } ` )
145
+ . join ( '&' )
146
+ : `feedCxpProductIds[]=${ GAME_MAP [ category ] . id } ` ;
147
+ }
148
+
62
149
async function handler ( ctx ) {
63
- const category = ctx . req . param ( 'category' ) || '' ;
150
+ const category = GAME_MAP [ ctx . req . param ( 'category' ) ] ?. key || 'all ' ;
64
151
const language = ctx . req . param ( 'language' ) || 'en-us' ;
152
+ const rootUrl = `https://news.blizzard.com/${ language } ` ;
153
+ const currentUrl = category === 'all' ? rootUrl : `${ rootUrl } /?filter=${ GAME_MAP [ category ] . value } ` ;
154
+ const apiUrl = `${ rootUrl } /api/news/blizzard` ;
155
+ let rssTitle = '' ;
65
156
66
- const rootUrl = 'https://news.blizzard.com' ;
67
- const currentUrl = `${ rootUrl } /${ language } /${ category } ` ;
68
- const apiUrl = `${ rootUrl } /${ language } /blog/list` ;
69
- const response = await got ( apiUrl , {
70
- searchParams : {
71
- community : category === '' ? 'all' : category ,
157
+ const {
158
+ data : {
159
+ feed : { contentItems : response } ,
72
160
} ,
73
- } ) ;
74
-
75
- const $ = load ( response . data . html , null , false ) ;
161
+ } = await got ( `${ apiUrl } ?${ getSearchParams ( category ) } ` ) ;
76
162
77
- const list = $ ( '.FeaturedArticle-text > a, .ArticleListItem > article > a' )
78
- . slice ( 0 , ctx . req . query ( 'limit' ) ? Number . parseInt ( ctx . req . query ( 'limit' ) , 10 ) : 30 )
79
- . toArray ( )
80
- . map ( ( item ) => {
81
- item = $ ( item ) ;
82
- return {
83
- title : item . text ( ) ,
84
- link : `${ rootUrl } ${ item . attr ( 'href' ) } ` ,
85
- } ;
86
- } ) ;
163
+ const list = response . map ( ( item ) => {
164
+ const content = item . properties ;
165
+ rssTitle = category === 'all' ? 'All News' : content . cxpProduct . title ; // 这个是用来填充 RSS 订阅源频道级别 title,没别的地方能拿到了(而且会根据语言切换)
166
+ return {
167
+ title : content . title ,
168
+ link : content . newsUrl ,
169
+ author : content . author ,
170
+ category : content . category ,
171
+ guid : content . newsId ,
172
+ description : content . summary ,
173
+ pubDate : content . lastUpdated ,
174
+ } ;
175
+ } ) ;
87
176
88
177
const items = await Promise . all (
89
178
list . map ( ( item ) =>
90
179
cache . tryGet ( item . link , async ( ) => {
91
- const detailResponse = await got ( item . link ) ;
92
- const content = load ( detailResponse . data ) ;
93
-
94
- item . author = content ( '.ArticleDetail-bylineAuthor ' ) . text ( ) ;
95
- item . description = content ( '.ArticleDetail-headingImageBlock' ) . html ( ) + content ( '.ArticleDetail-content' ) . html ( ) ;
96
- item . pubDate = content ( '.ArticleDetail-subHeadingLeft time' ) . attr ( 'timestamp' ) ;
97
-
98
- return item ;
180
+ try {
181
+ const { data : response } = await got ( item . link ) ;
182
+ const $ = load ( response ) ;
183
+ item . description = $ ( '.Content ' ) . html ( ) ;
184
+ return item ;
185
+ } catch {
186
+ return item ;
187
+ }
99
188
} )
100
189
)
101
190
) ;
102
191
103
192
return {
104
- title : $ ( 'title' ) . text ( ) ,
193
+ title : rssTitle ,
105
194
link : currentUrl ,
106
195
item : items ,
107
196
} ;
0 commit comments