use crate::api::article::Article;
use crate::api::breezemoon::BreezeMoon;
use crate::api::chat::Chat;
use crate::api::chatroom::ChatRoom;
use crate::api::comment::Comment;
use crate::api::notice::Notice;
use crate::api::redpacket::Redpacket;
use crate::model::misc::{Report, UploadResult};
use crate::model::user::{UpdateUserInfoParams, UserInfo, UserPoint};
use crate::utils::error::Error;
use crate::utils::{ResponseResult, build_http_path, get, post, upload_files};
use serde_json::{Value, json};
pub struct User {
api_key: String,
pub chatroom: ChatRoom,
pub chat: Chat,
pub breezemoon: BreezeMoon,
pub article: Article,
pub notice: Notice,
pub redpacket: Redpacket,
pub comment: Comment,
}
impl User {
pub fn new(api_key: String) -> Self {
Self {
api_key: api_key.clone(),
chatroom: ChatRoom::new(api_key.clone()),
chat: Chat::new(api_key.clone()),
breezemoon: BreezeMoon::new(api_key.clone()),
article: Article::new(api_key.clone()),
notice: Notice::new(api_key.clone()),
redpacket: Redpacket::new(api_key.clone()),
comment: Comment::new(api_key.clone()),
}
}
pub fn get_token(&self) -> &str {
&self.api_key
}
pub fn set_token(&mut self, token: String) {
self.api_key = token.clone();
self.chatroom.set_api_key(token.clone());
self.chat = Chat::new(token.clone());
self.breezemoon = BreezeMoon::new(token.clone());
self.article = Article::new(token.clone());
self.notice = Notice::new(token.clone());
self.redpacket = Redpacket::new(token.clone());
self.comment = Comment::new(token);
}
pub fn is_logined(&self) -> bool {
!self.api_key.is_empty()
}
pub async fn info(&self) -> Result<UserInfo, Error> {
let mut resp = get(&build_http_path("api/user", &[("apiKey", self.api_key.clone())])).await?;
if resp["code"] != 0 {
return Err(Error::Api(
resp["msg"].as_str().unwrap_or("API error").to_string(),
));
}
let data_value = if let Some(data_str) = resp["data"].as_str() {
serde_json::from_str(data_str).map_err(|e| Error::Api(e.to_string()))?
} else {
resp["data"].take()
};
UserInfo::from_value(&data_value)
}
pub async fn emotions(&self) -> Result<Vec<String>, Error> {
let mut resp = get(&build_http_path("users/emotions", &[("apiKey", self.api_key.clone())])).await?;
if resp["code"] != 0 {
return Err(Error::Api(
resp["msg"].as_str().unwrap_or("API error").to_string(),
));
}
let data: Vec<Value> =
serde_json::from_value(resp["data"].take()).map_err(|e| Error::Parse(format!("Failed to parse emotions: {}", e)))?;
let emotions: Vec<String> = data
.into_iter()
.filter_map(|v| {
v.as_object()
.and_then(|obj| obj.values().next())
.and_then(|val| val.as_str())
.map(|s| s.to_string())
})
.collect();
Ok(emotions)
}
pub async fn liveness(&self) -> Result<u32, Error> {
let resp = get(&build_http_path("user/liveness", &[("apiKey", self.api_key.clone())])).await?;
let liveness_raw = resp
.get("liveness")
.and_then(|v| {
v.as_f64()
.or_else(|| v.as_u64().map(|n| n as f64))
.or_else(|| v.as_i64().map(|n| n as f64))
.or_else(|| v.as_str().and_then(|s| s.parse::<f64>().ok()))
})
.ok_or_else(|| Error::Api("Missing or invalid liveness".to_string()))?;
Ok(liveness_raw.max(0.0).round() as u32)
}
pub async fn is_checkin(&self) -> Result<bool, Error> {
let resp = get(&build_http_path("user/isCheckin", &[("apiKey", self.api_key.clone())])).await?;
let is_checkin: bool = resp["isCheckin"].as_bool().unwrap_or(false);
Ok(is_checkin)
}
pub async fn is_collected_liveness(&self) -> Result<bool, Error> {
let resp = get(&build_http_path(
"api/activity/is-collected-liveness",
&[("apiKey", self.api_key.clone())],
))
.await?;
let is_rewarded: bool = resp["isLivenessRewarded"].as_bool().unwrap_or(false);
Ok(is_rewarded)
}
pub async fn reward_liveness(&self) -> Result<u32, Error> {
let resp = get(&build_http_path(
"activity/yesterday-liveness-reward-api",
&[("apiKey", self.api_key.clone())],
))
.await?;
let success: u32 = resp["sum"].as_u64().unwrap_or(0) as u32;
Ok(success)
}
pub async fn transfer(&self, username: &str, amount: u32, memo: &str) -> Result<bool, Error> {
let data = json!({
"username": username,
"amount": amount,
"memo": memo,
"apiKey": self.api_key,
});
let resp = post("point/transfer", Some(data)).await?;
if resp["code"] != 0 {
return Err(Error::Api(
resp["msg"].as_str().unwrap_or("API error").to_string(),
));
}
Ok(true)
}
pub async fn follow(&self, following_id: &str) -> Result<bool, Error> {
let data = json!({
"followingId": following_id,
"apiKey": self.api_key,
});
let resp = post("follow/user", Some(data)).await?;
if resp["code"] != 0 {
return Err(Error::Api(
resp["msg"].as_str().unwrap_or("API error").to_string(),
));
}
Ok(true)
}
pub async fn unfollow(&self, following_id: &str) -> Result<bool, Error> {
let data = json!({
"followingId": following_id,
"apiKey": self.api_key,
});
let resp = post("unfollow/user", Some(data)).await?;
if resp["code"] != 0 {
return Err(Error::Api(
resp["msg"].as_str().unwrap_or("API error").to_string(),
));
}
Ok(true)
}
pub async fn update_avatar(&self, avatar_url: &str) -> Result<bool, Error> {
let data = json!({
"userAvatarURL": avatar_url,
"apiKey": self.api_key
});
let resp = post("api/settings/avatar", Some(data)).await?;
if resp["code"] != 0 {
return Err(Error::Api(
resp["msg"].as_str().unwrap_or("API error").to_string(),
));
}
Ok(true)
}
pub async fn update_user_info(&self, params: UpdateUserInfoParams) -> Result<bool, Error> {
let data = json!({
"userNickname": params.nickName,
"userURL": params.userUrl,
"userIntro": params.userIntro,
"userTag": params.userTag,
"apiKey": self.api_key,
});
let resp = post("api/settings/profiles", Some(data)).await?;
if resp["code"] != 0 {
return Err(Error::Api(
resp["msg"].as_str().unwrap_or("API error").to_string(),
));
}
Ok(true)
}
pub async fn get_user(&self, username: &str) -> Result<UserInfo, Error> {
let url = build_http_path(
&format!("user/{}", username),
&[("apiKey", self.api_key.clone())],
);
let rsp = get(&url).await?;
if rsp.get("code").and_then(|c| c.as_i64()).unwrap_or(0) != 0 {
return Err(Error::Api(
rsp["msg"].as_str().unwrap_or("API error").to_string(),
));
}
UserInfo::from_value(&rsp)
}
pub async fn report(&self, data: &Report) -> Result<ResponseResult, Error> {
let url = "report".to_string();
let mut data_json = serde_json::to_value(data)
.map_err(|e| Error::Parse(format!("Failed to serialize Report: {}", e)))?;
data_json["apiKey"] = Value::String(self.api_key.clone());
let rsp = post(&url, Some(data_json)).await?;
ResponseResult::from_value(&rsp)
}
pub async fn upload(&self, files: Vec<String>) -> Result<UploadResult, Error> {
for file in &files {
if !std::path::Path::new(file).exists() {
return Err(Error::Api(format!("File not exist: {}", file)));
}
}
let url = "upload".to_string();
let rsp = upload_files(&url, files, &self.api_key).await?;
if rsp.get("code").and_then(|c| c.as_i64()).unwrap_or(-1) != 0 {
return Err(Error::Api(
rsp["msg"].as_str().unwrap_or("API error").to_string(),
));
}
UploadResult::from_value(&rsp["data"])
}
pub async fn get_points(&self, username: &str) -> Result<UserPoint, Error> {
let resp = get(&format!("user/{}/point", username)).await?;
if resp.get("code").and_then(|c| c.as_i64()).unwrap_or(-1) != 0 {
return Err(Error::Api(
resp["msg"].as_str().unwrap_or("API error").to_string(),
));
}
UserPoint::from_value(&resp)
}
}