1use serde::{ Deserialize, Serialize };
2
3use crate::{ BilibiliRequest, BpiClient, BpiError, BpiResponse };
4use base64::{ Engine as _, engine::general_purpose };
5
6#[derive(Debug, Serialize, Clone, Deserialize)]
9pub struct HeartBeatData {
10 pub next_interval: i32,
12}
13
14pub type HeartBeatResponse = BpiResponse<HeartBeatData>;
15
16impl BpiClient {
19 pub async fn live_web_heart_beat(
24 &self,
25 room_id: i64,
26 next_interval: Option<i32>,
27 platform: Option<&str>
28 ) -> Result<HeartBeatResponse, BpiError> {
29 let interval = next_interval.unwrap_or(60);
31 let heart_beat_data = format!("{interval}|{room_id}|1|0");
32
33 let encoded_hb = general_purpose::STANDARD.encode(heart_beat_data);
35
36 let mut params: Vec<(&str, String)> = vec![("hb", encoded_hb)];
37
38 if let Some(platform) = platform {
39 params.push(("pf", platform.to_string()));
40 } else {
41 params.push(("pf", "web".to_string()));
42 }
43
44 let resp: HeartBeatResponse = self
45 .get("https://live-trace.bilibili.com/xlive/rdata-interface/v1/heartbeat/webHeartBeat")
46 .query(¶ms)
47 .send_bpi("直播心跳上报").await?;
48
49 Ok(resp)
50 }
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56
57 #[tokio::test]
58 async fn test_web_heart_beat() -> Result<(), Box<BpiError>> {
59 let bpi = BpiClient::new();
60 let resp = bpi.live_web_heart_beat(23174842, None, None).await?;
61
62 let data = resp.data.unwrap();
63
64 assert!(data.next_interval > 0);
65 Ok(())
66 }
67}