bpi_rs/electric/
charge_msg.rs

1use chrono::NaiveDate;
2use serde::{Deserialize, Serialize};
3
4use crate::{BilibiliRequest, BpiClient, BpiError, BpiResponse};
5
6/// 发送充电留言的请求体
7#[derive(Debug, Clone, Serialize)]
8pub struct SendElecMessageBody<'a> {
9    pub order_id: &'a str,
10    pub message: &'a str,
11    pub csrf: &'a str,
12}
13
14/// 充电留言列表分页信息
15#[derive(Debug, Clone, Deserialize, Serialize)]
16pub struct ElecRemarkPager {
17    /// 当前页数
18    pub current: u64,
19    /// 当前分页大小
20    pub size: u64,
21    /// 记录总数
22    pub total: u64,
23}
24
25/// 充电留言列表中的单条留言
26#[derive(Debug, Clone, Deserialize, Serialize)]
27pub struct ElecRemarkRecord {
28    pub aid: u64,
29    pub bvid: String,
30    pub id: u64,
31    pub mid: u64,
32    pub reply_mid: u64,
33    pub elec_num: u64,
34    /// UP是否已经回复这条留言 0: 未回复 1: 已回复
35    pub state: u8,
36    /// 留言信息
37    pub msg: String,
38    pub aname: String,
39    pub uname: String,
40    pub avator: String,
41    pub reply_name: String,
42    pub reply_avator: String,
43    pub reply_msg: String,
44    /// 留言时间毫秒级时间戳
45    pub ctime: u64,
46    pub reply_time: u64,
47}
48
49/// 充电留言列表数据
50#[derive(Debug, Clone, Deserialize, Serialize)]
51pub struct ElecRemarkList {
52    pub list: Vec<ElecRemarkRecord>,
53    pub pager: ElecRemarkPager,
54}
55
56/// 充电留言详情数据
57#[derive(Debug, Clone, Deserialize, Serialize)]
58pub struct ElecRemarkDetail {
59    pub aid: u64,
60    pub bvid: String,
61    pub id: u64,
62    /// 留言者mid(充电用户)
63    pub mid: u64,
64    /// UP主mid
65    pub reply_mid: u64,
66    pub elec_num: u64,
67    /// UP是否已经回复这条留言 0: 未回复 1: 已回复
68    pub state: u8,
69    /// 留言内容
70    pub msg: String,
71    pub aname: String,
72    /// 留言者用户名
73    pub uname: String,
74    /// 留言者头像
75    pub avator: String,
76    /// UP主用户名
77    pub reply_name: String,
78    /// UP主头像
79    pub reply_avator: String,
80    /// 回复内容
81    pub reply_msg: String,
82    /// 留言时间毫秒级时间戳
83    pub ctime: u64,
84    /// 回复时间毫秒级时间戳
85    pub reply_time: u64,
86}
87
88impl BpiClient {
89    /// 发送充电留言
90    ///
91    /// 注意: 此接口需要登录态 (Cookie: SESSDATA)
92    ///
93    /// 文档: https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/electric
94    ///
95    /// 参数
96    ///
97    /// | 名称 | 类型 | 说明 |
98    /// | ---- | ---- | ---- |
99    /// | `order_id` | &str | 留言 token |
100    /// | `message` | &str | 留言内容 |
101    pub async fn electric_message_send(
102        &self,
103        order_id: &str,
104        message: &str,
105    ) -> Result<BpiResponse<serde_json::Value>, BpiError> {
106        let csrf = self.csrf()?;
107
108        let body = [
109            ("order_id", order_id),
110            ("message", message),
111            ("csrf", &csrf),
112        ];
113
114        self.post("https://api.bilibili.com/x/ugcpay/trade/elec/message")
115            .form(&body)
116            .send_bpi("发送充电留言")
117            .await
118    }
119
120    /// 查询我收到的充电留言
121    /// GET https://member.bilibili.com/x/web/elec/remark/list
122    ///
123    /// 注意: 此接口需要登录态 (Cookie: SESSDATA)
124    ///
125    /// 文档: https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/electric
126    ///
127    /// 参数
128    ///
129    /// | 名称 | 类型 | 说明 |
130    /// | ---- | ---- | ---- |
131    /// | `pn` | Option<u64> | 页数,默认 1 |
132    /// | `ps` | Option<u64> | 分页大小,默认 10,范围 [1,12] |
133    /// | `begin` | Option<NaiveDate> | 开始日期 YYYY-MM-DD |
134    /// | `end` | Option<NaiveDate> | 结束日期 YYYY-MM-DD |
135    pub async fn electric_remark_list(
136        &self,
137        pn: Option<u64>,
138        ps: Option<u64>,
139        begin: Option<NaiveDate>,
140        end: Option<NaiveDate>,
141    ) -> Result<BpiResponse<ElecRemarkList>, BpiError> {
142        let mut req = self.get("https://member.bilibili.com/x/web/elec/remark/list");
143
144        if let Some(page) = pn {
145            req = req.query(&[("pn", page)]);
146        }
147        if let Some(size) = ps {
148            req = req.query(&[("ps", size)]);
149        }
150        if let Some(begin_date) = begin {
151            req = req.query(&[("begin", begin_date.format("%Y-%m-%d").to_string())]);
152        }
153        if let Some(end_date) = end {
154            req = req.query(&[("end", end_date.format("%Y-%m-%d").to_string())]);
155        }
156
157        req.send_bpi("查询收到的充电留言").await
158    }
159
160    /// 查询充电留言详情
161    /// GET https://member.bilibili.com/x/web/elec/remark/detail
162    ///
163    /// 注意: 此接口需要登录态 (Cookie: SESSDATA)
164    ///
165    /// 文档: https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/electric
166    ///
167    /// 参数
168    ///
169    /// | 名称 | 类型 | 说明 |
170    /// | ---- | ---- | ---- |
171    /// | `id` | u64 | 留言 id |
172    pub async fn electric_remark_detail(
173        &self,
174        id: u64,
175    ) -> Result<BpiResponse<ElecRemarkDetail>, BpiError> {
176        self.get("https://member.bilibili.com/x/web/elec/remark/detail")
177            .query(&[("id", id)])
178            .send_bpi("查询充电留言详情")
179            .await
180    }
181
182    /// 回复充电留言
183    /// POST https://member.bilibili.com/x/web/elec/remark/reply
184    ///
185    /// 注意: 此接口需要登录态 (Cookie: SESSDATA)
186    ///
187    /// 文档: https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/electric
188    ///
189    /// 参数
190    ///
191    /// | 名称 | 类型 | 说明 |
192    /// | ---- | ---- | ---- |
193    /// | `id` | u64 | 留言 id |
194    /// | `msg` | &str | 回复内容 |
195    pub async fn electric_remark_reply(
196        &self,
197        id: u64,
198        msg: &str,
199    ) -> Result<BpiResponse<u64>, BpiError> {
200        let csrf = self.csrf()?;
201
202        let body = [
203            ("id", id.to_string()),
204            ("msg", msg.to_string()),
205            ("csrf", csrf.to_string()),
206        ];
207
208        self.post("https://member.bilibili.com/x/web/elec/remark/reply")
209            .form(&body)
210            .send_bpi("回复充电留言")
211            .await
212    }
213}
214
215#[cfg(test)]
216mod tests {
217    use super::*;
218    use tracing::info;
219
220    #[tokio::test]
221    /// 未测试
222    async fn test_send_elec_message() {
223        let bpi = BpiClient::new();
224        // 替换为有效的 order_id 和留言
225        let resp = bpi.electric_message_send("ORDER_ID_HERE", "测试留言").await;
226        info!("响应: {:?}", resp);
227        assert!(resp.is_ok());
228    }
229
230    #[tokio::test]
231    async fn test_get_elec_remark_list() {
232        let bpi = BpiClient::new();
233        let resp = bpi
234            .electric_remark_list(Some(1), Some(10), None, None)
235            .await;
236        info!("响应: {:?}", resp);
237        assert!(resp.is_ok());
238
239        if let Ok(response) = resp {
240            let data = response.data.unwrap();
241            info!("留言总记录数: {}", data.pager.total);
242            info!("当前页留言记录数: {}", data.list.len());
243        }
244    }
245
246    #[tokio::test]
247    async fn test_get_elec_remark_detail() {
248        let bpi = BpiClient::new();
249        // 替换为有效的留言id
250        let resp = bpi.electric_remark_detail(6507563).await;
251        info!("响应: {:?}", resp);
252        assert!(resp.is_ok());
253    }
254
255    #[tokio::test]
256    async fn test_reply_elec_remark() {
257        let bpi = BpiClient::new();
258        // 替换为有效的留言id和回复内容
259        let resp = bpi.electric_remark_reply(6507563, "测试回复").await;
260        info!("响应: {:?}", resp);
261        assert!(resp.is_ok());
262    }
263}