1use serde::{ Deserialize, Serialize };
2
3use crate::{ BilibiliRequest, BpiClient, BpiError, BpiResponse };
4
5#[derive(Debug, Clone, Default, Serialize, Deserialize)]
6pub struct DynamicAllData {
7 pub has_more: bool,
8 pub items: Vec<DynamicItem>,
9 pub offset: String,
10 pub update_baseline: String,
11 pub update_num: i64,
12}
13
14#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
15pub struct DynamicItem {
16 pub basic: Basic,
17 pub id_str: String,
18 pub modules: serde_json::Value,
19 #[serde(rename = "type")]
20 pub type_field: String,
21 pub visible: bool,
22}
23
24#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
25pub struct Basic {
26 pub comment_id_str: String,
27 pub comment_type: i64,
28 pub like_icon: serde_json::Value,
29 pub rid_str: String,
30 pub is_only_fans: Option<bool>,
31 pub jump_url: Option<String>,
32}
33
34#[derive(Debug, Clone, Deserialize)]
36pub struct DynamicUpdateData {
37 pub update_num: u64,
39}
40
41impl BpiClient {
42 pub async fn dynamic_all(
55 &self,
56 host_mid: Option<&str>,
57 offset: Option<&str>,
58 update_baseline: Option<&str>
59 ) -> Result<BpiResponse<DynamicAllData>, BpiError> {
60 let mut req = self.get("https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/all").query(
61 &[
62 (
63 "features",
64 "itemOpusStyle,listOnlyfans,opusBigCover,onlyfansVote,decorationCard,onlyfansAssetsV2,forwardListHidden,ugcDelete",
65 ),
66 ("web_location", "333.1365"),
67 ]
68 );
69
70 if let Some(mid) = host_mid {
71 req = req.query(&[("host_mid", mid)]);
72 }
73 if let Some(off) = offset {
74 req = req.query(&[("offset", off)]);
75 }
76 if let Some(baseline) = update_baseline {
77 req = req.query(&[("update_baseline", baseline)]);
78 }
79
80 req.send_bpi("获取全部动态列表").await
81 }
82
83 pub async fn dynamic_check_new(
95 &self,
96 update_baseline: &str,
97 type_str: Option<&str>
98 ) -> Result<BpiResponse<DynamicUpdateData>, BpiError> {
99 let mut req = self
100 .get("https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/all/update")
101 .query(&[("update_baseline", update_baseline)]);
102
103 if let Some(typ) = type_str {
104 req = req.query(&[("type", typ)]);
105 }
106
107 req.send_bpi("检测新动态").await
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114 use tracing::info;
115
116 #[tokio::test]
117 async fn test_dynamic_get_all() -> Result<(), BpiError> {
118 let bpi = BpiClient::new();
119 let resp = bpi.dynamic_all(None, None, None).await?;
120 assert_eq!(resp.code, 0);
121
122 let data = resp.into_data()?;
123
124 info!("成功获取 {} 条动态", data.items.len());
125
126 Ok(())
127 }
128
129 #[tokio::test]
130 async fn test_dynamic_check_new() -> Result<(), BpiError> {
131 let bpi = BpiClient::new();
132 let update_baseline = "0";
133 let resp = bpi.dynamic_check_new(update_baseline, None).await?;
134 let data = resp.into_data().unwrap();
135
136 info!("成功检测到 {} 条新动态", data.update_num);
137
138 Ok(())
139 }
140}