1use serde::{ Deserialize, Serialize };
2use std::collections::HashMap;
3
4use crate::{ BilibiliRequest, BpiClient, BpiError, BpiResponse };
5
6#[derive(Debug, Clone, Deserialize, Serialize)]
10pub struct Renew {
11 pub uid: u64,
13 pub ruid: u64,
15 pub goods_id: u64,
17 pub status: u8,
19 pub next_execute_time: u64,
21 pub signed_time: u64,
23 pub signed_price: u64,
25 pub pay_channel: u8,
27 pub period: u64,
29 pub mobile_app: String,
31}
32
33#[derive(Debug, Clone, Deserialize, Serialize)]
35pub struct ChargeItem {
36 pub privilege_type: u64,
38 pub icon: String,
40 pub name: String,
42 pub expire_time: u64,
44 pub renew: Option<Renew>,
46 pub start_time: u64,
48 pub renew_list: Option<Vec<Renew>>,
50}
51
52#[derive(Debug, Clone, Deserialize, Serialize)]
54pub struct ChargeUp {
55 pub up_uid: u64,
57 pub user_name: String,
59 pub user_face: String,
61 pub item: Vec<ChargeItem>,
63 pub start: u64,
65 pub high_level_state: u8,
67 pub elec_reply_state: u8,
69}
70
71#[derive(Debug, Clone, Deserialize, Serialize)]
73pub struct ChargeRecordData {
74 pub list: Option<Vec<ChargeUp>>,
76 pub page: u64,
78 pub page_size: u64,
80 pub total_page: u64,
82 pub total_num: u64,
84 pub is_more: u8,
86}
87
88#[derive(Debug, Clone, Deserialize, Serialize)]
92pub struct UpowerRankUser {
93 pub rank: u64,
95 pub mid: u64,
97 pub nickname: String,
99 pub avatar: String,
101}
102
103#[derive(Debug, Clone, Deserialize, Serialize)]
105pub struct UpowerRank {
106 pub total: u64,
108 pub total_desc: String,
110 pub list: Vec<UpowerRankUser>,
112}
113
114#[derive(Debug, Clone, Deserialize, Serialize)]
116pub struct ItemDetailIntro {
117 pub intro_video_aid: String,
119 pub welcomes: String,
121}
122
123#[derive(Debug, Clone, Deserialize, Serialize)]
125pub struct UpUserCard {
126 pub avatar: String,
128 pub nickname: String,
130}
131
132#[derive(Debug, Clone, Deserialize, Serialize)]
134pub struct UpowerRightCount {
135 #[serde(flatten)]
136 pub counts: HashMap<String, u64>,
137}
138
139#[derive(Debug, Clone, Deserialize, Serialize)]
141pub struct UpowerItemDetail {
142 pub upower_rank: UpowerRank,
144 pub item: ItemDetailIntro,
146 pub user_card: UpUserCard,
148 pub upower_level: u8,
150 pub elec_reply_state: u8,
152 pub voucher_state: serde_json::Value,
154 pub upower_right_count: UpowerRightCount,
156 pub only_contain_medal: bool,
158 pub privilege_type: u64,
160}
161
162#[derive(Debug, Clone, Deserialize, Serialize)]
166pub struct UpCard {
167 pub mid: u64,
169 pub nickname: String,
171 pub official_title: String,
173 pub avatar: String,
175}
176
177#[derive(Debug, Clone, Deserialize, Serialize)]
179pub struct UserCard {
180 pub avatar: String,
182 pub nickname: String,
184}
185
186#[derive(Debug, Clone, Deserialize, Serialize)]
188pub struct ChargeFollowInfo {
189 pub days: u64,
191 pub up_card: UpCard,
193 pub user_card: UserCard,
195 pub remain_days: i64,
197 pub remain_less_1day: u8,
199 pub upower_rank: UpowerRank,
201 pub upower_icon: String,
203 pub upower_right_count: i64,
205 pub only_contain_medal: bool,
207 pub privilege_type: u64,
209 pub challenge_info: ChallengeInfo,
211}
212
213#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
214pub struct ChallengeInfo {
215 pub challenge_id: String,
216 pub description: String,
217 pub challenge_type: i64,
218 pub remaining_days: i64,
219 pub end_time: String,
220 pub progress: i64,
221 pub targets: Vec<serde_json::Value>,
222 pub state: i64,
223 pub end_time_unix: i64,
224 pub pub_dyn: i64,
225 pub dyn_content: String,
226}
227
228#[derive(Debug, Clone, Deserialize, Serialize)]
230pub struct UpInfo {
231 pub mid: u64,
233 pub nickname: String,
235 pub avatar: String,
237 pub r#type: i32,
239 pub title: String,
241 pub upower_state: u8,
243}
244
245#[derive(Debug, Clone, Deserialize, Serialize)]
247pub struct RankInfo {
248 pub mid: u64,
250 pub nickname: String,
252 pub avatar: String,
254 pub rank: u64,
256 pub day: u64,
258 pub expire_at: u64,
260 pub remain_days: u64,
262}
263
264#[derive(Debug, Clone, Deserialize, Serialize)]
266pub struct MemberUserInfo {
267 pub mid: u64,
269 pub nickname: String,
271 pub avatar: String,
273 pub rank: i64,
275 pub day: u64,
277 pub expire_at: u64,
279 pub remain_days: u64,
281}
282
283#[derive(Debug, Clone, Deserialize, Serialize)]
285pub struct LevelInfo {
286 pub privilege_type: u64,
288 pub name: String,
290 pub price: u64,
292 pub member_total: u64,
294}
295
296#[derive(Debug, Clone, Deserialize, Serialize)]
298pub struct MemberRankData {
299 pub up_info: UpInfo,
301 pub rank_info: Vec<RankInfo>,
303 pub user_info: MemberUserInfo,
305 pub member_total: u64,
307 pub privilege_type: u64,
309 pub is_charge: bool,
311 pub tabs: Vec<u64>,
313 pub level_info: Vec<LevelInfo>,
315}
316
317impl BpiClient {
318 pub async fn electric_charge_record(
332 &self,
333 page: u64,
334 charge_type: u32
335 ) -> Result<BpiResponse<ChargeRecordData>, BpiError> {
336 self
337 .get("https://api.live.bilibili.com/xlive/revenue/v1/guard/getChargeRecord")
338 .query(
339 &[
340 ("page", page.to_string()),
341 ("type", charge_type.to_string()),
342 ]
343 )
344 .send_bpi("获取包月充电列表").await
345 }
346
347 pub async fn electric_upower_item_detail(
358 &self,
359 up_mid: u64
360 ) -> Result<BpiResponse<UpowerItemDetail>, BpiError> {
361 self
362 .get("https://api.bilibili.com/x/upower/item/detail")
363 .query(&[("up_mid", up_mid)])
364 .send_bpi("获取UP主包月充电详情").await
365 }
366
367 pub async fn electric_charge_follow_info(
380 &self,
381 up_mid: u64
382 ) -> Result<BpiResponse<ChargeFollowInfo>, BpiError> {
383 self
384 .get("https://api.bilibili.com/x/upower/charge/follow/info")
385 .query(&[("up_mid", up_mid)])
386 .send_bpi("获取与UP主的包月充电关系").await
387 }
388
389 pub async fn electric_upower_member_rank(
405 &self,
406 up_mid: u64,
407 pn: u64,
408 ps: u64,
409 privilege_type: Option<u64>
410 ) -> Result<BpiResponse<MemberRankData>, BpiError> {
411 let mut req = self.get("https://api.bilibili.com/x/upower/up/member/rank/v2").query(
412 &[
413 ("up_mid", up_mid),
414 ("pn", pn),
415 ("ps", ps),
416 ]
417 );
418
419 if let Some(ptype) = privilege_type {
420 req = req.query(&[("privilege_type", ptype)]);
421 }
422
423 req.send_bpi("获取包月充电用户排名").await
424 }
425}
426
427#[cfg(test)]
428mod tests {
429 use super::*;
430 use tracing::info;
431
432 #[tokio::test]
433 async fn test_get_charge_record() {
434 let bpi = BpiClient::new();
435 let resp = bpi.electric_charge_record(1, 1).await;
437 info!("响应: {:?}", resp);
438 assert!(resp.is_ok());
439
440 if let Ok(response) = resp {
441 if let Some(list) = response.data.unwrap().list {
442 info!("找到 {} 个正在充电的UP主", list.len());
443 } else {
444 info!("没有正在充电的UP主");
445 }
446 }
447 }
448
449 #[tokio::test]
450 async fn test_get_upower_item_detail() {
451 let bpi = BpiClient::new();
452 let up_mid = 1265680561;
454 let resp = bpi.electric_upower_item_detail(up_mid).await;
455 info!("响应: {:?}", resp);
456 assert!(resp.is_ok());
457
458 if let Ok(response) = resp {
459 let data = response.data.unwrap();
460 info!("UP主 {} 的充电总人数: {}", data.user_card.nickname, data.upower_rank.total);
461 }
462 }
463
464 #[tokio::test]
465 async fn test_get_charge_follow_info() {
466 let bpi = BpiClient::new();
467 let up_mid = 293793435;
468 let resp = bpi.electric_charge_follow_info(up_mid).await;
469 info!("响应: {:?}", resp);
470 assert!(resp.is_ok());
471
472 if let Ok(response) = resp {
473 let data = response.data.unwrap();
474 info!("与UP主 {} 的充电关系:已保持 {} 天", data.up_card.nickname, data.days);
475 }
476 }
477
478 #[tokio::test]
479 async fn test_get_upower_member_rank() {
480 let bpi = BpiClient::new();
481 let up_mid = 1265680561;
483 let resp = bpi.electric_upower_member_rank(up_mid, 1, 10, None).await;
485 info!("响应: {:?}", resp);
486 assert!(resp.is_ok());
487
488 if let Ok(response) = resp {
489 let data = response.data.unwrap();
490
491 info!("当前档位充电用户总数: {}", data.member_total);
492 if let Some(first_rank) = data.rank_info.first() {
493 info!("排名第一的用户: {}", first_rank.nickname);
494 }
495 }
496 }
497}