Skip to main content

bpi_rs/activity/
list.rs

1//! 活动列表
2//!
3//! [查看 API 文档](https://github.com/Yuelioi/bilibili-API-collect/tree/cfc5fddcc8a94b74d91970bb5b4eaeb349addc47/docs/activity/list.md)
4
5use crate::{ BilibiliRequest, BpiClient, BpiError, BpiResponse };
6use serde::{ Deserialize, Serialize };
7
8/// 活动列表数据
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct ActivityListData {
11    /// 活动列表
12    pub list: Vec<ActivityItem>,
13    /// 当前页码
14    pub num: i32,
15    /// 每页条数
16    pub size: i32,
17    /// 总条数
18    pub total: i32,
19}
20
21/// 活动项目
22#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct ActivityItem {
24    /// 活动 ID
25    pub id: i32,
26    /// 固定值 1
27    pub state: i32,
28    /// 开始时间 UNIX 秒级时间戳
29    pub stime: i64,
30    /// 结束时间 UNIX 秒级时间戳
31    pub etime: i64,
32    /// 创建时间? UNIX 秒级时间戳, 可能为 0
33    pub ctime: i64,
34    /// 修改时间? UNIX 秒级时间戳, 可能为 0
35    pub mtime: i64,
36    /// 活动名称
37    pub name: String,
38    /// 活动链接
39    pub h5_url: String,
40    /// 活动封面
41    pub h5_cover: String,
42    /// 页面名称
43    pub page_name: String,
44    /// 活动平台类型? 即 URL 中 `plat` 参数
45    pub plat: i32,
46    /// 活动描述
47    pub desc: String,
48}
49
50#[derive(Debug, Clone, Serialize)]
51pub struct ActivityListParams {
52    /// 活动平台类型,可选范围 [1, 3],以半角逗号分隔
53    #[serde(skip_serializing_if = "Option::is_none")]
54    pub plat: Option<String>,
55
56    /// 固定值 0
57    #[serde(skip_serializing_if = "Option::is_none")]
58    pub mold: Option<i32>,
59
60    /// 固定值 3
61    #[serde(skip_serializing_if = "Option::is_none")]
62    pub http: Option<i32>,
63
64    /// 目标页码
65    #[serde(skip_serializing_if = "Option::is_none")]
66    pub pn: Option<i32>,
67
68    /// 每页条数
69    #[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    /// 获取活动列表
87    ///
88    /// # 参数
89    /// | 名称    | 类型   | 说明                                               |
90    /// | ------- | ------ | -------------------------------------------------- |
91    /// | `plat`  | u32    | 活动平台类型,可选范围 ``[1,3]``,以半角逗号分隔,默认 `1,3` |
92    /// | `mold`  | u32    | 固定值 `0` (可选)                                  |
93    /// | `http`  | u32    | 固定值 `3` (可选)                                  |
94    /// | `pn`    | u32    | 目标页码 (可选,默认为 `1`)                        |
95    /// | `ps`    | u32    | 每页条数 (可选,默认为 `15`)                       |
96    ///
97    /// # 文档
98    /// [获取活动列表](https://github.com/Yuelioi/bilibili-API-collect/tree/cfc5fddcc8a94b74d91970bb5b4eaeb349addc47/docs/activity/list.md#获取活动列表)
99
100    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.map(|s| s.to_string()).or_else(|| Some("1,3".to_string())),
110            mold: mold.or(Some(0)),
111            http: http.or(Some(3)),
112            pn: pn.or(Some(1)),
113            ps: ps.or(Some(15)),
114        };
115
116        let result = self
117            .get("https://api.bilibili.com/x/activity/page/list")
118            .query(&params)
119            .send_bpi("获取活动列表").await?;
120
121        Ok(result)
122    }
123
124    /// 获取活动列表(简化版本,使用默认参数)
125    ///
126    /// # 文档
127    /// [获取活动列表](https://github.com/Yuelioi/bilibili-API-collect/tree/cfc5fddcc8a94b74d91970bb5b4eaeb349addc47/docs/activity/list.md#获取活动列表)
128    pub async fn activity_list_default(&self) -> Result<BpiResponse<ActivityListData>, BpiError> {
129        self.activity_list(Some("1,3"), None, None, Some(1), Some(15)).await
130    }
131}
132
133#[cfg(test)]
134mod tests {
135    use super::*;
136
137    #[tokio::test]
138    async fn test_get_activity_list() -> Result<(), Box<BpiError>> {
139        let bpi = BpiClient::new();
140
141        // 测试获取活动列表
142        let result = bpi.activity_list(Some("1,3"), None, None, Some(1), Some(4)).await?;
143        let data = result.into_data()?;
144        tracing::info!("{:#?}", data);
145
146        assert!(!data.list.is_empty());
147        assert_eq!(data.num, 1);
148        assert_eq!(data.size, 4);
149        assert!(data.total > 0);
150
151        Ok(())
152    }
153
154    #[tokio::test]
155    async fn test_get_activity_list_simple() -> Result<(), Box<BpiError>> {
156        let bpi = BpiClient::new();
157
158        // 测试简化版本获取活动列表
159        let result = bpi.activity_list_default().await?;
160        let data = result.into_data()?;
161        tracing::info!("{:#?}", data);
162
163        assert!(!data.list.is_empty());
164        assert_eq!(data.num, 1);
165        assert_eq!(data.size, 15);
166
167        Ok(())
168    }
169
170    #[tokio::test]
171    async fn test_activity_item_fields() -> Result<(), Box<BpiError>> {
172        let bpi = BpiClient::new();
173
174        let result = bpi.activity_list(Some("1,3"), None, None, Some(1), Some(1)).await?;
175        let data = result.into_data()?;
176        tracing::info!("{:#?}", data);
177
178        if let Some(activity) = data.list.first() {
179            assert!(activity.id > 0);
180            assert_eq!(activity.state, 1);
181            assert!(!activity.name.is_empty());
182            assert!(!activity.page_name.is_empty());
183        }
184
185        Ok(())
186    }
187}