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, 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;
}
pub fn is_logined(&self) -> bool {
!self.api_key.is_empty()
}
pub async fn info(&self) -> Result<UserInfo, Error> {
let mut resp = get(&format!("api/user?apiKey={}", &self.api_key)).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(&format!("users/emotions?apiKey={}", &self.api_key)).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(&format!("user/liveness?apiKey={}", &self.api_key)).await?;
let liveness = resp["liveness"].as_u64().unwrap_or(0) as u32;
Ok(liveness)
}
pub async fn is_checkin(&self) -> Result<bool, Error> {
let resp = get(&format!("user/isCheckin?apiKey={}", &self.api_key)).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(&format!(
"api/activity/is-collected-liveness?apiKey={}",
&self.api_key
))
.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(&format!(
"activity/yesterday-liveness-reward-api?apiKey={}",
&self.api_key
))
.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 = format!("user/{}?apiKey={}", username, self.api_key);
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)
}
}