bpi-rs 0.1.3

Bilibili API client library for Rust
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
use serde::{ Deserialize, Serialize };
use std::collections::HashMap;

use crate::{ BilibiliRequest, BpiClient, BpiError, BpiResponse };

// --- Structs for `getChargeRecord` ---

/// 充电自动续费详情
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Renew {
    /// 自己的mid
    pub uid: u64,
    /// UP主的mid
    pub ruid: u64,
    /// 充电类型 172:一个月 173:连续包月 174:连续包年
    pub goods_id: u64,
    /// 充电状态 1
    pub status: u8,
    /// 下次续费时间秒级时间戳
    pub next_execute_time: u64,
    /// 签约时间秒级时间戳
    pub signed_time: u64,
    /// 下次续费金额单位为千分之一元人民币
    pub signed_price: u64,
    /// 签约平台 2:微信支付 4:支付宝
    pub pay_channel: u8,
    /// 下次充电天数
    pub period: u64,
    /// 充电渠道
    pub mobile_app: String,
}

/// 充电档位详情
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ChargeItem {
    /// 充电档位代码
    pub privilege_type: u64,
    /// 充电图标
    pub icon: String,
    /// 充电档位名称
    pub name: String,
    /// 该档位过期时间秒级时间戳
    pub expire_time: u64,
    /// 充电自动续费详情
    pub renew: Option<Renew>,
    /// 该档位生效时间秒级时间戳
    pub start_time: u64,
    /// 充电自动续费列表
    pub renew_list: Option<Vec<Renew>>,
}

/// 包月充电UP主
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ChargeUp {
    /// 充电UP主mid
    pub up_uid: u64,
    /// 充电UP主昵称
    pub user_name: String,
    /// 充电UP主头像url
    pub user_face: String,
    /// 充电详情
    pub item: Vec<ChargeItem>,
    /// 开始充电时间秒级时间戳
    pub start: u64,
    /// 是否可对UP主进行高档充电
    pub high_level_state: u8,
    /// 是否可对UP主进行专属问答 0:否 1:是 2:状态未知
    pub elec_reply_state: u8,
}

/// 包月充电列表数据
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ChargeRecordData {
    /// 包月充电UP主列表
    pub list: Option<Vec<ChargeUp>>,
    /// 当前页数
    pub page: u64,
    /// 当前分页大小
    pub page_size: u64,
    /// 总页数
    pub total_page: u64,
    /// 用户总数
    pub total_num: u64,
    /// 是否有更多用户 0:否 1:是
    pub is_more: u8,
}

// --- Structs for `upower/item/detail` ---

/// 充电用户排名
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct UpowerRankUser {
    /// 充电用户索引
    pub rank: u64,
    /// 充电用户mid
    pub mid: u64,
    /// 充电用户昵称
    pub nickname: String,
    /// 充电用户头像url
    pub avatar: String,
}

/// 充电详情
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct UpowerRank {
    /// 充电用户总数
    pub total: u64,
    /// 充电总数文字说明
    pub total_desc: String,
    /// 充电用户列表
    pub list: Vec<UpowerRankUser>,
}

/// 充电介绍
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ItemDetailIntro {
    /// 充电介绍视频AV号
    pub intro_video_aid: String,
    /// 充电介绍语
    pub welcomes: String,
}

/// UP主信息卡片
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct UpUserCard {
    /// UP主头像url
    pub avatar: String,
    /// UP主昵称
    pub nickname: String,
}

/// 不同充电档位下的充电权益数
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct UpowerRightCount {
    #[serde(flatten)]
    pub counts: HashMap<String, u64>,
}

/// 包月充电详情数据
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct UpowerItemDetail {
    /// 充电详情
    pub upower_rank: UpowerRank,
    /// 充电欢迎语信息
    pub item: ItemDetailIntro,
    /// UP主信息
    pub user_card: UpUserCard,
    /// UP主开通的充电等级 1:非高档充电 2:高档充电
    pub upower_level: u8,
    /// 是否可对UP主进行专属问答
    pub elec_reply_state: u8,
    /// 包月充电券信息
    pub voucher_state: serde_json::Value,
    /// 不同充电档位下的充电权益数
    pub upower_right_count: UpowerRightCount,
    /// 享有的权益仅为粉丝勋章
    pub only_contain_medal: bool,
    /// 当前给该UP主包月充电的档位
    pub privilege_type: u64,
}

// --- Structs for `charge/follow/info` ---

/// UP主信息卡片
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct UpCard {
    /// UP主mid
    pub mid: u64,
    /// UP主昵称
    pub nickname: String,
    /// UP主认证信息
    pub official_title: String,
    /// UP主头像url
    pub avatar: String,
}

/// 用户信息卡片
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct UserCard {
    /// 用户头像url
    pub avatar: String,
    /// 用户昵称
    pub nickname: String,
}

/// 与UP主的包月充电关系数据
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ChargeFollowInfo {
    /// 已保持多少天包月充电状态
    pub days: u64,
    /// UP主信息
    pub up_card: UpCard,
    /// 自己的信息
    pub user_card: UserCard,
    /// 剩余天数 未处于包月充电状态为-1
    pub remain_days: i64,
    /// 剩余的天数是否小于1天 0:否 1:是 未处于包月充电状态为0
    pub remain_less_1day: u8,
    /// 充电详情
    pub upower_rank: UpowerRank,
    /// 充电图标url 仅在处于包月充电状态时有内容
    pub upower_icon: String,
    /// 当前自己享有该UP主的充电权益数
    pub upower_right_count: i64,
    /// 享有的权益仅为粉丝勋章
    pub only_contain_medal: bool,
    /// 当前给该UP主包月充电的档位代码
    pub privilege_type: u64,
    /// 充电挑战信息
    pub challenge_info: ChallengeInfo,
}

#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ChallengeInfo {
    pub challenge_id: String,
    pub description: String,
    pub challenge_type: i64,
    pub remaining_days: i64,
    pub end_time: String,
    pub progress: i64,
    pub targets: Vec<serde_json::Value>,
    pub state: i64,
    pub end_time_unix: i64,
    pub pub_dyn: i64,
    pub dyn_content: String,
}

/// UP主信息
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct UpInfo {
    /// UP主mid
    pub mid: u64,
    /// UP主昵称
    pub nickname: String,
    /// UP主头像url
    pub avatar: String,
    /// UP主认证类型
    pub r#type: i32,
    /// UP主认证文字
    pub title: String,
    /// UP主充电功能开启状态
    pub upower_state: u8,
}

/// 充电用户排名
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct RankInfo {
    /// 充电用户mid
    pub mid: u64,
    /// 充电用户昵称
    pub nickname: String,
    /// 充电用户头像url
    pub avatar: String,
    /// 充电用户排名
    pub rank: u64,
    /// 包月充电天数
    pub day: u64,
    /// 包月充电过期时间恒为0
    pub expire_at: u64,
    /// 剩余天数恒为0
    pub remain_days: u64,
}

/// 自己的充电关系信息
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct MemberUserInfo {
    /// 用户mid
    pub mid: u64,
    /// 用户昵称
    pub nickname: String,
    /// 用户头像url
    pub avatar: String,
    /// 包月充电排名
    pub rank: i64,
    /// 包月充电天数
    pub day: u64,
    /// 包月充电过期时间秒级时间戳
    pub expire_at: u64,
    /// 剩余天数
    pub remain_days: u64,
}

/// 充电档位信息
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct LevelInfo {
    /// 充电档位代码
    pub privilege_type: u64,
    /// 档位名称
    pub name: String,
    /// 档位价格单位为百分之一元人民币
    pub price: u64,
    /// 当前档位的用户总数
    pub member_total: u64,
}

/// 包月充电用户排名数据
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct MemberRankData {
    /// UP主信息
    pub up_info: UpInfo,
    /// 当前档位的充电用户排名
    pub rank_info: Vec<RankInfo>,
    /// 自己在该档位下与UP主的充电关系
    pub user_info: MemberUserInfo,
    /// 当前档位充电用户总数
    pub member_total: u64,
    /// 当前充电档位代码
    pub privilege_type: u64,
    /// 自己是否给该UP主包月充电过
    pub is_charge: bool,
    /// 可显示排名的充电档位代码列表
    pub tabs: Vec<u64>,
    /// 可显示排名的充电档位信息
    pub level_info: Vec<LevelInfo>,
}

impl BpiClient {
    /// 获取包月充电列表
    ///
    /// 注意: 此接口需要登录态 (Cookie: SESSDATA)
    ///
    /// # 文档
    /// [查看API文档](https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/electric)
    ///
    /// # 参数
    ///
    /// | 名称 | 类型 | 说明 |
    /// | ---- | ---- | ---- |
    /// | `page` | u64 | 页码 |
    /// | `charge_type` | u32 | 充电状态:1 使用中,2 已过期 |
    pub async fn electric_charge_record(
        &self,
        page: u64,
        charge_type: u32
    ) -> Result<BpiResponse<ChargeRecordData>, BpiError> {
        self
            .get("https://api.live.bilibili.com/xlive/revenue/v1/guard/getChargeRecord")
            .query(
                &[
                    ("page", page.to_string()),
                    ("type", charge_type.to_string()),
                ]
            )
            .send_bpi("获取包月充电列表").await
    }

    /// UP主包月充电详情
    ///
    /// # 文档
    /// [查看API文档](https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/electric)
    ///
    /// # 参数
    ///
    /// | 名称 | 类型 | 说明 |
    /// | ---- | ---- | ---- |
    /// | `up_mid` | u64 | 目标用户 mid |
    pub async fn electric_upower_item_detail(
        &self,
        up_mid: u64
    ) -> Result<BpiResponse<UpowerItemDetail>, BpiError> {
        self
            .get("https://api.bilibili.com/x/upower/item/detail")
            .query(&[("up_mid", up_mid)])
            .send_bpi("获取UP主包月充电详情").await
    }

    /// 与UP主的包月充电关系
    ///
    /// 注意: 此接口需要登录态 (Cookie: SESSDATA)
    ///
    /// # 文档
    /// [查看API文档](https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/electric)
    ///
    /// # 参数
    ///
    /// | 名称 | 类型 | 说明 |
    /// | ---- | ---- | ---- |
    /// | `up_mid` | u64 | 目标用户 mid |
    pub async fn electric_charge_follow_info(
        &self,
        up_mid: u64
    ) -> Result<BpiResponse<ChargeFollowInfo>, BpiError> {
        self
            .get("https://api.bilibili.com/x/upower/charge/follow/info")
            .query(&[("up_mid", up_mid)])
            .send_bpi("获取与UP主的包月充电关系").await
    }

    /// 包月充电用户排名
    ///
    /// 注意: 此接口需要登录态 (Cookie: SESSDATA)
    ///
    /// # 文档
    /// [查看API文档](https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/electric)
    ///
    /// # 参数
    ///
    /// | 名称 | 类型 | 说明 |
    /// | ---- | ---- | ---- |
    /// | `up_mid` | u64 | 目标用户 mid |
    /// | `pn` | u64 | 页码 |
    /// | `ps` | u64 | 每页项数,最大 101 |
    /// | `privilege_type` | `Option<u64>` | 充电档位代码 |
    pub async fn electric_upower_member_rank(
        &self,
        up_mid: u64,
        pn: u64,
        ps: u64,
        privilege_type: Option<u64>
    ) -> Result<BpiResponse<MemberRankData>, BpiError> {
        let mut req = self.get("https://api.bilibili.com/x/upower/up/member/rank/v2").query(
            &[
                ("up_mid", up_mid),
                ("pn", pn),
                ("ps", ps),
            ]
        );

        if let Some(ptype) = privilege_type {
            req = req.query(&[("privilege_type", ptype)]);
        }

        req.send_bpi("获取包月充电用户排名").await
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use tracing::info;

    #[tokio::test]
    async fn test_get_charge_record() {
        let bpi = BpiClient::new();
        // 获取自己使用中的包月充电列表
        let resp = bpi.electric_charge_record(1, 1).await;
        info!("响应: {:?}", resp);
        assert!(resp.is_ok());

        if let Ok(response) = resp {
            if let Some(list) = response.data.unwrap().list {
                info!("找到 {} 个正在充电的UP主", list.len());
            } else {
                info!("没有正在充电的UP主");
            }
        }
    }

    #[tokio::test]
    async fn test_get_upower_item_detail() {
        let bpi = BpiClient::new();
        // 替换为有效的UP主mid
        let up_mid = 1265680561;
        let resp = bpi.electric_upower_item_detail(up_mid).await;
        info!("响应: {:?}", resp);
        assert!(resp.is_ok());

        if let Ok(response) = resp {
            let data = response.data.unwrap();
            info!("UP主 {} 的充电总人数: {}", data.user_card.nickname, data.upower_rank.total);
        }
    }

    #[tokio::test]
    async fn test_get_charge_follow_info() {
        let bpi = BpiClient::new();
        let up_mid = 293793435;
        let resp = bpi.electric_charge_follow_info(up_mid).await;
        info!("响应: {:?}", resp);
        assert!(resp.is_ok());

        if let Ok(response) = resp {
            let data = response.data.unwrap();
            info!("与UP主 {} 的充电关系:已保持 {} 天", data.up_card.nickname, data.days);
        }
    }

    #[tokio::test]
    async fn test_get_upower_member_rank() {
        let bpi = BpiClient::new();
        // 替换为有效的UP主mid
        let up_mid = 1265680561;
        // 获取所有档位的用户排名
        let resp = bpi.electric_upower_member_rank(up_mid, 1, 10, None).await;
        info!("响应: {:?}", resp);
        assert!(resp.is_ok());

        if let Ok(response) = resp {
            let data = response.data.unwrap();

            info!("当前档位充电用户总数: {}", data.member_total);
            if let Some(first_rank) = data.rank_info.first() {
                info!("排名第一的用户: {}", first_rank.nickname);
            }
        }
    }
}