use serde::{ Deserialize, Serialize };
use std::collections::HashMap;
use crate::{ BilibiliRequest, BpiClient, BpiError, BpiResponse };
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Renew {
pub uid: u64,
pub ruid: u64,
pub goods_id: u64,
pub status: u8,
pub next_execute_time: u64,
pub signed_time: u64,
pub signed_price: u64,
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>>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ChargeUp {
pub up_uid: u64,
pub user_name: String,
pub user_face: String,
pub item: Vec<ChargeItem>,
pub start: u64,
pub high_level_state: u8,
pub elec_reply_state: u8,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ChargeRecordData {
pub list: Option<Vec<ChargeUp>>,
pub page: u64,
pub page_size: u64,
pub total_page: u64,
pub total_num: u64,
pub is_more: u8,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct UpowerRankUser {
pub rank: u64,
pub mid: u64,
pub nickname: String,
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 {
pub intro_video_aid: String,
pub welcomes: String,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct UpUserCard {
pub avatar: String,
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,
pub user_card: UpUserCard,
pub upower_level: u8,
pub elec_reply_state: u8,
pub voucher_state: serde_json::Value,
pub upower_right_count: UpowerRightCount,
pub only_contain_medal: bool,
pub privilege_type: u64,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct UpCard {
pub mid: u64,
pub nickname: String,
pub official_title: String,
pub avatar: String,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct UserCard {
pub avatar: String,
pub nickname: String,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ChargeFollowInfo {
pub days: u64,
pub up_card: UpCard,
pub user_card: UserCard,
pub remain_days: i64,
pub remain_less_1day: u8,
pub upower_rank: UpowerRank,
pub upower_icon: String,
pub upower_right_count: i64,
pub only_contain_medal: bool,
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,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct UpInfo {
pub mid: u64,
pub nickname: String,
pub avatar: String,
pub r#type: i32,
pub title: String,
pub upower_state: u8,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct RankInfo {
pub mid: u64,
pub nickname: String,
pub avatar: String,
pub rank: u64,
pub day: u64,
pub expire_at: u64,
pub remain_days: u64,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct MemberUserInfo {
pub mid: u64,
pub nickname: String,
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 {
pub up_info: UpInfo,
pub rank_info: Vec<RankInfo>,
pub user_info: MemberUserInfo,
pub member_total: u64,
pub privilege_type: u64,
pub is_charge: bool,
pub tabs: Vec<u64>,
pub level_info: Vec<LevelInfo>,
}
impl BpiClient {
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
}
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
}
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
}
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();
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();
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);
}
}
}
}