1pub mod api;
34pub mod model;
35pub mod utils;
36
37use serde_json::{Value, json};
38
39use crate::{
40 api::user::User,
41 model::{
42 misc::{Log, LoginData, PreRegisterInfo, RegisterInfo, UserLite, UserVipInfo},
43 user::AtUser,
44 },
45 utils::{ResponseResult, build_http_path, error::Error, get, post},
46};
47
48pub struct FishPi;
50
51impl FishPi {
52 pub async fn login(data: &LoginData) -> Result<User, Error> {
58 let url = "api/getKey".to_string();
59
60 let data_json = data.to_value()?;
61
62 let rsp = post(&url, Some(data_json)).await?;
63
64 if rsp.get("code").and_then(|c| c.as_i64()).unwrap_or(-1) != 0 {
65 return Err(Error::Api(
66 rsp["msg"].as_str().unwrap_or("API error").to_string(),
67 ));
68 }
69
70 let token = rsp["Key"].as_str().unwrap_or("").trim().to_string();
71
72 Ok(User::new(token))
73 }
74
75 pub async fn pre_register(data: &PreRegisterInfo) -> Result<ResponseResult, Error> {
81 let url = "register".to_string();
82
83 let data_json = serde_json::to_value(data)
84 .map_err(|e| Error::Parse(format!("Failed to serialize PreRegisterInfo: {}", e)))?;
85
86 let rsp = post(&url, Some(data_json)).await?;
87
88 ResponseResult::from_value(&rsp)
89 }
90
91 pub async fn verify(code: &str) -> Result<String, Error> {
97 let url = build_http_path("verify", &[("code", code.to_string())]);
98
99 let rsp = get(&url).await?;
100
101 if rsp.get("code").and_then(|c| c.as_i64()).unwrap_or(-1) != 0 {
102 return Err(Error::Api(
103 rsp["msg"].as_str().unwrap_or("API error").to_string(),
104 ));
105 }
106
107 Ok(rsp["userId"].as_str().unwrap_or("").to_string())
108 }
109
110 pub async fn register(data: &RegisterInfo) -> Result<ResponseResult, Error> {
116 let url = if let Some(r) = &data.r {
117 build_http_path("register2", &[("r", r.to_string())])
118 } else {
119 "register2".to_string()
120 };
121
122 let data_json = serde_json::to_value(data)
123 .map_err(|e| Error::Parse(format!("Failed to serialize RegisterInfo: {}", e)))?;
124
125 let rsp = post(&url, Some(data_json)).await?;
126
127 if let Some(code) = rsp.get("code").and_then(|c| c.as_i64())
128 && code != 0
129 {
130 return Err(Error::Api(
131 rsp["msg"].as_str().unwrap_or("API error").to_string(),
132 ));
133 }
134
135 ResponseResult::from_value(&rsp)
136 }
137
138 pub async fn names(name: &str) -> Result<Vec<AtUser>, Error> {
144 let url = "users/names".to_string();
145
146 let data_json = json!({
147 "name": name,
148 });
149
150 let rsp = post(&url, Some(data_json)).await?;
151
152 if rsp.get("code").and_then(|c| c.as_i64()).unwrap_or(0) != 0 {
153 return Err(Error::Api(
154 rsp["msg"].as_str().unwrap_or("API error").to_string(),
155 ));
156 }
157
158 let at_users = rsp["data"]
159 .as_array()
160 .ok_or_else(|| Error::Api("Data is not an array".to_string()))?
161 .iter()
162 .map(AtUser::from_value)
163 .collect::<Result<Vec<AtUser>, _>>()?;
164
165 Ok(at_users)
166 }
167
168 pub async fn recent_register() -> Result<Vec<UserLite>, Error> {
172 let url = "api/user/recentReg".to_string();
173
174 let rsp = get(&url).await?;
175
176 let user_lites = rsp["data"]
177 .as_array()
178 .ok_or_else(|| Error::Api("Data is not an array".to_string()))?
179 .iter()
180 .map(UserLite::from_value)
181 .collect::<Result<Vec<UserLite>, _>>()?;
182
183 Ok(user_lites)
184 }
185
186 pub async fn vip_info(user_id: &str) -> Result<UserVipInfo, Error> {
192 let url = format!("api/membership/{}", user_id);
193
194 let rsp = get(&url).await?;
195
196 if rsp.get("code").and_then(|c| c.as_i64()).unwrap_or(0) != 0 {
197 return Err(Error::Api(
198 rsp["msg"].as_str().unwrap_or("API error").to_string(),
199 ));
200 }
201
202 let data_obj = rsp["data"]
203 .as_object()
204 .ok_or_else(|| Error::Api("Data is not an object".to_string()))?;
205
206 let config_json_str = data_obj
207 .get("configJson")
208 .and_then(|v| v.as_str())
209 .unwrap_or("null");
210 let mut data: Value = serde_json::from_str(config_json_str).unwrap_or_else(|_| json!({}));
211
212 if let Some(data_map) = data.as_object_mut() {
213 data_map.insert(
214 "state".to_string(),
215 data_obj.get("state").cloned().unwrap_or(Value::Null),
216 );
217 if data_obj.get("state").and_then(|v| v.as_i64()).unwrap_or(0) == 1 {
218 data_map.insert(
219 "oId".to_string(),
220 data_obj.get("oId").cloned().unwrap_or(Value::Null),
221 );
222 data_map.insert(
223 "userId".to_string(),
224 data_obj.get("userId").cloned().unwrap_or(Value::Null),
225 );
226 data_map.insert(
227 "lvCode".to_string(),
228 data_obj.get("lvCode").cloned().unwrap_or(Value::Null),
229 );
230 data_map.insert(
231 "expiresAt".to_string(),
232 data_obj.get("expiresAt").cloned().unwrap_or(Value::Null),
233 );
234 data_map.insert(
235 "createdAt".to_string(),
236 data_obj.get("createdAt").cloned().unwrap_or(Value::Null),
237 );
238 data_map.insert(
239 "updatedAt".to_string(),
240 data_obj.get("updatedAt").cloned().unwrap_or(Value::Null),
241 );
242 }
243 }
244
245 UserVipInfo::from_value(&data)
246 }
247
248 pub async fn log(page: u32, page_size: u32) -> Result<Vec<Log>, Error> {
255 let url = build_http_path(
256 "logs/more",
257 &[
258 ("page", page.to_string()),
259 ("pageSize", page_size.to_string()),
260 ],
261 );
262
263 let rsp = get(&url).await?;
264
265 let logs = rsp["data"]
266 .as_array()
267 .ok_or_else(|| Error::Api("Data is not an array".to_string()))?
268 .iter()
269 .map(Log::from_value)
270 .collect::<Result<Vec<Log>, _>>()?;
271
272 Ok(logs)
273 }
274}