leetcoderustapi/
profile.rs1use serde_json::json;
2
3use crate::{
4 error::Errors,
5 resources::{
6 beat_stats::BeatStats, data_profile::ProfileData, fav_list::FavoriteList,
7 lang_stats::LanguageStats, notification::NotificationsData,
8 pub_data_profile::UserFoundData, skill_stats::SkillStats, subm_list::RecentSubmList,
9 },
10};
11
12#[derive(Debug)]
13pub struct MyProfile {
14 pub(crate) client: reqwest::Client,
15 pub(crate) fav_lists: FavoriteList,
16}
17
18impl MyProfile {
19 pub async fn create_list(&self, list_name: &str) -> Result<&MyProfile, Errors> {
20 let query = json!({ "name": list_name });
21
22 let query = serde_json::to_string(&query)?;
23
24 self.client
25 .get("https://leetcode.com/list/api/")
26 .body(query)
27 .send()
28 .await?;
29 Ok(self)
30 }
31 pub fn show_lists(&self) -> FavoriteList {
32 self.fav_lists.clone()
33 }
34
35 pub async fn rename_list(
36 &self,
37 prev_list_name: &str,
38 new_list_name: &str,
39 ) -> Result<&MyProfile, Errors> {
40 let id_hash = if let Some(id) = Self::get_id_hash(&self, prev_list_name) {
41 id
42 } else {
43 return Err(Errors::ApiError("Provided name doesn't found".into()));
44 };
45 let query = json!({
46 "favorite_id_hash": id_hash.0,
47 "is_public_favorite": id_hash.1,
48 "name": new_list_name
49 });
50
51 let query = serde_json::to_string(&query)?;
52
53 self.client
54 .put("https://leetcode.com/list/api/")
55 .body(query)
56 .send()
57 .await?;
58 Ok(self)
59 }
60
61 pub async fn set_public(&self, list_name: &str) -> Result<&MyProfile, Errors> {
62 let id_hash = if let Some(id) = Self::get_id_hash(&self, list_name) {
63 id
64 } else {
65 return Err(Errors::ApiError(
66 "Provided name doesn't found in lists".into(),
67 ));
68 };
69 let query = json!({
70 "favorite_id_hash": id_hash.0,
71 "is_public_favorite": true,
72 "name": list_name
73 });
74
75 let query = serde_json::to_string(&query)?;
76
77 self.client
78 .put("https://leetcode.com/list/api/")
79 .body(query)
80 .send()
81 .await?;
82 Ok(self)
83 }
84
85 pub async fn set_private(&self, list_name: &str) -> Result<&MyProfile, Errors> {
86 let id_hash = if let Some(id) = Self::get_id_hash(&self, list_name) {
87 id
88 } else {
89 return Err(Errors::ApiError(
90 "Provided name doesn't found in lists".into(),
91 ));
92 };
93 let query = json!({
94 "favorite_id_hash": id_hash.0,
95 "is_public_favorite": true,
96 "name": list_name
97 });
98
99 let query = serde_json::to_string(&query)?;
100
101 self.client
102 .put("https://leetcode.com/list/api/")
103 .body(query)
104 .send()
105 .await?;
106 Ok(self)
107 }
108
109 pub async fn get_share_url(&self, list_name: &str) -> Result<String, Errors> {
110 let id_hash = if let Some(id) = Self::get_id_hash(&self, list_name) {
111 id
112 } else {
113 return Err(Errors::ApiError(
114 "Provided name doesn't found in lists".into(),
115 ));
116 };
117
118 Ok(format!("https://leetcode.com/list/{}", id_hash.0))
119 }
120
121 pub async fn delete_list(&self, list_name: &str) -> Result<&MyProfile, Errors> {
122 let id_hash = if let Some(id) = Self::get_id_hash(&self, list_name) {
123 id
124 } else {
125 return Err(Errors::ApiError(
126 "Provided name doesn't found in lists".into(),
127 ));
128 };
129
130 self.client
131 .delete(format!("https://leetcode.com/list/api/{}", id_hash.0))
132 .send()
133 .await?;
134
135 Ok(self)
136 }
137
138 fn get_id_hash(&self, list_name: &str) -> Option<(String, bool)> {
139 for favourite in &self.fav_lists.data.favoritesLists.allFavorites {
140 if favourite.name == list_name.to_string() {
141 return Some((favourite.idHash.clone(), favourite.isPublicFavorite.clone()));
142 }
143 }
144 None
145 }
146
147 pub async fn get_notifications(&self) -> Result<NotificationsData, Errors> {
148 let operation_name = "fetchNotifications";
149 let variables = json!({ "first": 10 });
150 let query = r#"query fetchNotifications($first: Int!, $after: String) {
151 notifications(first: $first, after: $after) {
152 edges {
153 node {
154 id
155 notificationId
156 modifiedDate
157 actioned
158 notificationData {
159 id
160 content
161 type
162 metadata
163 __typename
164 }
165 __typename
166 }
167 __typename
168 }
169 pageInfo {
170 endCursor
171 hasNextPage
172 __typename
173 }
174 __typename
175 }
176 }"#;
177
178 let json_data = json!({
179 "operationName": operation_name,
180 "variables": variables,
181 "query": query,
182 });
183
184 let query = serde_json::to_string(&json_data)?;
185
186 let problem_info = self
187 .client
188 .post("https://leetcode.com/graphql/")
189 .body(query)
190 .send()
191 .await?
192 .text()
193 .await?;
194
195 Ok(serde_json::from_str::<NotificationsData>(&problem_info)?)
196 }
197
198 pub async fn profile_info(&self) -> Result<ProfileData, Errors> {
199 let query = r#"
200 query globalData {
201 userStatus {
202 userId
203 isSignedIn
204 isMockUser
205 isPremium
206 isVerified
207 username
208 avatar
209 isAdmin
210 isSuperuser
211 permissions
212 isTranslator
213 activeSessionId
214 checkedInToday
215 notificationStatus {
216 lastModified
217 numUnread
218 }
219 }
220 }
221 "#;
222
223 let variables = json!({});
224 let operation_name = "globalData";
225
226 let json_data = json!({
227 "query": query,
228 "variables": variables,
229 "operationName": operation_name
230 });
231
232 let query = serde_json::to_string(&json_data)?;
233
234 let data_info = self
235 .client
236 .post("https://leetcode.com/graphql/")
237 .body(query)
238 .send()
239 .await?
240 .text()
241 .await?;
242
243 Ok(serde_json::from_str::<ProfileData>(&data_info)?)
244 }
245}
246
247#[derive(Debug)]
248pub struct UserProfile {
249 pub(crate) client: reqwest::Client,
250 pub(crate) username: String,
251}
252
253impl UserProfile {
254 pub async fn user_stats(&self) -> Result<UserFoundData, Errors> {
255 let query = json!({
256 "query": "query userPublicProfile($username: String!) {\n matchedUser(username: $username) {\n contestBadge {\n name\n expired\n hoverText\n icon\n }\n username\n githubUrl\n twitterUrl\n linkedinUrl\n profile {\n ranking\n userAvatar\n realName\n aboutMe\n school\n websites\n countryName\n company\n jobTitle\n skillTags\n postViewCount\n postViewCountDiff\n reputation\n reputationDiff\n solutionCount\n solutionCountDiff\n categoryDiscussCount\n categoryDiscussCountDiff\n }\n }\n}",
257 "variables": {
258 "username": self.username
259 },
260 "operationName": "userPublicProfile"
261 });
262
263 let query = serde_json::to_string(&query)?;
264
265 let data_info = self
266 .client
267 .post("https://leetcode.com/graphql/")
268 .body(query)
269 .send()
270 .await?
271 .text()
272 .await?;
273
274 Ok(serde_json::from_str::<UserFoundData>(&data_info)?)
275 }
276
277 pub async fn language_stats(&self) -> Result<LanguageStats, Errors> {
278 let query = json!({
279 "query": "query languageStats($username: String!) {\n matchedUser(username: $username) {\n languageProblemCount {\n languageName\n problemsSolved\n }\n }\n}",
280 "variables": {
281 "username": self.username
282 },
283 "operationName": "languageStats"
284 });
285
286 let query = serde_json::to_string(&query)?;
287
288 let data_info = self
289 .client
290 .post("https://leetcode.com/graphql/")
291 .body(query)
292 .send()
293 .await?
294 .text()
295 .await?;
296
297 Ok(serde_json::from_str::<LanguageStats>(&data_info)?)
298 }
299
300 pub async fn skill_stats(&self) -> Result<SkillStats, Errors> {
301 let query = json!({
302 "query": r#"
303 query skillStats($username: String!) {
304 matchedUser(username: $username) {
305 tagProblemCounts {
306 advanced {
307 tagName
308 tagSlug
309 problemsSolved
310 }
311 intermediate {
312 tagName
313 tagSlug
314 problemsSolved
315 }
316 fundamental {
317 tagName
318 tagSlug
319 problemsSolved
320 }
321 }
322 }
323 }
324 "#,
325 "variables": {
326 "username": self.username
327 },
328 "operationName": "skillStats"
329 });
330
331 let query = serde_json::to_string(&query)?;
332
333 let data_info = self
334 .client
335 .post("https://leetcode.com/graphql/")
336 .body(query)
337 .send()
338 .await?
339 .text()
340 .await?;
341
342 Ok(serde_json::from_str::<SkillStats>(&data_info)?)
343 }
344
345 pub async fn problem_beat_stats(&self) -> Result<BeatStats, Errors> {
346 let query = json!({
347 "query": "query userProblemsSolved($username: String!) {\n matchedUser(username: $username) {\n problemsSolvedBeatsStats {\n difficulty\n percentage\n }\n submitStatsGlobal {\n acSubmissionNum {\n difficulty\n count\n }\n }\n }\n}",
348 "variables": {
349 "username": self.username
350 },
351 "operationName": "userProblemsSolved"
352 });
353
354 let query = serde_json::to_string(&query)?;
355
356 let data_info = self
357 .client
358 .post("https://leetcode.com/graphql/")
359 .body(query)
360 .send()
361 .await?
362 .text()
363 .await?;
364
365 Ok(serde_json::from_str::<BeatStats>(&data_info)?)
366 }
367
368 pub async fn recent_subm_list(&self) -> Result<RecentSubmList, Errors> {
369 let query = json!({
370 "query": "query recentAcSubmissions($username: String!, $limit: Int!) {\n recentAcSubmissionList(username: $username, limit: $limit) {\n id\n title\n titleSlug\n timestamp\n }\n}",
371 "variables": {
372 "username": self.username,
373 "limit": 15
374 },
375 "operationName": "recentAcSubmissions"
376 });
377
378 let query = serde_json::to_string(&query)?;
379
380 let data_info = self
381 .client
382 .post("https://leetcode.com/graphql/")
383 .body(query)
384 .send()
385 .await?
386 .text()
387 .await?;
388
389 Ok(serde_json::from_str::<RecentSubmList>(&data_info)?)
390 }
391
392 pub async fn deactivate_token(&self) -> Result<(), Errors> {
393 self.client
394 .post("https://leetcode.com/accounts/logout/")
395 .send()
396 .await?
397 .text()
398 .await?;
399 Ok(())
400 }
401}