1use crate::{BilibiliRequest, BpiClient, BpiError, BpiResponse};
6use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct ActivityListData {
11 pub list: Vec<ActivityItem>,
13 pub num: i32,
15 pub size: i32,
17 pub total: i32,
19}
20
21#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct ActivityItem {
24 pub id: i32,
26 pub state: i32,
28 pub stime: i64,
30 pub etime: i64,
32 pub ctime: i64,
34 pub mtime: i64,
36 pub name: String,
38 pub h5_url: String,
40 pub h5_cover: String,
42 pub page_name: String,
44 pub plat: i32,
46 pub desc: String,
48}
49
50#[derive(Debug, Clone, Serialize)]
51pub struct ActivityListParams {
52 #[serde(skip_serializing_if = "Option::is_none")]
54 pub plat: Option<String>,
55
56 #[serde(skip_serializing_if = "Option::is_none")]
58 pub mold: Option<i32>,
59
60 #[serde(skip_serializing_if = "Option::is_none")]
62 pub http: Option<i32>,
63
64 #[serde(skip_serializing_if = "Option::is_none")]
66 pub pn: Option<i32>,
67
68 #[serde(skip_serializing_if = "Option::is_none")]
70 pub ps: Option<i32>,
71}
72
73impl Default for ActivityListParams {
74 fn default() -> Self {
75 Self {
76 plat: Some("1,3".to_string()),
77 mold: Some(0),
78 http: Some(3),
79 pn: Some(1),
80 ps: Some(15),
81 }
82 }
83}
84
85impl BpiClient {
86 pub async fn activity_list(
101 &self,
102 plat: Option<&str>,
103 mold: Option<i32>,
104 http: Option<i32>,
105 pn: Option<i32>,
106 ps: Option<i32>,
107 ) -> Result<BpiResponse<ActivityListData>, BpiError> {
108 let params = ActivityListParams {
109 plat: plat
110 .map(|s| s.to_string())
111 .or_else(|| Some("1,3".to_string())),
112 mold: mold.or(Some(0)),
113 http: http.or(Some(3)),
114 pn: pn.or(Some(1)),
115 ps: ps.or(Some(15)),
116 };
117
118 let result = self
119 .get("https://api.bilibili.com/x/activity/page/list")
120 .query(¶ms)
121 .send_bpi("获取活动列表")
122 .await?;
123
124 Ok(result)
125 }
126
127 pub async fn activity_list_default(&self) -> Result<BpiResponse<ActivityListData>, BpiError> {
132 self.activity_list(Some("1,3"), None, None, Some(1), Some(15))
133 .await
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140
141 #[tokio::test]
142 async fn test_get_activity_list() -> Result<(), Box<BpiError>> {
143 let bpi = BpiClient::new();
144
145 let result = bpi
147 .activity_list(Some("1,3"), None, None, Some(1), Some(4))
148 .await?;
149 let data = result.into_data()?;
150 tracing::info!("{:#?}", data);
151
152 assert!(!data.list.is_empty());
153 assert_eq!(data.num, 1);
154 assert_eq!(data.size, 4);
155 assert!(data.total > 0);
156
157 Ok(())
158 }
159
160 #[tokio::test]
161 async fn test_get_activity_list_simple() -> Result<(), Box<BpiError>> {
162 let bpi = BpiClient::new();
163
164 let result = bpi.activity_list_default().await?;
166 let data = result.into_data()?;
167 tracing::info!("{:#?}", data);
168
169 assert!(!data.list.is_empty());
170 assert_eq!(data.num, 1);
171 assert_eq!(data.size, 15);
172
173 Ok(())
174 }
175
176 #[tokio::test]
177 async fn test_activity_item_fields() -> Result<(), Box<BpiError>> {
178 let bpi = BpiClient::new();
179
180 let result = bpi
181 .activity_list(Some("1,3"), None, None, Some(1), Some(1))
182 .await?;
183 let data = result.into_data()?;
184 tracing::info!("{:#?}", data);
185
186 if let Some(activity) = data.list.first() {
187 assert!(activity.id > 0);
188 assert_eq!(activity.state, 1);
189 assert!(!activity.name.is_empty());
190 assert!(!activity.page_name.is_empty());
191 }
192
193 Ok(())
194 }
195}