1use crate::api::article::Article;
69use crate::api::breezemoon::BreezeMoon;
70use crate::api::chat::Chat;
71use crate::api::chatroom::ChatRoom;
72use crate::api::comment::Comment;
73use crate::api::notice::Notice;
74use crate::api::redpacket::Redpacket;
75use crate::model::misc::{Report, UploadResult};
76use crate::model::user::{UpdateUserInfoParams, UserInfo, UserPoint};
77use crate::utils::error::Error;
78use crate::utils::{ResponseResult, build_http_path, get, post, upload_files};
79use serde_json::{Value, json};
80
81pub struct User {
82 api_key: String,
83 pub chatroom: ChatRoom,
84 pub chat: Chat,
85 pub breezemoon: BreezeMoon,
86 pub article: Article,
87 pub notice: Notice,
88 pub redpacket: Redpacket,
89 pub comment: Comment,
90}
91
92impl User {
93 pub fn new(api_key: String) -> Self {
94 Self {
95 api_key: api_key.clone(),
96 chatroom: ChatRoom::new(api_key.clone()),
97 chat: Chat::new(api_key.clone()),
98 breezemoon: BreezeMoon::new(api_key.clone()),
99 article: Article::new(api_key.clone()),
100 notice: Notice::new(api_key.clone()),
101 redpacket: Redpacket::new(api_key.clone()),
102 comment: Comment::new(api_key.clone()),
103 }
104 }
105
106 pub fn get_token(&self) -> &str {
107 &self.api_key
108 }
109
110 pub fn set_token(&mut self, token: String) {
112 self.api_key = token.clone();
113 self.chatroom.set_api_key(token.clone());
114 self.chat = Chat::new(token.clone());
115 self.breezemoon = BreezeMoon::new(token.clone());
116 self.article = Article::new(token.clone());
117 self.notice = Notice::new(token.clone());
118 self.redpacket = Redpacket::new(token.clone());
119 self.comment = Comment::new(token);
120 }
121
122 pub fn is_logined(&self) -> bool {
123 !self.api_key.is_empty()
124 }
125
126 pub async fn info(&self) -> Result<UserInfo, Error> {
128 let mut resp = get(&build_http_path("api/user", &[("apiKey", self.api_key.clone())])).await?;
129
130 if resp["code"] != 0 {
131 return Err(Error::Api(
132 resp["msg"].as_str().unwrap_or("API error").to_string(),
133 ));
134 }
135
136 let data_value = if let Some(data_str) = resp["data"].as_str() {
137 serde_json::from_str(data_str).map_err(|e| Error::Api(e.to_string()))?
138 } else {
139 resp["data"].take()
140 };
141
142 UserInfo::from_value(&data_value)
143 }
144
145 pub async fn emotions(&self) -> Result<Vec<String>, Error> {
147 let mut resp = get(&build_http_path("users/emotions", &[("apiKey", self.api_key.clone())])).await?;
148
149 if resp["code"] != 0 {
150 return Err(Error::Api(
151 resp["msg"].as_str().unwrap_or("API error").to_string(),
152 ));
153 }
154
155 let data: Vec<Value> =
156 serde_json::from_value(resp["data"].take()).map_err(|e| Error::Parse(format!("Failed to parse emotions: {}", e)))?;
157 let emotions: Vec<String> = data
158 .into_iter()
159 .filter_map(|v| {
160 v.as_object()
161 .and_then(|obj| obj.values().next())
162 .and_then(|val| val.as_str())
163 .map(|s| s.to_string())
164 })
165 .collect();
166 Ok(emotions)
167 }
168
169 pub async fn liveness(&self) -> Result<u32, Error> {
171 let resp = get(&build_http_path("user/liveness", &[("apiKey", self.api_key.clone())])).await?;
172
173 let liveness_raw = resp
174 .get("liveness")
175 .and_then(|v| {
176 v.as_f64()
177 .or_else(|| v.as_u64().map(|n| n as f64))
178 .or_else(|| v.as_i64().map(|n| n as f64))
179 .or_else(|| v.as_str().and_then(|s| s.parse::<f64>().ok()))
180 })
181 .ok_or_else(|| Error::Api("Missing or invalid liveness".to_string()))?;
182
183 Ok(liveness_raw.max(0.0).round() as u32)
184 }
185
186 pub async fn is_checkin(&self) -> Result<bool, Error> {
188 let resp = get(&build_http_path("user/isCheckin", &[("apiKey", self.api_key.clone())])).await?;
189
190 let is_checkin: bool = resp["isCheckin"].as_bool().unwrap_or(false);
191 Ok(is_checkin)
192 }
193
194 pub async fn is_collected_liveness(&self) -> Result<bool, Error> {
196 let resp = get(&build_http_path(
197 "api/activity/is-collected-liveness",
198 &[("apiKey", self.api_key.clone())],
199 ))
200 .await?;
201
202 let is_rewarded: bool = resp["isLivenessRewarded"].as_bool().unwrap_or(false);
203 Ok(is_rewarded)
204 }
205
206 pub async fn reward_liveness(&self) -> Result<u32, Error> {
208 let resp = get(&build_http_path(
209 "activity/yesterday-liveness-reward-api",
210 &[("apiKey", self.api_key.clone())],
211 ))
212 .await?;
213
214 let success: u32 = resp["sum"].as_u64().unwrap_or(0) as u32;
215 Ok(success)
216 }
217
218 pub async fn transfer(&self, username: &str, amount: u32, memo: &str) -> Result<bool, Error> {
220 let data = json!({
221 "username": username,
222 "amount": amount,
223 "memo": memo,
224 "apiKey": self.api_key,
225 });
226
227 let resp = post("point/transfer", Some(data)).await?;
228
229 if resp["code"] != 0 {
230 return Err(Error::Api(
231 resp["msg"].as_str().unwrap_or("API error").to_string(),
232 ));
233 }
234
235 Ok(true)
236 }
237
238 pub async fn follow(&self, following_id: &str) -> Result<bool, Error> {
240 let data = json!({
241 "followingId": following_id,
242 "apiKey": self.api_key,
243 });
244
245 let resp = post("follow/user", Some(data)).await?;
246
247 if resp["code"] != 0 {
248 return Err(Error::Api(
249 resp["msg"].as_str().unwrap_or("API error").to_string(),
250 ));
251 }
252
253 Ok(true)
254 }
255
256 pub async fn unfollow(&self, following_id: &str) -> Result<bool, Error> {
258 let data = json!({
259 "followingId": following_id,
260 "apiKey": self.api_key,
261 });
262
263 let resp = post("unfollow/user", Some(data)).await?;
264
265 if resp["code"] != 0 {
266 return Err(Error::Api(
267 resp["msg"].as_str().unwrap_or("API error").to_string(),
268 ));
269 }
270
271 Ok(true)
272 }
273
274 pub async fn update_avatar(&self, avatar_url: &str) -> Result<bool, Error> {
276 let data = json!({
277 "userAvatarURL": avatar_url,
278 "apiKey": self.api_key
279 });
280
281 let resp = post("api/settings/avatar", Some(data)).await?;
282
283 if resp["code"] != 0 {
284 return Err(Error::Api(
285 resp["msg"].as_str().unwrap_or("API error").to_string(),
286 ));
287 }
288
289 Ok(true)
290 }
291
292 pub async fn update_user_info(&self, params: UpdateUserInfoParams) -> Result<bool, Error> {
297 let data = json!({
298 "userNickname": params.nickName,
299 "userURL": params.userUrl,
300 "userIntro": params.userIntro,
301 "userTag": params.userTag,
302 "apiKey": self.api_key,
303 });
304
305 let resp = post("api/settings/profiles", Some(data)).await?;
306
307 if resp["code"] != 0 {
308 return Err(Error::Api(
309 resp["msg"].as_str().unwrap_or("API error").to_string(),
310 ));
311 }
312
313 Ok(true)
314 }
315
316 pub async fn get_user(&self, username: &str) -> Result<UserInfo, Error> {
322 let url = build_http_path(
323 &format!("user/{}", username),
324 &[("apiKey", self.api_key.clone())],
325 );
326
327 let rsp = get(&url).await?;
328
329 if rsp.get("code").and_then(|c| c.as_i64()).unwrap_or(0) != 0 {
330 return Err(Error::Api(
331 rsp["msg"].as_str().unwrap_or("API error").to_string(),
332 ));
333 }
334
335 UserInfo::from_value(&rsp)
336 }
337
338 pub async fn report(&self, data: &Report) -> Result<ResponseResult, Error> {
344 let url = "report".to_string();
345
346 let mut data_json = serde_json::to_value(data)
347 .map_err(|e| Error::Parse(format!("Failed to serialize Report: {}", e)))?;
348 data_json["apiKey"] = Value::String(self.api_key.clone());
349
350 let rsp = post(&url, Some(data_json)).await?;
351
352 ResponseResult::from_value(&rsp)
353 }
354
355 pub async fn upload(&self, files: Vec<String>) -> Result<UploadResult, Error> {
361 for file in &files {
363 if !std::path::Path::new(file).exists() {
364 return Err(Error::Api(format!("File not exist: {}", file)));
365 }
366 }
367
368 let url = "upload".to_string();
369 let rsp = upload_files(&url, files, &self.api_key).await?;
370
371 if rsp.get("code").and_then(|c| c.as_i64()).unwrap_or(-1) != 0 {
372 return Err(Error::Api(
373 rsp["msg"].as_str().unwrap_or("API error").to_string(),
374 ));
375 }
376
377 UploadResult::from_value(&rsp["data"])
378 }
379
380 pub async fn get_points(&self, username: &str) -> Result<UserPoint, Error> {
385 let resp = get(&format!("user/{}/point", username)).await?;
386
387 if resp.get("code").and_then(|c| c.as_i64()).unwrap_or(-1) != 0 {
388 return Err(Error::Api(
389 resp["msg"].as_str().unwrap_or("API error").to_string(),
390 ));
391 }
392
393 UserPoint::from_value(&resp)
394 }
395}