Skip to main content

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)]
10pub struct Renew {
11    /// 自己的mid
12    pub uid: u64,
13    /// UP主的mid
14    pub ruid: u64,
15    /// 充电类型 172:一个月 173:连续包月 174:连续包年
16    pub goods_id: u64,
17    /// 充电状态 1
18    pub status: u8,
19    /// 下次续费时间秒级时间戳
20    pub next_execute_time: u64,
21    /// 签约时间秒级时间戳
22    pub signed_time: u64,
23    /// 下次续费金额单位为千分之一元人民币
24    pub signed_price: u64,
25    /// 签约平台 2:微信支付 4:支付宝
26    pub pay_channel: u8,
27    /// 下次充电天数
28    pub period: u64,
29    /// 充电渠道
30    pub mobile_app: String,
31}
32
33/// 充电档位详情
34#[derive(Debug, Clone, Deserialize, Serialize)]
35pub struct ChargeItem {
36    /// 充电档位代码
37    pub privilege_type: u64,
38    /// 充电图标
39    pub icon: String,
40    /// 充电档位名称
41    pub name: String,
42    /// 该档位过期时间秒级时间戳
43    pub expire_time: u64,
44    /// 充电自动续费详情
45    pub renew: Option<Renew>,
46    /// 该档位生效时间秒级时间戳
47    pub start_time: u64,
48    /// 充电自动续费列表
49    pub renew_list: Option<Vec<Renew>>,
50}
51
52/// 包月充电UP主
53#[derive(Debug, Clone, Deserialize, Serialize)]
54pub struct ChargeUp {
55    /// 充电UP主mid
56    pub up_uid: u64,
57    /// 充电UP主昵称
58    pub user_name: String,
59    /// 充电UP主头像url
60    pub user_face: String,
61    /// 充电详情
62    pub item: Vec<ChargeItem>,
63    /// 开始充电时间秒级时间戳
64    pub start: u64,
65    /// 是否可对UP主进行高档充电
66    pub high_level_state: u8,
67    /// 是否可对UP主进行专属问答 0:否 1:是 2:状态未知
68    pub elec_reply_state: u8,
69}
70
71/// 包月充电列表数据
72#[derive(Debug, Clone, Deserialize, Serialize)]
73pub struct ChargeRecordData {
74    /// 包月充电UP主列表
75    pub list: Option<Vec<ChargeUp>>,
76    /// 当前页数
77    pub page: u64,
78    /// 当前分页大小
79    pub page_size: u64,
80    /// 总页数
81    pub total_page: u64,
82    /// 用户总数
83    pub total_num: u64,
84    /// 是否有更多用户 0:否 1:是
85    pub is_more: u8,
86}
87
88// --- Structs for `upower/item/detail` ---
89
90/// 充电用户排名
91#[derive(Debug, Clone, Deserialize, Serialize)]
92pub struct UpowerRankUser {
93    /// 充电用户索引
94    pub rank: u64,
95    /// 充电用户mid
96    pub mid: u64,
97    /// 充电用户昵称
98    pub nickname: String,
99    /// 充电用户头像url
100    pub avatar: String,
101}
102
103/// 充电详情
104#[derive(Debug, Clone, Deserialize, Serialize)]
105pub struct UpowerRank {
106    /// 充电用户总数
107    pub total: u64,
108    /// 充电总数文字说明
109    pub total_desc: String,
110    /// 充电用户列表
111    pub list: Vec<UpowerRankUser>,
112}
113
114/// 充电介绍
115#[derive(Debug, Clone, Deserialize, Serialize)]
116pub struct ItemDetailIntro {
117    /// 充电介绍视频AV号
118    pub intro_video_aid: String,
119    /// 充电介绍语
120    pub welcomes: String,
121}
122
123/// UP主信息卡片
124#[derive(Debug, Clone, Deserialize, Serialize)]
125pub struct UpUserCard {
126    /// UP主头像url
127    pub avatar: String,
128    /// UP主昵称
129    pub nickname: String,
130}
131
132/// 不同充电档位下的充电权益数
133#[derive(Debug, Clone, Deserialize, Serialize)]
134pub struct UpowerRightCount {
135    #[serde(flatten)]
136    pub counts: HashMap<String, u64>,
137}
138
139/// 包月充电详情数据
140#[derive(Debug, Clone, Deserialize, Serialize)]
141pub struct UpowerItemDetail {
142    /// 充电详情
143    pub upower_rank: UpowerRank,
144    /// 充电欢迎语信息
145    pub item: ItemDetailIntro,
146    /// UP主信息
147    pub user_card: UpUserCard,
148    /// UP主开通的充电等级 1:非高档充电 2:高档充电
149    pub upower_level: u8,
150    /// 是否可对UP主进行专属问答
151    pub elec_reply_state: u8,
152    /// 包月充电券信息
153    pub voucher_state: serde_json::Value,
154    /// 不同充电档位下的充电权益数
155    pub upower_right_count: UpowerRightCount,
156    /// 享有的权益仅为粉丝勋章
157    pub only_contain_medal: bool,
158    /// 当前给该UP主包月充电的档位
159    pub privilege_type: u64,
160}
161
162// --- Structs for `charge/follow/info` ---
163
164/// UP主信息卡片
165#[derive(Debug, Clone, Deserialize, Serialize)]
166pub struct UpCard {
167    /// UP主mid
168    pub mid: u64,
169    /// UP主昵称
170    pub nickname: String,
171    /// UP主认证信息
172    pub official_title: String,
173    /// UP主头像url
174    pub avatar: String,
175}
176
177/// 用户信息卡片
178#[derive(Debug, Clone, Deserialize, Serialize)]
179pub struct UserCard {
180    /// 用户头像url
181    pub avatar: String,
182    /// 用户昵称
183    pub nickname: String,
184}
185
186/// 与UP主的包月充电关系数据
187#[derive(Debug, Clone, Deserialize, Serialize)]
188pub struct ChargeFollowInfo {
189    /// 已保持多少天包月充电状态
190    pub days: u64,
191    /// UP主信息
192    pub up_card: UpCard,
193    /// 自己的信息
194    pub user_card: UserCard,
195    /// 剩余天数 未处于包月充电状态为-1
196    pub remain_days: i64,
197    /// 剩余的天数是否小于1天 0:否 1:是 未处于包月充电状态为0
198    pub remain_less_1day: u8,
199    /// 充电详情
200    pub upower_rank: UpowerRank,
201    /// 充电图标url 仅在处于包月充电状态时有内容
202    pub upower_icon: String,
203    /// 当前自己享有该UP主的充电权益数
204    pub upower_right_count: i64,
205    /// 享有的权益仅为粉丝勋章
206    pub only_contain_medal: bool,
207    /// 当前给该UP主包月充电的档位代码
208    pub privilege_type: u64,
209    /// 充电挑战信息
210    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/// UP主信息
229#[derive(Debug, Clone, Deserialize, Serialize)]
230pub struct UpInfo {
231    /// UP主mid
232    pub mid: u64,
233    /// UP主昵称
234    pub nickname: String,
235    /// UP主头像url
236    pub avatar: String,
237    /// UP主认证类型
238    pub r#type: i32,
239    /// UP主认证文字
240    pub title: String,
241    /// UP主充电功能开启状态
242    pub upower_state: u8,
243}
244
245/// 充电用户排名
246#[derive(Debug, Clone, Deserialize, Serialize)]
247pub struct RankInfo {
248    /// 充电用户mid
249    pub mid: u64,
250    /// 充电用户昵称
251    pub nickname: String,
252    /// 充电用户头像url
253    pub avatar: String,
254    /// 充电用户排名
255    pub rank: u64,
256    /// 包月充电天数
257    pub day: u64,
258    /// 包月充电过期时间恒为0
259    pub expire_at: u64,
260    /// 剩余天数恒为0
261    pub remain_days: u64,
262}
263
264/// 自己的充电关系信息
265#[derive(Debug, Clone, Deserialize, Serialize)]
266pub struct MemberUserInfo {
267    /// 用户mid
268    pub mid: u64,
269    /// 用户昵称
270    pub nickname: String,
271    /// 用户头像url
272    pub avatar: String,
273    /// 包月充电排名
274    pub rank: i64,
275    /// 包月充电天数
276    pub day: u64,
277    /// 包月充电过期时间秒级时间戳
278    pub expire_at: u64,
279    /// 剩余天数
280    pub remain_days: u64,
281}
282
283/// 充电档位信息
284#[derive(Debug, Clone, Deserialize, Serialize)]
285pub struct LevelInfo {
286    /// 充电档位代码
287    pub privilege_type: u64,
288    /// 档位名称
289    pub name: String,
290    /// 档位价格单位为百分之一元人民币
291    pub price: u64,
292    /// 当前档位的用户总数
293    pub member_total: u64,
294}
295
296/// 包月充电用户排名数据
297#[derive(Debug, Clone, Deserialize, Serialize)]
298pub struct MemberRankData {
299    /// UP主信息
300    pub up_info: UpInfo,
301    /// 当前档位的充电用户排名
302    pub rank_info: Vec<RankInfo>,
303    /// 自己在该档位下与UP主的充电关系
304    pub user_info: MemberUserInfo,
305    /// 当前档位充电用户总数
306    pub member_total: u64,
307    /// 当前充电档位代码
308    pub privilege_type: u64,
309    /// 自己是否给该UP主包月充电过
310    pub is_charge: bool,
311    /// 可显示排名的充电档位代码列表
312    pub tabs: Vec<u64>,
313    /// 可显示排名的充电档位信息
314    pub level_info: Vec<LevelInfo>,
315}
316
317impl BpiClient {
318    /// 获取包月充电列表
319    ///
320    /// 注意: 此接口需要登录态 (Cookie: SESSDATA)
321    ///
322    /// # 文档
323    /// [查看API文档](https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/electric)
324    ///
325    /// # 参数
326    ///
327    /// | 名称 | 类型 | 说明 |
328    /// | ---- | ---- | ---- |
329    /// | `page` | u64 | 页码 |
330    /// | `charge_type` | u32 | 充电状态:1 使用中,2 已过期 |
331    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    /// UP主包月充电详情
348    ///
349    /// # 文档
350    /// [查看API文档](https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/electric)
351    ///
352    /// # 参数
353    ///
354    /// | 名称 | 类型 | 说明 |
355    /// | ---- | ---- | ---- |
356    /// | `up_mid` | u64 | 目标用户 mid |
357    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    /// 与UP主的包月充电关系
368    ///
369    /// 注意: 此接口需要登录态 (Cookie: SESSDATA)
370    ///
371    /// # 文档
372    /// [查看API文档](https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/electric)
373    ///
374    /// # 参数
375    ///
376    /// | 名称 | 类型 | 说明 |
377    /// | ---- | ---- | ---- |
378    /// | `up_mid` | u64 | 目标用户 mid |
379    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    /// 包月充电用户排名
390    ///
391    /// 注意: 此接口需要登录态 (Cookie: SESSDATA)
392    ///
393    /// # 文档
394    /// [查看API文档](https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/electric)
395    ///
396    /// # 参数
397    ///
398    /// | 名称 | 类型 | 说明 |
399    /// | ---- | ---- | ---- |
400    /// | `up_mid` | u64 | 目标用户 mid |
401    /// | `pn` | u64 | 页码 |
402    /// | `ps` | u64 | 每页项数,最大 101 |
403    /// | `privilege_type` | `Option<u64>` | 充电档位代码 |
404    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        // 获取自己使用中的包月充电列表
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!("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        // 替换为有效的UP主mid
482        let up_mid = 1265680561;
483        // 获取所有档位的用户排名
484        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}