bpi_rs/electric/
monthly.rs

1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4use crate::{BilibiliRequest, BpiClient, BpiError, BpiResponse};
5
6// --- Structs for `getChargeRecord` ---
7
8/// 充电自动续费详情
9#[derive(Debug, Clone, Deserialize, Serialize)]
10
11pub struct Renew {
12    /// 自己的mid
13    pub uid: u64,
14    /// UP主的mid
15    pub ruid: u64,
16    /// 充电类型 172:一个月 173:连续包月 174:连续包年
17    pub goods_id: u64,
18    /// 充电状态 1
19    pub status: u8,
20    /// 下次续费时间秒级时间戳
21    pub next_execute_time: u64,
22    /// 签约时间秒级时间戳
23    pub signed_time: u64,
24    /// 下次续费金额单位为千分之一元人民币
25    pub signed_price: u64,
26    /// 签约平台 2:微信支付 4:支付宝
27    pub pay_channel: u8,
28    /// 下次充电天数
29    pub period: u64,
30    /// 充电渠道
31    pub mobile_app: String,
32}
33
34/// 充电档位详情
35#[derive(Debug, Clone, Deserialize, Serialize)]
36pub struct ChargeItem {
37    /// 充电档位代码
38    pub privilege_type: u64,
39    /// 充电图标
40    pub icon: String,
41    /// 充电档位名称
42    pub name: String,
43    /// 该档位过期时间秒级时间戳
44    pub expire_time: u64,
45    /// 充电自动续费详情
46    pub renew: Option<Renew>,
47    /// 该档位生效时间秒级时间戳
48    pub start_time: u64,
49    /// 充电自动续费列表
50    pub renew_list: Option<Vec<Renew>>,
51}
52
53/// 包月充电UP主
54#[derive(Debug, Clone, Deserialize, Serialize)]
55pub struct ChargeUp {
56    /// 充电UP主mid
57    pub up_uid: u64,
58    /// 充电UP主昵称
59    pub user_name: String,
60    /// 充电UP主头像url
61    pub user_face: String,
62    /// 充电详情
63    pub item: Vec<ChargeItem>,
64    /// 开始充电时间秒级时间戳
65    pub start: u64,
66    /// 是否可对UP主进行高档充电
67    pub high_level_state: u8,
68    /// 是否可对UP主进行专属问答 0:否 1:是 2:状态未知
69    pub elec_reply_state: u8,
70}
71
72/// 包月充电列表数据
73#[derive(Debug, Clone, Deserialize, Serialize)]
74pub struct ChargeRecordData {
75    /// 包月充电UP主列表
76    pub list: Option<Vec<ChargeUp>>,
77    /// 当前页数
78    pub page: u64,
79    /// 当前分页大小
80    pub page_size: u64,
81    /// 总页数
82    pub total_page: u64,
83    /// 用户总数
84    pub total_num: u64,
85    /// 是否有更多用户 0:否 1:是
86    pub is_more: u8,
87}
88
89// --- Structs for `upower/item/detail` ---
90
91/// 充电用户排名
92#[derive(Debug, Clone, Deserialize, Serialize)]
93
94pub struct UpowerRankUser {
95    /// 充电用户索引
96    pub rank: u64,
97    /// 充电用户mid
98    pub mid: u64,
99    /// 充电用户昵称
100    pub nickname: String,
101    /// 充电用户头像url
102    pub avatar: String,
103}
104
105/// 充电详情
106#[derive(Debug, Clone, Deserialize, Serialize)]
107pub struct UpowerRank {
108    /// 充电用户总数
109    pub total: u64,
110    /// 充电总数文字说明
111    pub total_desc: String,
112    /// 充电用户列表
113    pub list: Vec<UpowerRankUser>,
114}
115
116/// 充电介绍
117#[derive(Debug, Clone, Deserialize, Serialize)]
118pub struct ItemDetailIntro {
119    /// 充电介绍视频AV号
120    pub intro_video_aid: String,
121    /// 充电介绍语
122    pub welcomes: String,
123}
124
125/// UP主信息卡片
126#[derive(Debug, Clone, Deserialize, Serialize)]
127pub struct UpUserCard {
128    /// UP主头像url
129    pub avatar: String,
130    /// UP主昵称
131    pub nickname: String,
132}
133
134/// 不同充电档位下的充电权益数
135#[derive(Debug, Clone, Deserialize, Serialize)]
136pub struct UpowerRightCount {
137    #[serde(flatten)]
138    pub counts: HashMap<String, u64>,
139}
140
141/// 包月充电详情数据
142#[derive(Debug, Clone, Deserialize, Serialize)]
143pub struct UpowerItemDetail {
144    /// 充电详情
145    pub upower_rank: UpowerRank,
146    /// 充电欢迎语信息
147    pub item: ItemDetailIntro,
148    /// UP主信息
149    pub user_card: UpUserCard,
150    /// UP主开通的充电等级 1:非高档充电 2:高档充电
151    pub upower_level: u8,
152    /// 是否可对UP主进行专属问答
153    pub elec_reply_state: u8,
154    /// 包月充电券信息
155    pub voucher_state: serde_json::Value,
156    /// 不同充电档位下的充电权益数
157    pub upower_right_count: UpowerRightCount,
158    /// 享有的权益仅为粉丝勋章
159    pub only_contain_medal: bool,
160    /// 当前给该UP主包月充电的档位
161    pub privilege_type: u64,
162}
163
164// --- Structs for `charge/follow/info` ---
165
166/// UP主信息卡片
167#[derive(Debug, Clone, Deserialize, Serialize)]
168
169pub struct UpCard {
170    /// UP主mid
171    pub mid: u64,
172    /// UP主昵称
173    pub nickname: String,
174    /// UP主认证信息
175    pub official_title: String,
176    /// UP主头像url
177    pub avatar: String,
178}
179
180/// 用户信息卡片
181#[derive(Debug, Clone, Deserialize, Serialize)]
182
183pub struct UserCard {
184    /// 用户头像url
185    pub avatar: String,
186    /// 用户昵称
187    pub nickname: String,
188}
189
190/// 与UP主的包月充电关系数据
191#[derive(Debug, Clone, Deserialize, Serialize)]
192
193pub struct ChargeFollowInfo {
194    /// 已保持多少天包月充电状态
195    pub days: u64,
196    /// UP主信息
197    pub up_card: UpCard,
198    /// 自己的信息
199    pub user_card: UserCard,
200    /// 剩余天数 未处于包月充电状态为-1
201    pub remain_days: i64,
202    /// 剩余的天数是否小于1天 0:否 1:是 未处于包月充电状态为0
203    pub remain_less_1day: u8,
204    /// 充电详情
205    pub upower_rank: UpowerRank,
206    /// 充电图标url 仅在处于包月充电状态时有内容
207    pub upower_icon: String,
208    /// 当前自己享有该UP主的充电权益数
209    pub upower_right_count: i64,
210    /// 享有的权益仅为粉丝勋章
211    pub only_contain_medal: bool,
212    /// 当前给该UP主包月充电的档位代码
213    pub privilege_type: u64,
214    /// 充电挑战信息
215    pub challenge_info: ChallengeInfo,
216}
217
218#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
219pub struct ChallengeInfo {
220    pub challenge_id: String,
221    pub description: String,
222    pub challenge_type: i64,
223    pub remaining_days: i64,
224    pub end_time: String,
225    pub progress: i64,
226    pub targets: Vec<serde_json::Value>,
227    pub state: i64,
228    pub end_time_unix: i64,
229    pub pub_dyn: i64,
230    pub dyn_content: String,
231}
232
233/// UP主信息
234#[derive(Debug, Clone, Deserialize, Serialize)]
235
236pub struct UpInfo {
237    /// UP主mid
238    pub mid: u64,
239    /// UP主昵称
240    pub nickname: String,
241    /// UP主头像url
242    pub avatar: String,
243    /// UP主认证类型
244    pub r#type: i32,
245    /// UP主认证文字
246    pub title: String,
247    /// UP主充电功能开启状态
248    pub upower_state: u8,
249}
250
251/// 充电用户排名
252#[derive(Debug, Clone, Deserialize, Serialize)]
253
254pub struct RankInfo {
255    /// 充电用户mid
256    pub mid: u64,
257    /// 充电用户昵称
258    pub nickname: String,
259    /// 充电用户头像url
260    pub avatar: String,
261    /// 充电用户排名
262    pub rank: u64,
263    /// 包月充电天数
264    pub day: u64,
265    /// 包月充电过期时间恒为0
266    pub expire_at: u64,
267    /// 剩余天数恒为0
268    pub remain_days: u64,
269}
270
271/// 自己的充电关系信息
272#[derive(Debug, Clone, Deserialize, Serialize)]
273
274pub struct MemberUserInfo {
275    /// 用户mid
276    pub mid: u64,
277    /// 用户昵称
278    pub nickname: String,
279    /// 用户头像url
280    pub avatar: String,
281    /// 包月充电排名
282    pub rank: i64,
283    /// 包月充电天数
284    pub day: u64,
285    /// 包月充电过期时间秒级时间戳
286    pub expire_at: u64,
287    /// 剩余天数
288    pub remain_days: u64,
289}
290
291/// 充电档位信息
292#[derive(Debug, Clone, Deserialize, Serialize)]
293
294pub struct LevelInfo {
295    /// 充电档位代码
296    pub privilege_type: u64,
297    /// 档位名称
298    pub name: String,
299    /// 档位价格单位为百分之一元人民币
300    pub price: u64,
301    /// 当前档位的用户总数
302    pub member_total: u64,
303}
304
305/// 包月充电用户排名数据
306#[derive(Debug, Clone, Deserialize, Serialize)]
307
308pub struct MemberRankData {
309    /// UP主信息
310    pub up_info: UpInfo,
311    /// 当前档位的充电用户排名
312    pub rank_info: Vec<RankInfo>,
313    /// 自己在该档位下与UP主的充电关系
314    pub user_info: MemberUserInfo,
315    /// 当前档位充电用户总数
316    pub member_total: u64,
317    /// 当前充电档位代码
318    pub privilege_type: u64,
319    /// 自己是否给该UP主包月充电过
320    pub is_charge: bool,
321    /// 可显示排名的充电档位代码列表
322    pub tabs: Vec<u64>,
323    /// 可显示排名的充电档位信息
324    pub level_info: Vec<LevelInfo>,
325}
326
327impl BpiClient {
328    /// 获取包月充电列表
329    ///
330    /// 注意: 此接口需要登录态 (Cookie: SESSDATA)
331    ///
332    /// 文档: https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/electric
333    ///
334    /// 参数
335    ///
336    /// | 名称 | 类型 | 说明 |
337    /// | ---- | ---- | ---- |
338    /// | `page` | u64 | 页码 |
339    /// | `charge_type` | u32 | 充电状态:1 使用中,2 已过期 |
340    pub async fn electric_charge_record(
341        &self,
342        page: u64,
343        charge_type: u32,
344    ) -> Result<BpiResponse<ChargeRecordData>, BpiError> {
345        self.get("https://api.live.bilibili.com/xlive/revenue/v1/guard/getChargeRecord")
346            .query(&[
347                ("page", page.to_string()),
348                ("type", charge_type.to_string()),
349            ])
350            .send_bpi("获取包月充电列表")
351            .await
352    }
353
354    /// UP主包月充电详情
355    ///
356    /// 文档: https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/electric
357    ///
358    /// 参数
359    ///
360    /// | 名称 | 类型 | 说明 |
361    /// | ---- | ---- | ---- |
362    /// | `up_mid` | u64 | 目标用户 mid |
363    pub async fn electric_upower_item_detail(
364        &self,
365        up_mid: u64,
366    ) -> Result<BpiResponse<UpowerItemDetail>, BpiError> {
367        self.get("https://api.bilibili.com/x/upower/item/detail")
368            .query(&[("up_mid", up_mid)])
369            .send_bpi("获取UP主包月充电详情")
370            .await
371    }
372
373    /// 与UP主的包月充电关系
374    ///
375    /// 注意: 此接口需要登录态 (Cookie: SESSDATA)
376    ///
377    /// 文档: https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/electric
378    ///
379    /// 参数
380    ///
381    /// | 名称 | 类型 | 说明 |
382    /// | ---- | ---- | ---- |
383    /// | `up_mid` | u64 | 目标用户 mid |
384    pub async fn electric_charge_follow_info(
385        &self,
386        up_mid: u64,
387    ) -> Result<BpiResponse<ChargeFollowInfo>, BpiError> {
388        self.get("https://api.bilibili.com/x/upower/charge/follow/info")
389            .query(&[("up_mid", up_mid)])
390            .send_bpi("获取与UP主的包月充电关系")
391            .await
392    }
393
394    /// 包月充电用户排名
395    ///
396    /// 注意: 此接口需要登录态 (Cookie: SESSDATA)
397    ///
398    /// 文档: https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/electric
399    ///
400    /// 参数
401    ///
402    /// | 名称 | 类型 | 说明 |
403    /// | ---- | ---- | ---- |
404    /// | `up_mid` | u64 | 目标用户 mid |
405    /// | `pn` | u64 | 页码 |
406    /// | `ps` | u64 | 每页项数,最大 101 |
407    /// | `privilege_type` | Option<u64> | 充电档位代码 |
408    pub async fn electric_upower_member_rank(
409        &self,
410        up_mid: u64,
411        pn: u64,
412        ps: u64,
413        privilege_type: Option<u64>,
414    ) -> Result<BpiResponse<MemberRankData>, BpiError> {
415        let mut req = self
416            .get("https://api.bilibili.com/x/upower/up/member/rank/v2")
417            .query(&[("up_mid", up_mid), ("pn", pn), ("ps", ps)]);
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        // 获取自己使用中的包月充电列表
436        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        // 替换为有效的UP主mid
453        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!(
461                "UP主 {} 的充电总人数: {}",
462                data.user_card.nickname, data.upower_rank.total
463            );
464        }
465    }
466
467    #[tokio::test]
468    async fn test_get_charge_follow_info() {
469        let bpi = BpiClient::new();
470        let up_mid = 293793435;
471        let resp = bpi.electric_charge_follow_info(up_mid).await;
472        info!("响应: {:?}", resp);
473        assert!(resp.is_ok());
474
475        if let Ok(response) = resp {
476            let data = response.data.unwrap();
477            info!(
478                "与UP主 {} 的充电关系:已保持 {} 天",
479                data.up_card.nickname, data.days
480            );
481        }
482    }
483
484    #[tokio::test]
485    async fn test_get_upower_member_rank() {
486        let bpi = BpiClient::new();
487        // 替换为有效的UP主mid
488        let up_mid = 1265680561;
489        // 获取所有档位的用户排名
490        let resp = bpi.electric_upower_member_rank(up_mid, 1, 10, None).await;
491        info!("响应: {:?}", resp);
492        assert!(resp.is_ok());
493
494        if let Ok(response) = resp {
495            let data = response.data.unwrap();
496
497            info!("当前档位充电用户总数: {}", data.member_total);
498            if let Some(first_rank) = data.rank_info.first() {
499                info!("排名第一的用户: {}", first_rank.nickname);
500            }
501        }
502    }
503}