1use crate::{ BilibiliRequest, BpiClient, BpiError, BpiResponse };
6use serde::{ Deserialize, Serialize };
7
8use super::types::{
9 Comment, Config,
11 Control,
12 Cursor,
13 PageInfo,
14 Top,
15 Upper,
16};
17
18pub type CommentListResponse = BpiResponse<CommentListData>;
20
21#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct CommentListData {
23 pub page: Option<PageInfo>,
24 pub cursor: Option<Cursor>, pub replies: Option<Vec<Comment>>, pub top: Option<Top>, pub top_replies: Option<Vec<Comment>>,
28 pub effects: Option<serde_json::Value>,
29 pub assist: Option<u64>, pub blacklist: Option<u64>, pub vote: Option<u64>, pub config: Option<Config>, pub upper: Option<Upper>, pub control: Option<Control>, pub note: Option<u32>,
37 pub cm_info: Option<serde_json::Value>, }
48
49#[derive(Debug, Clone, Serialize, Deserialize)]
51pub struct Notice {
52 pub content: Option<String>,
53 pub id: Option<u64>,
54 pub link: Option<String>,
55 pub title: Option<String>,
56}
57
58type HotCommentResponse = BpiResponse<HotCommentData>;
59
60#[derive(Debug, Clone, Deserialize, Serialize)]
61pub struct HotCommentData {
62 pub page: HotCommentPage,
63 pub replies: Vec<Comment>, }
65
66#[derive(Debug, Clone, Deserialize, Serialize)]
67pub struct HotCommentPage {
68 pub acount: i64, pub count: i64, pub num: i32, pub size: i32, }
73
74#[derive(Debug, Clone, Deserialize, Serialize)]
75pub struct CountData {
76 count: u64,
77}
78
79impl BpiClient {
80 pub async fn comment_list(
97 &self,
98 r#type: i32,
99 oid: i64,
100 pn: Option<i32>,
101 ps: Option<i32>,
102 sort: Option<i32>,
103 nohot: Option<i32>
104 ) -> Result<CommentListResponse, BpiError> {
105 let mut params = vec![("type", r#type.to_string()), ("oid", oid.to_string())];
106 if let Some(pn) = pn {
107 params.push(("pn", pn.to_string()));
108 }
109 if let Some(ps) = ps {
110 params.push(("ps", ps.to_string()));
111 }
112 if let Some(sort) = sort {
113 params.push(("sort", sort.to_string()));
114 }
115 if let Some(nohot) = nohot {
116 params.push(("nohot", nohot.to_string()));
117 }
118
119 self
120 .get("https://api.bilibili.com/x/v2/reply")
121 .query(¶ms)
122 .send_bpi("获取评论主列表").await
123 }
124
125 pub async fn comment_replies(
141 &self,
142 r#type: i32,
143 oid: i64,
144 root: i64,
145 pn: Option<i32>,
146 ps: Option<i32>
147 ) -> Result<CommentListResponse, BpiError> {
148 let mut params = vec![
149 ("type", r#type.to_string()),
150 ("oid", oid.to_string()),
151 ("root", root.to_string())
152 ];
153 if let Some(pn) = pn {
154 params.push(("pn", pn.to_string()));
155 }
156 if let Some(ps) = ps {
157 params.push(("ps", ps.to_string()));
158 }
159
160 self
161 .get("https://api.bilibili.com/x/v2/reply/reply")
162 .query(¶ms)
163 .send_bpi("获取子评论列表").await
164 }
165
166 pub async fn comment_hot(
182 &self,
183 r#type: i32,
184 oid: i64,
185 root: i64,
186 pn: Option<i32>,
187 ps: Option<i32>
188 ) -> Result<HotCommentResponse, BpiError> {
189 let mut params = vec![
190 ("type", r#type.to_string()),
191 ("oid", oid.to_string()),
192 ("root", root.to_string())
193 ];
194 if let Some(pn) = pn {
195 params.push(("pn", pn.to_string()));
196 }
197 if let Some(ps) = ps {
198 params.push(("ps", ps.to_string()));
199 }
200
201 self
202 .get("https://api.bilibili.com/x/v2/reply/hot")
203 .query(¶ms)
204 .send_bpi("获取评论区热评列表").await
205 }
206 pub async fn comment_count(
217 &self,
218 r#type: i32,
219 oid: i64
220 ) -> Result<BpiResponse<CountData>, BpiError> {
221 let params = [
222 ("type", r#type.to_string()),
223 ("oid", oid.to_string()),
224 ];
225 self
226 .get("https://api.bilibili.com/x/v2/reply/count")
227 .query(¶ms)
228 .send_bpi("获取评论区评论总数").await
229 }
230}
231
232#[cfg(test)]
233mod tests {
234 use super::*;
235 use tracing::info;
236
237 const TEST_TYPE: i32 = 1;
238 const TEST_OID: i64 = 23199;
239 const TEST_ROOT_RPID: i64 = 2554491176;
240
241 #[tokio::test]
242 async fn test_comment_list() -> Result<(), Box<BpiError>> {
243 let bpi = BpiClient::new();
244
245 let result = bpi.comment_list(
246 TEST_TYPE,
247 TEST_OID,
248 Some(1),
249 Some(5),
250 Some(0),
251 Some(0)
252 ).await?;
253 let data = result.into_data()?;
254 info!("总评论数: {}", data.replies.unwrap().len());
255
256 Ok(())
257 }
258
259 #[tokio::test]
260 async fn test_comment_replies() -> Result<(), Box<BpiError>> {
261 let bpi = BpiClient::new();
262
263 let result = bpi.comment_replies(
264 TEST_TYPE,
265 TEST_OID,
266 TEST_ROOT_RPID,
267 Some(1),
268 Some(5)
269 ).await?;
270 let data = result.into_data()?;
271 info!("总评论数: {}", data.replies.unwrap().len());
272
273 Ok(())
274 }
275
276 #[tokio::test]
277 async fn test_comment_hot() -> Result<(), Box<BpiError>> {
278 let bpi = BpiClient::new();
279 let root_rpid = 654321;
280
281 let result = bpi.comment_hot(TEST_TYPE, TEST_OID, root_rpid, Some(1), Some(5)).await?;
282 let data = result.into_data()?;
283
284 info!("热评数量: {}", data.replies.len());
285 for comment in data.replies.iter() {
286 info!("热评内容: {}", comment.content.message);
287 }
288
289 Ok(())
290 }
291
292 #[tokio::test]
293 async fn test_comment_count() -> Result<(), Box<BpiError>> {
294 let bpi = BpiClient::new();
295
296 let result = bpi.comment_count(TEST_TYPE, TEST_OID).await?;
297
298 let data = result.into_data()?;
299 info!("评论总数: {}", data.count);
300
301 Ok(())
302 }
303}