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> {
97 self
98 .get("https://api.vc.bilibili.com/x/im/web/msgfeed/unread")
99 .query(
100 &[
101 ("build", "0"),
102 ("mobi_app", "web"),
103 ]
104 )
105 .send_bpi("获取未读消息数").await
106 }
107
108 pub async fn message_reply_feed(
120 &self,
121 start_id: Option<u64>,
122 start_time: Option<u64>
123 ) -> Result<BpiResponse<ReplyFeedData>, BpiError> {
124 let mut params = HashMap::new();
125 params.insert("build", "0".to_string());
126 params.insert("mobi_app", "web".to_string());
127 params.insert("platform", "web".to_string());
128 params.insert("web_location", "".to_string());
129
130 if let Some(id) = start_id {
131 params.insert("id", id.to_string());
132 }
133 if let Some(time) = start_time {
134 params.insert("reply_time", time.to_string());
135 }
136
137 self
138 .get("https://api.bilibili.com/x/msgfeed/reply")
139 .query(¶ms)
140 .send_bpi("获取回复我的信息").await
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147
148 #[tokio::test]
149 async fn test_get_unread_count() -> Result<(), BpiError> {
150 let bpi = BpiClient::new();
151
152 let new_resp = bpi.message_unread_count().await?;
153 let new_data = new_resp.into_data()?;
154 println!("未读消息数 (新接口): {:?}", new_data);
155 Ok(())
156 }
157
158 #[tokio::test]
159 async fn test_get_reply_feed() -> Result<(), BpiError> {
160 let bpi = BpiClient::new();
161
162 let resp = bpi.message_reply_feed(None, None).await?;
163 let data = resp.into_data()?;
164
165 println!("最近回复我的信息:");
166 println!(" 上次查看时间: {}", data.last_view_at);
167 println!(" 游标信息: {:?}", data.cursor);
168
169 for item in data.items {
170 println!("---");
171 println!(" 回复者: {}", item.user.nickname);
172 println!(" 回复内容: {}", item.item.source_content);
173 println!(" 回复时间: {}", item.reply_time);
174 println!(" 关联视频/动态: {}", item.item.title);
175 println!(" 根评论: {}", item.item.root_reply_content);
176 println!(" 跳转链接: {}", item.item.uri);
177 }
178
179 if !data.cursor.is_end {
180 println!("---");
181 println!(
182 "还有更多数据,下次请求可使用 id: {:?}, time: {:?}",
183 data.cursor.id,
184 data.cursor.time
185 );
186 }
187
188 Ok(())
189 }
190}