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
61 .get("https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/all")
62 .query(&[("features", "itemOpusStyle,listOnlyfans,opusBigCover,onlyfansVote,decorationCard,onlyfansAssetsV2,forwardListHidden,ugcDelete"), ("web_location", "333.1365")]);
63
64 if let Some(mid) = host_mid {
65 req = req.query(&[("host_mid", mid)]);
66 }
67 if let Some(off) = offset {
68 req = req.query(&[("offset", off)]);
69 }
70 if let Some(baseline) = update_baseline {
71 req = req.query(&[("update_baseline", baseline)]);
72 }
73
74 req.send_bpi("获取全部动态列表").await
75 }
76
77 pub async fn dynamic_check_new(
89 &self,
90 update_baseline: &str,
91 type_str: Option<&str>,
92 ) -> Result<BpiResponse<DynamicUpdateData>, BpiError> {
93 let mut req = self
94 .get("https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/all/update")
95 .query(&[("update_baseline", update_baseline)]);
96
97 if let Some(typ) = type_str {
98 req = req.query(&[("type", typ)]);
99 }
100
101 req.send_bpi("检测新动态").await
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108 use tracing::info;
109
110 #[tokio::test]
111
112 async fn test_dynamic_get_all() -> Result<(), BpiError> {
113 let bpi = BpiClient::new();
114 let resp = bpi.dynamic_all(None, None, None).await?;
115 assert_eq!(resp.code, 0);
116
117 let data = resp.into_data()?;
118
119 info!("成功获取 {} 条动态", data.items.len());
120
121 Ok(())
122 }
123
124 #[tokio::test]
125
126 async fn test_dynamic_check_new() -> Result<(), BpiError> {
127 let bpi = BpiClient::new();
128 let update_baseline = "0";
129 let resp = bpi.dynamic_check_new(update_baseline, None).await?;
130 let data = resp.into_data().unwrap();
131
132 info!("成功检测到 {} 条新动态", data.update_num);
133
134 Ok(())
135 }
136}