bpi_rs/user/relation/
group.rs1use crate::{ BilibiliRequest, BpiClient, BpiError, BpiResponse };
5use serde::{ Deserialize, Serialize };
6
7#[derive(Debug, Clone, Deserialize, Serialize)]
11pub struct CreateTagResponseData {
12 pub tagid: i64,
14}
15
16impl BpiClient {
19 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 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 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 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 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 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 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#[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 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 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 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 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 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 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}