1use crate::{BilibiliRequest, BpiClient, BpiError, BpiResponse};
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4
5#[derive(Debug, Clone, Deserialize, Serialize)]
9pub struct UnreadCountData {
10 pub coin: u32, pub danmu: u32, pub favorite: u32, pub recv_like: u32, pub recv_reply: u32, pub sys_msg: u32, pub up: u32, }
18
19#[derive(Debug, Clone, Deserialize, Serialize)]
21pub struct ReplyFeedData {
22 pub cursor: ReplyCursor,
23 pub items: Vec<ReplyItem>,
24 pub last_view_at: u64,
25}
26
27#[derive(Debug, Clone, Deserialize, Serialize)]
29pub struct ReplyCursor {
30 pub is_end: bool,
31 pub id: Option<u64>,
32 pub time: Option<u64>,
33}
34
35#[derive(Debug, Clone, Deserialize, Serialize)]
37pub struct ReplyItem {
38 pub id: u64,
39 pub user: ReplyUser,
40 pub item: ReplyDetail,
41 pub counts: u32,
42 pub is_multi: u32,
43 pub reply_time: u64,
44}
45
46#[derive(Debug, Clone, Deserialize, Serialize)]
48pub struct ReplyUser {
49 pub mid: u64,
50 pub nickname: String,
51 pub avatar: String,
52 pub follow: bool,
53 pub fans: Option<u32>,
55 pub mid_link: Option<String>,
56}
57
58#[derive(Debug, Clone, Deserialize, Serialize)]
60pub struct ReplyDetail {
61 pub subject_id: u64,
62 pub root_id: u64,
63 pub source_id: u64,
64 pub target_id: u64,
65 #[serde(rename = "type")]
66 pub reply_type: String,
67 pub business_id: u32,
68 pub business: String,
69 pub title: String,
70 pub desc: String,
71 pub uri: String,
72 pub native_uri: String,
73 pub root_reply_content: String,
74 pub source_content: String,
75 pub target_reply_content: String,
76 pub at_details: Vec<AtUserDetail>,
77 pub hide_reply_button: bool,
78 pub hide_like_button: bool,
79 pub like_state: u32,
80}
81
82#[derive(Debug, Clone, Deserialize, Serialize)]
84pub struct AtUserDetail {
85 pub mid: u64,
86 pub nickname: String,
87 pub avatar: String,
88 pub follow: bool,
89}
90
91impl BpiClient {
92 pub async fn message_unread_count(&self) -> Result<BpiResponse<UnreadCountData>, BpiError> {
96 self.get("https://api.vc.bilibili.com/x/im/web/msgfeed/unread")
97 .query(&[("build", "0"), ("mobi_app", "web")])
98 .send_bpi("获取未读消息数")
99 .await
100 }
101
102 pub async fn message_reply_feed(
113 &self,
114 start_id: Option<u64>,
115 start_time: Option<u64>,
116 ) -> Result<BpiResponse<ReplyFeedData>, BpiError> {
117 let mut params = HashMap::new();
118 params.insert("build", "0".to_string());
119 params.insert("mobi_app", "web".to_string());
120 params.insert("platform", "web".to_string());
121 params.insert("web_location", "".to_string());
122
123 if let Some(id) = start_id {
124 params.insert("id", id.to_string());
125 }
126 if let Some(time) = start_time {
127 params.insert("reply_time", time.to_string());
128 }
129
130 self.get("https://api.bilibili.com/x/msgfeed/reply")
131 .query(¶ms)
132 .send_bpi("获取回复我的信息")
133 .await
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140
141 #[tokio::test]
142
143 async fn test_get_unread_count() -> Result<(), BpiError> {
144 let bpi = BpiClient::new();
145
146 let new_resp = bpi.message_unread_count().await?;
147 let new_data = new_resp.into_data()?;
148 println!("未读消息数 (新接口): {:?}", new_data);
149 Ok(())
150 }
151
152 #[tokio::test]
153
154 async fn test_get_reply_feed() -> Result<(), BpiError> {
155 let bpi = BpiClient::new();
156
157 let resp = bpi.message_reply_feed(None, None).await?;
158 let data = resp.into_data()?;
159
160 println!("最近回复我的信息:");
161 println!(" 上次查看时间: {}", data.last_view_at);
162 println!(" 游标信息: {:?}", data.cursor);
163
164 for item in data.items {
165 println!("---");
166 println!(" 回复者: {}", item.user.nickname);
167 println!(" 回复内容: {}", item.item.source_content);
168 println!(" 回复时间: {}", item.reply_time);
169 println!(" 关联视频/动态: {}", item.item.title);
170 println!(" 根评论: {}", item.item.root_reply_content);
171 println!(" 跳转链接: {}", item.item.uri);
172 }
173
174 if !data.cursor.is_end {
175 println!("---");
176 println!(
177 "还有更多数据,下次请求可使用 id: {:?}, time: {:?}",
178 data.cursor.id, data.cursor.time
179 );
180 }
181
182 Ok(())
183 }
184}