Skip to main content

bpi_rs/user/relation/
group.rs

1//! B站用户分组相关接口
2//!
3//! [查看 API 文档](https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/user)
4use crate::{ BilibiliRequest, BpiClient, BpiError, BpiResponse };
5use serde::{ Deserialize, Serialize };
6
7// --- 响应数据结构体 ---
8
9/// 创建分组响应数据
10#[derive(Debug, Clone, Deserialize, Serialize)]
11pub struct CreateTagResponseData {
12    /// 创建的分组的 ID
13    pub tagid: i64,
14}
15
16// --- API 实现 ---
17
18impl BpiClient {
19    /// 创建分组
20    ///
21    /// # 文档
22    /// [查看API文档](https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/user/relation/group#创建分组)
23    ///
24    /// # 参数
25    /// | 名称      | 类型         | 说明           |
26    /// | --------- | ------------| -------------- |
27    /// | `group_name` | &str      | 分组名,最长16字|
28    pub async fn user_group_create_tag(
29        &self,
30        group_name: &str
31    ) -> Result<BpiResponse<CreateTagResponseData>, BpiError> {
32        let csrf = self.csrf()?;
33        let form = reqwest::multipart::Form
34            ::new()
35            .text("tag", group_name.to_string())
36            .text("csrf", csrf.to_string());
37
38        self
39            .post("https://api.bilibili.com/x/relation/tag/create")
40            .multipart(form)
41            .send_bpi("创建分组").await
42    }
43
44    /// 重命名分组
45    ///
46    /// # 文档
47    /// [查看API文档](https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/user/relation/group#重命名分组)
48    ///
49    /// # 参数
50    /// | 名称      | 类型         | 说明           |
51    /// | --------- | ------------| -------------- |
52    /// | `tag_id`  | i64         | 分组ID         |
53    /// | `new_name`| &str        | 新名称,最长16字|
54    pub async fn user_group_update_tag(
55        &self,
56        tag_id: i64,
57        new_name: &str
58    ) -> Result<BpiResponse<serde_json::Value>, BpiError> {
59        let csrf = self.csrf()?;
60        let form = reqwest::multipart::Form
61            ::new()
62            .text("tagid", tag_id.to_string())
63            .text("name", new_name.to_string())
64            .text("csrf", csrf.to_string());
65
66        self
67            .post("https://api.bilibili.com/x/relation/tag/update")
68            .multipart(form)
69            .send_bpi("重命名分组").await
70    }
71
72    /// 删除分组
73    ///
74    /// # 文档
75    /// [查看API文档](https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/user/relation/group#删除分组)
76    ///
77    /// # 参数
78    /// | 名称      | 类型         | 说明           |
79    /// | --------- | ------------| -------------- |
80    /// | `tag_id`  | i64         | 分组ID         |
81    pub async fn user_group_delete_tag(
82        &self,
83        tag_id: i64
84    ) -> Result<BpiResponse<serde_json::Value>, BpiError> {
85        let csrf = self.csrf()?;
86        let form = reqwest::multipart::Form
87            ::new()
88            .text("tagid", tag_id.to_string())
89            .text("csrf", csrf.to_string());
90
91        self
92            .post("https://api.bilibili.com/x/relation/tag/del")
93            .multipart(form)
94            .send_bpi("删除分组").await
95    }
96
97    /// 修改分组成员(添加)
98    ///
99    /// # 文档
100    /// [查看API文档](https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/user/relation/group#修改分组成员)
101    ///
102    /// # 参数
103    /// | 名称      | 类型         | 说明           |
104    /// | --------- | ------------| -------------- |
105    /// | `fids`    | &`[u64]`      | 目标用户 mid 列表|
106    /// | `tagids`  | &`[i64]`      | 分组ID列表      |
107    pub async fn user_group_add_users_to_tags(
108        &self,
109        fids: &[u64],
110        tagids: &[i64]
111    ) -> Result<BpiResponse<serde_json::Value>, BpiError> {
112        let csrf = self.csrf()?;
113        let fids_str = fids
114            .iter()
115            .map(|id| id.to_string())
116            .collect::<Vec<_>>()
117            .join(",");
118        let tagids_str = tagids
119            .iter()
120            .map(|id| id.to_string())
121            .collect::<Vec<_>>()
122            .join(",");
123
124        let form = reqwest::multipart::Form
125            ::new()
126            .text("fids", fids_str)
127            .text("tagids", tagids_str)
128            .text("csrf", csrf.to_string());
129
130        self
131            .post("https://api.bilibili.com/x/relation/tags/addUsers")
132            .multipart(form)
133            .send_bpi("修改分组成员").await
134    }
135
136    // 修改分组成员(删除)
137    ///
138    /// # 文档
139    /// [查看API文档](https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/user/relation/group#修改分组成员)
140    ///
141    /// # 参数
142    /// | 名称      | 类型         | 说明           |
143    /// | --------- | ------------| -------------- |
144    /// | `fids`    | &`[u64]`      | 目标用户 mid 列表|
145    pub async fn user_group_remove_users_(
146        &self,
147        fids: &[u64]
148    ) -> Result<BpiResponse<serde_json::Value>, BpiError> {
149        let csrf = self.csrf()?;
150        let fids_str = fids
151            .iter()
152            .map(|id| id.to_string())
153            .collect::<Vec<_>>()
154            .join(",");
155
156        let form = reqwest::multipart::Form
157            ::new()
158            .text("fids", fids_str)
159            .text("tagids", "0".to_string())
160            .text("csrf", csrf.to_string());
161
162        self
163            .post("https://api.bilibili.com/x/relation/tags/addUsers")
164            .multipart(form)
165            .send_bpi("修改分组成员").await
166    }
167
168    /// 复制关注到分组
169    ///
170    /// # 文档
171    /// [查看API文档](https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/user/relation/group#复制关注到分组)
172    ///
173    /// # 参数
174    /// | 名称      | 类型         | 说明           |
175    /// | --------- | ------------| -------------- |
176    /// | `fids`    | &`[u64]`      | 用户 mid 列表   |
177    /// | `tagids`  | &`[i64]`      | 目标分组ID列表  |
178    pub async fn user_group_copy_users_to_tags(
179        &self,
180        fids: &[u64],
181        tagids: &[i64]
182    ) -> Result<BpiResponse<serde_json::Value>, BpiError> {
183        let csrf = self.csrf()?;
184        let fids_str = fids
185            .iter()
186            .map(|id| id.to_string())
187            .collect::<Vec<_>>()
188            .join(",");
189        let tagids_str = tagids
190            .iter()
191            .map(|id| id.to_string())
192            .collect::<Vec<_>>()
193            .join(",");
194
195        let form = reqwest::multipart::Form
196            ::new()
197            .text("fids", fids_str)
198            .text("tagids", tagids_str)
199            .text("csrf", csrf.to_string());
200
201        self
202            .post("https://api.bilibili.com/x/relation/tags/copyUsers")
203            .multipart(form)
204            .send_bpi("复制关注到分组").await
205    }
206
207    /// 移动关注到分组
208    ///
209    /// # 文档
210    /// [查看API文档](https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/user/relation/group#移动关注到分组)
211    ///
212    /// # 参数
213    /// | 名称            | 类型         | 说明           |
214    /// | --------------- | ------------| -------------- |
215    /// | `fids`          | &`[u64]`      | 用户 mid 列表   |
216    /// | `before_tag_ids`| &`[i64]`      | 原分组ID列表    |
217    /// | `after_tag_ids` | &`[i64]`      | 新分组ID列表    |
218    pub async fn user_group_move_users_to_tags(
219        &self,
220        fids: &[u64],
221        before_tag_ids: &[i64],
222        after_tag_ids: &[i64]
223    ) -> Result<BpiResponse<serde_json::Value>, BpiError> {
224        let csrf = self.csrf()?;
225        let fids_str = fids
226            .iter()
227            .map(|id| id.to_string())
228            .collect::<Vec<_>>()
229            .join(",");
230        let before_tag_ids_str = before_tag_ids
231            .iter()
232            .map(|id| id.to_string())
233            .collect::<Vec<_>>()
234            .join(",");
235        let after_tag_ids_str = after_tag_ids
236            .iter()
237            .map(|id| id.to_string())
238            .collect::<Vec<_>>()
239            .join(",");
240
241        let form = reqwest::multipart::Form
242            ::new()
243            .text("fids", fids_str)
244            .text("beforeTagids", before_tag_ids_str)
245            .text("afterTagids", after_tag_ids_str)
246            .text("csrf", csrf.to_string());
247
248        self
249            .post("https://api.bilibili.com/x/relation/tags/moveUsers")
250            .multipart(form)
251            .send_bpi("移动关注到分组").await
252    }
253}
254
255// --- 测试模块 ---
256
257#[cfg(test)]
258mod tests {
259    use super::*;
260    use tracing::info;
261
262    const TEST_FID: u64 = 107997089;
263
264    const DEFAULT_GROUP_FID: u64 = 3493257409464519;
265
266    #[tokio::test]
267    async fn test_tag_operations() -> Result<(), BpiError> {
268        let bpi = BpiClient::new();
269        let test_tag_name = "测试分组1";
270        let new_tag_name = "新测试分组2";
271
272        // 1. 创建分组
273        info!("正在创建分组...");
274        let create_resp = bpi.user_group_create_tag(&test_tag_name).await?;
275        let tag_id = create_resp.into_data()?.tagid;
276        info!("创建分组成功,ID: {}", tag_id);
277
278        // 2. 重命名分组
279        info!("正在重命名分组...");
280        let update_resp = bpi.user_group_update_tag(tag_id, &new_tag_name).await?;
281        info!("重命名分组成功");
282        assert_eq!(update_resp.code, 0);
283
284        // 3. 修改分组成员
285        info!("正在将用户添加到默认分组...");
286        let add_resp = bpi.user_group_add_users_to_tags(&[TEST_FID], &[-10]).await?;
287        info!("添加用户到默认分组成功");
288        assert_eq!(add_resp.code, 0);
289
290        // 5. 移动关注到分组
291        // 假设存在一个默认分组(tagid=-10)
292        info!("正在将用户从默认分组移动到新分组...");
293        let move_resp = bpi.user_group_move_users_to_tags(
294            &[DEFAULT_GROUP_FID],
295            &[-10],
296            &[tag_id]
297        ).await?;
298        info!("移动用户到分组成功");
299        assert_eq!(move_resp.code, 0);
300
301        // 4. 复制关注到分组
302        info!("正在将用户复制到分组...");
303        let copy_resp = bpi.user_group_copy_users_to_tags(&[TEST_FID], &[tag_id]).await?;
304        info!("复制用户到分组成功");
305        assert_eq!(copy_resp.code, 0);
306
307        // 6. 删除分组
308        info!("正在删除分组...");
309        let delete_resp = bpi.user_group_delete_tag(tag_id).await?;
310        info!("删除分组成功");
311        assert_eq!(delete_resp.code, 0);
312
313        Ok(())
314    }
315}