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("直播心跳上报")
48 .await?;
49
50 Ok(resp)
51 }
52}
53
54#[cfg(test)]
55mod tests {
56 use super::*;
57
58 #[tokio::test]
59 async fn test_web_heart_beat() -> Result<(), Box<BpiError>> {
60 let bpi = BpiClient::new();
61 let resp = bpi.live_web_heart_beat(23174842, None, None).await?;
62
63 let data = resp.data.unwrap();
64
65 assert!(data.next_interval > 0);
66 Ok(())
67 }
68}