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.get("https://api.bilibili.com/x/v2/reply")
120 .query(¶ms)
121 .send_bpi("获取评论主列表")
122 .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.get("https://api.bilibili.com/x/v2/reply/reply")
161 .query(¶ms)
162 .send_bpi("获取子评论列表")
163 .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.get("https://api.bilibili.com/x/v2/reply/hot")
202 .query(¶ms)
203 .send_bpi("获取评论区热评列表")
204 .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 = [("type", r#type.to_string()), ("oid", oid.to_string())];
222 self.get("https://api.bilibili.com/x/v2/reply/count")
223 .query(¶ms)
224 .send_bpi("获取评论区评论总数")
225 .await
226 }
227}
228
229#[cfg(test)]
230mod tests {
231 use super::*;
232 use tracing::info;
233
234 const TEST_TYPE: i32 = 1;
235 const TEST_OID: i64 = 23199;
236 const TEST_ROOT_RPID: i64 = 2554491176;
237
238 #[tokio::test]
239 async fn test_comment_list() -> Result<(), Box<BpiError>> {
240 let bpi = BpiClient::new();
241
242 let result = bpi
243 .comment_list(TEST_TYPE, TEST_OID, Some(1), Some(5), Some(0), Some(0))
244 .await?;
245 let data = result.into_data()?;
246 info!("总评论数: {}", data.replies.unwrap().len());
247
248 Ok(())
249 }
250
251 #[tokio::test]
252 async fn test_comment_replies() -> Result<(), Box<BpiError>> {
253 let bpi = BpiClient::new();
254
255 let result = bpi
256 .comment_replies(TEST_TYPE, TEST_OID, TEST_ROOT_RPID, Some(1), Some(5))
257 .await?;
258 let data = result.into_data()?;
259 info!("总评论数: {}", data.replies.unwrap().len());
260
261 Ok(())
262 }
263
264 #[tokio::test]
265 async fn test_comment_hot() -> Result<(), Box<BpiError>> {
266 let bpi = BpiClient::new();
267 let root_rpid = 654321;
268
269 let result = bpi
270 .comment_hot(TEST_TYPE, TEST_OID, root_rpid, Some(1), Some(5))
271 .await?;
272 let data = result.into_data()?;
273
274 info!("热评数量: {}", data.replies.len());
275 for comment in data.replies.iter() {
276 info!("热评内容: {}", comment.content.message);
277 }
278
279 Ok(())
280 }
281
282 #[tokio::test]
283 async fn test_comment_count() -> Result<(), Box<BpiError>> {
284 let bpi = BpiClient::new();
285
286 let result = bpi.comment_count(TEST_TYPE, TEST_OID).await?;
287
288 let data = result.into_data()?;
289 info!("评论总数: {}", data.count);
290
291 Ok(())
292 }
293}