Skip to content

Commit 33b28ca

Browse files
committed
Add command to refetch news from Hoyolab posts
1 parent 5ebdbea commit 33b28ca

File tree

5 files changed

+232
-125
lines changed

5 files changed

+232
-125
lines changed

src/commands/admin/thread.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ export default class ThreadCommand extends Command {
2222

2323
async runInteraction(source: CommandInteraction): Promise<SendMessage | undefined> {
2424
return sendMessage(source, "Slash command not supported")
25-
2625
}
26+
2727
async runMessage(source: Message, args: string[]): Promise<SendMessage | undefined> {
2828
const id = source.author.id
2929
let name = args.join(" ")

src/commands/admin/updatenews.ts

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import { CommandInteraction, Message, MessageEmbed } from "discord.js"
2+
import config from "../../data/config.json"
3+
import client from "../../main"
4+
import Command from "../../utils/Command"
5+
import { CommandSource, Cover, NewsLang, SendMessage } from "../../utils/Types"
6+
import { Colors, findFuzzy, sendMessage } from "../../utils/Utils"
7+
8+
9+
export default class UpdateNews extends Command {
10+
constructor(name: string) {
11+
super({
12+
name,
13+
category: "Admin",
14+
usage: "updatenews [language] <id>",
15+
shortHelp: "Update a news post.",
16+
help: `Update a news post.
17+
Supported languages: ${client.newsManager.getLanguages().map(l => `\`${l}\``).join(", ")}`,
18+
aliases: [],
19+
options: []
20+
})
21+
}
22+
23+
24+
async runInteraction(source: CommandInteraction): Promise<SendMessage | undefined> {
25+
return sendMessage(source, "Slash command not supported")
26+
}
27+
28+
async runMessage(source: Message, args: string[]): Promise<SendMessage | undefined> {
29+
let idMatch = args[0], langMatch = "en-us"
30+
if (args[0]?.match(/^\d+/)) {
31+
idMatch = args[0]
32+
langMatch = args[1]
33+
} else {
34+
idMatch = args[1]
35+
langMatch = args[0]
36+
}
37+
38+
return this.run(source, idMatch, langMatch)
39+
}
40+
41+
async run(source: CommandSource, id?: string | null, langMatch?: string | null): Promise<SendMessage | undefined> {
42+
const { newsManager } = client
43+
44+
const lang = this.getFuzzyLang(langMatch ?? "en-us")
45+
46+
if (!id) {
47+
const stored = newsManager
48+
.getNews(lang)
49+
.map(art => `[\`${art.post_id}\`](${art.lang == "bbs-zh-cn" ? `https://bbs.mihoyo.com/ys/article/${art.post_id}` : `https://www.hoyolab.com/article/${art.post_id}`}): ${art.subject}`)
50+
51+
while (stored.join("\n").length > 1500) stored.pop()
52+
53+
const embed = new MessageEmbed()
54+
.setColor(Colors.GREEN)
55+
.setTitle(`Most recent ${newsManager.getLanguageName(lang)} news articles:`)
56+
.setFooter(`You can use open the links or use \`${config.prefix}news <post id>\` to view more details about a post`)
57+
.setDescription(stored.join("\n"))
58+
59+
return sendMessage(source, embed)
60+
}
61+
62+
const post = newsManager.getNewsByIdLang(id, lang)
63+
if (!post)
64+
return sendMessage(source, `Couldn't find article in cache. Try to see if it exists on the forum: <https://www.hoyolab.com/article/${id}>`)
65+
66+
await newsManager.fetchPost({
67+
image_list: JSON.parse(post.image_url) as Cover[],
68+
post: {
69+
content: post.content,
70+
created_at: post.created_at,
71+
post_id: post.post_id,
72+
subject: post.subject
73+
},
74+
user: {
75+
nickname: post.nickname
76+
}
77+
}, lang, post.type)
78+
79+
80+
return sendMessage(source, "Updated!")
81+
}
82+
83+
getFuzzyLang(search: string): NewsLang {
84+
const { newsManager } = client
85+
86+
let lang = findFuzzy([
87+
...newsManager.getLanguages(),
88+
...newsManager.getLanguages().map(l => newsManager.getLanguageName(l))
89+
], search ?? "en-us") ?? "en-us"
90+
91+
for (const l of newsManager.getLanguages())
92+
if (lang == newsManager.getLanguageName(l)) {
93+
lang = l
94+
break
95+
}
96+
97+
return lang as NewsLang
98+
}
99+
}

src/utils/NewsManager.ts

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -119,30 +119,10 @@ export default class NewsManager {
119119
this.invalidateHasNewsByIdLangCache()
120120

121121
Logger.info(`Fetching new post: ${language} ${post_id} - ${article.post.subject}`)
122-
let fetched
123-
for (let attempt = 1; attempt <= 5; attempt++)
124-
try {
125-
this.lastFetched = Date.now()
126-
if (langid == "bbs-zh-cn")
127-
fetched = await fetch(`https://bbs-api.mihoyo.com/post/wapi/getPostFull?gids=2&post_id=${post_id}&read=1`, { headers: { "x-rpc-language": langid, Referer: "https://bbs.mihoyo.com/" }, timeout: 15000 })
128-
else
129-
fetched = await fetch(`https://bbs-api-os.hoyolab.com/community/post/wapi/getPostFull?gids=2&post_id=${post_id}&read=1`, { headers: { "x-rpc-language": langid, Referer: "https://bbs.mihoyo.com/" }, timeout: 29000 })
130-
break
131-
} catch (error) {
132-
Logger.error(`Failed to fetch ${language} - ${type}, attempt #${attempt}.`)
133-
if (attempt == 8) throw error
134-
}
135-
136-
if (!fetched) continue
137-
const postdata = await fetched.json()
138-
139-
this.lastFetched = Date.now()
140-
if (!postdata?.data?.post) continue
141-
142-
const post: News = postdata.data.post
143-
article.post = post.post
144-
145-
const stored = this.addNews(article, langid, type)
122+
123+
const stored = await this.fetchPost(article, langid, type)
124+
if (!stored) continue
125+
146126
this.post(language, stored).catch(Logger.error)
147127
if (stored.subject.includes("Event Wish"))
148128
parseEventWishNews(stored)
@@ -153,6 +133,34 @@ export default class NewsManager {
153133
}
154134
}
155135

136+
async fetchPost(article: News, langid: NewsLang, type: number) {
137+
const post_id = article.post.post_id
138+
let fetched
139+
for (let attempt = 1; attempt <= 5; attempt++)
140+
try {
141+
this.lastFetched = Date.now()
142+
if (langid == "bbs-zh-cn")
143+
fetched = await fetch(`https://bbs-api.mihoyo.com/post/wapi/getPostFull?gids=2&post_id=${post_id}&read=1`, { headers: { "x-rpc-language": langid, Referer: "https://bbs.mihoyo.com/" }, timeout: 15000 })
144+
else
145+
fetched = await fetch(`https://bbs-api-os.hoyolab.com/community/post/wapi/getPostFull?gids=2&post_id=${post_id}&read=1`, { headers: { "x-rpc-language": langid, Referer: "https://bbs.mihoyo.com/" }, timeout: 29000 })
146+
break
147+
} catch (error) {
148+
Logger.error(`Failed to fetch ${langid} - ${type}, attempt #${attempt}.`)
149+
if (attempt == 8) throw error
150+
}
151+
152+
if (!fetched) return
153+
const postdata = await fetched.json()
154+
155+
this.lastFetched = Date.now()
156+
if (!postdata?.data?.post) return
157+
158+
const post: News = postdata.data.post
159+
article.post = post.post
160+
161+
return this.addNews(article, langid, type)
162+
}
163+
156164
async post(lang: FollowCategory, post: StoredNews): Promise<void> {
157165
const embed = getNewsEmbed(post)
158166

src/utils/Types.ts

Lines changed: 97 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -56,19 +56,19 @@ export interface Note {
5656
// News posts
5757
export interface News {
5858
post: Post
59-
forum: Forum
60-
topics: Topic[]
59+
// forum: Forum
60+
// topics: Topic[]
6161
user: NewsUser
62-
self_operation: SelfOperation
63-
stat: Stat
64-
help_sys?: HelpSys | null
65-
cover?: Cover | null
62+
// self_operation: SelfOperation
63+
// stat: Stat
64+
// help_sys?: HelpSys | null
65+
// cover?: Cover | null
6666
image_list: Cover[]
67-
is_official_master: boolean
68-
is_user_master: boolean
69-
hot_reply_exist: boolean
70-
vote_count: number
71-
last_modify_time: number
67+
// is_official_master: boolean
68+
// is_user_master: boolean
69+
// hot_reply_exist: boolean
70+
// vote_count: number
71+
// last_modify_time: number
7272
}
7373

7474
export interface Cover {
@@ -86,102 +86,102 @@ export enum Format {
8686
PNG = "png",
8787
}
8888

89-
export interface Forum {
90-
id: number
91-
name: string
92-
icon: string
93-
game_id: number
94-
}
89+
// export interface Forum {
90+
// id: number
91+
// name: string
92+
// icon: string
93+
// game_id: number
94+
// }
9595

96-
export interface HelpSys {
97-
top_up: null
98-
}
96+
// export interface HelpSys {
97+
// top_up: null
98+
// }
9999

100100
export interface Post {
101-
game_id: number
101+
// game_id: number
102102
post_id: string
103-
f_forum_id: number
104-
uid: string
103+
// f_forum_id: number
104+
// uid: string
105105
subject: string
106106
content: string
107-
cover: string
108-
view_type: number
107+
// cover: string
108+
// view_type: number
109109
created_at: number
110-
images: unknown[]
111-
post_status: PostStatus
112-
topic_ids: number[]
113-
view_status: number
114-
max_floor: number
115-
is_original: number
116-
republish_authorization: number
117-
reply_time: Date
118-
is_deleted: number
119-
is_interactive: boolean
120-
structured_content: string
121-
structured_content_rows: unknown[]
122-
lang: string
123-
official_type: number
124-
reply_forbid: ReplyForbid | null
125-
}
126-
127-
export interface PostStatus {
128-
is_top: boolean
129-
is_good: boolean
130-
is_official: boolean
131-
}
132-
133-
export interface ReplyForbid {
134-
date_type: number
135-
start_date: string
136-
cur_date: string
137-
level: number
138-
}
139-
140-
export interface SelfOperation {
141-
attitude: number
142-
is_collected: boolean
143-
}
144-
145-
export interface Stat {
146-
view_num: number
147-
reply_num: number
148-
like_num: number
149-
bookmark_num: number
150-
share_num: number
151-
}
152-
153-
export interface Topic {
154-
id: number
155-
name: string
156-
cover: string
157-
is_top: boolean
158-
is_good: boolean
159-
is_interactive: boolean
160-
game_id: number
161-
}
110+
// images: unknown[]
111+
// post_status: PostStatus
112+
// topic_ids: number[]
113+
// view_status: number
114+
// max_floor: number
115+
// is_original: number
116+
// republish_authorization: number
117+
// reply_time: Date
118+
// is_deleted: number
119+
// is_interactive: boolean
120+
// structured_content: string
121+
// structured_content_rows: unknown[]
122+
// lang: string
123+
// official_type: number
124+
// reply_forbid: ReplyForbid | null
125+
}
126+
127+
// export interface PostStatus {
128+
// is_top: boolean
129+
// is_good: boolean
130+
// is_official: boolean
131+
// }
132+
133+
// export interface ReplyForbid {
134+
// date_type: number
135+
// start_date: string
136+
// cur_date: string
137+
// level: number
138+
// }
139+
140+
// export interface SelfOperation {
141+
// attitude: number
142+
// is_collected: boolean
143+
// }
144+
145+
// export interface Stat {
146+
// view_num: number
147+
// reply_num: number
148+
// like_num: number
149+
// bookmark_num: number
150+
// share_num: number
151+
// }
152+
153+
// export interface Topic {
154+
// id: number
155+
// name: string
156+
// cover: string
157+
// is_top: boolean
158+
// is_good: boolean
159+
// is_interactive: boolean
160+
// game_id: number
161+
// }
162162

163163
export interface NewsUser {
164-
uid: string
164+
// uid: string
165165
nickname: string
166-
introduce: string
167-
avatar: string
168-
gender: number
169-
certification: Certification
170-
level_exp: LevelExp
171-
is_following: boolean
172-
is_followed: boolean
173-
avatar_url: string
174-
}
175-
176-
export interface Certification {
177-
type: number
178-
label: string
179-
}
180-
181-
export interface LevelExp {
182-
level: number
183-
exp: number
184-
}
166+
// introduce: string
167+
// avatar: string
168+
// gender: number
169+
// certification: Certification
170+
// level_exp: LevelExp
171+
// is_following: boolean
172+
// is_followed: boolean
173+
// avatar_url: string
174+
}
175+
176+
// export interface Certification {
177+
// type: number
178+
// label: string
179+
// }
180+
181+
// export interface LevelExp {
182+
// level: number
183+
// exp: number
184+
// }
185185

186186
export type NewsLang = "zh-cn" | "bbs-zh-cn" | "zh-tw" | "de-de" | "en-us" | "es-es" | "fr-fr" | "id-id" | "ja-jp" | "ko-kr" | "pt-pt" | "ru-ru" | "th-th" | "vi-vn"
187187

0 commit comments

Comments
 (0)