From c3c3df3a5df04245b368fe88725106632dff12e0 Mon Sep 17 00:00:00 2001 From: 152334H <54623771+152334H@users.noreply.github.com> Date: Wed, 18 May 2022 15:35:31 +0800 Subject: [PATCH 1/5] add graphql query for user info --- src/plugins/leetcode.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/plugins/leetcode.rs b/src/plugins/leetcode.rs index d98f4a8..4394532 100644 --- a/src/plugins/leetcode.rs +++ b/src/plugins/leetcode.rs @@ -121,6 +121,34 @@ impl LeetCode { .await } + pub async fn get_user_info(self) -> Result { + trace!("Requesting user info..."); + let url = &self.conf.sys.urls.get("graphql").ok_or(Error::NoneError)?; + let mut json: Json = HashMap::new(); + json.insert("operationName", "a".to_string()); + json.insert( + "query", + "query a { + user { + username + isCurrentUserPremium + } + }".to_owned() + ); + + Req { + default_headers: self.default_headers, + refer: None, + info: false, + json: Some(json), + mode: Mode::Post, + name: "get_user_info", + url: (*url).to_string(), + } + .send(&self.client) + .await + } + /// Get daily problem pub async fn get_question_daily(self) -> Result { trace!("Requesting daily problem..."); From e8e83eb04caa4d483e0b8ba52453799e51caef8c Mon Sep 17 00:00:00 2001 From: 152334H <54623771+152334H@users.noreply.github.com> Date: Wed, 18 May 2022 15:36:05 +0800 Subject: [PATCH 2/5] add parser for graphql user info --- src/cache/parser.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/cache/parser.rs b/src/cache/parser.rs index f60476d..130fbee 100644 --- a/src/cache/parser.rs +++ b/src/cache/parser.rs @@ -88,6 +88,21 @@ pub fn daily(v: Value) -> Option { .parse().ok() } +/// user parser +pub fn user(v: Value) -> Option> { + // None => error while parsing + // Some(None) => User not found + // Some("...") => username + let user = v.as_object()?.get("data")? + .as_object()?.get("user")?; + if *user == Value::Null { return Some(None) } + let user = user.as_object()?; + Some(Some(( + user.get("username")?.as_str()?.to_owned(), + user.get("isCurrentUserPremium")?.as_bool()? + ))) +} + pub use ss::ssr; /// string or squence mod ss { From a22a4f6df5a95ca5f09806d5ae2394e48623e965 Mon Sep 17 00:00:00 2001 From: 152334H <54623771+152334H@users.noreply.github.com> Date: Wed, 18 May 2022 15:41:04 +0800 Subject: [PATCH 3/5] return `CookieError` when leetcode rejects exec/test `Run` --- src/cache/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/cache/mod.rs b/src/cache/mod.rs index a0f862b..98363de 100644 --- a/src/cache/mod.rs +++ b/src/cache/mod.rs @@ -311,6 +311,12 @@ impl Cache { .await?; trace!("Run code result {:#?}", run_res); + // Check if leetcode accepted the Run request + if match run { + Run::Test => run_res.interpret_id.is_empty(), + Run::Submit => run_res.submission_id == 0 + } { return Err(Error::CookieError) } + let mut res: VerifyResult = VerifyResult::default(); while res.state != "SUCCESS" { res = match run { From 9d1a95cce035b87bc6801c6ccfb730cd4e43725c Mon Sep 17 00:00:00 2001 From: 152334H <54623771+152334H@users.noreply.github.com> Date: Wed, 18 May 2022 15:44:12 +0800 Subject: [PATCH 4/5] add the `is_session_bad` method to somewhat-accurately determine when a LEETCODE_SESSION becomes invalid --- src/cache/mod.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/cache/mod.rs b/src/cache/mod.rs index 98363de..f5ee14f 100644 --- a/src/cache/mod.rs +++ b/src/cache/mod.rs @@ -58,6 +58,27 @@ impl Cache { Ok(()) } + async fn get_user_info(&self) -> Result<(String,bool), Error> { + let user = parser::user( + self.clone().0 + .get_user_info().await? + .json().await? + ); + match user { + None => Err(Error::NoneError), + Some(None) => Err(Error::CookieError), + Some(Some((s,b))) => Ok((s,b)) + } + } + + async fn is_session_bad(&self) -> bool { + // i.e. self.get_user_info().contains_err(Error::CookieError) + match self.get_user_info().await { + Err(Error::CookieError) => true, + _ => false + } + } + /// Download leetcode problems to db pub async fn download_problems(self) -> Result, Error> { info!("Fetching leetcode problems..."); From 916e936d16c4067cdadc5863f98e0427768df8d7 Mon Sep 17 00:00:00 2001 From: 152334H <54623771+152334H@users.noreply.github.com> Date: Wed, 18 May 2022 15:48:03 +0800 Subject: [PATCH 5/5] When json parsing fails, if the underlying request requires user authentication, use `is_session_bad()` to check if LEETCODE_SESSION is valid. --- src/cache/mod.rs | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/cache/mod.rs b/src/cache/mod.rs index f5ee14f..8af78d7 100644 --- a/src/cache/mod.rs +++ b/src/cache/mod.rs @@ -9,8 +9,10 @@ use self::sql::*; use crate::{cfg, err::Error, plugins::LeetCode}; use colored::Colorize; use diesel::prelude::*; +use serde::de::DeserializeOwned; use serde_json::Value; use std::collections::HashMap; +use reqwest::Response; /// sqlite connection pub fn conn(p: String) -> SqliteConnection { @@ -79,6 +81,13 @@ impl Cache { } } + async fn resp_to_json(&self, resp: Response) -> Result { + let maybe_json: Result = resp.json().await; + if maybe_json.is_err() && self.is_session_bad().await { + Err(Error::CookieError) + } else { Ok(maybe_json?) } + } + /// Download leetcode problems to db pub async fn download_problems(self) -> Result, Error> { info!("Fetching leetcode problems..."); @@ -90,7 +99,7 @@ impl Cache { .clone() .get_category_problems(i) .await? - .json() + .json() // does not require LEETCODE_SESSION .await?; parser::problem(&mut ps, json).ok_or(Error::NoneError)?; } @@ -121,7 +130,7 @@ impl Cache { .0 .get_question_daily() .await? - .json() + .json() // does not require LEETCODE_SESSION .await? ).ok_or(Error::NoneError) } @@ -286,30 +295,20 @@ impl Cache { /// TODO: The real delay async fn recur_verify(&self, rid: String) -> Result { - use serde_json::{from_str, Error as SJError}; use std::time::Duration; trace!("Run veriy recursion..."); std::thread::sleep(Duration::from_micros(3000)); - // debug resp raw text - let debug_raw = self + let json: VerifyResult = self.resp_to_json( + self .clone() .0 .verify_result(rid.clone()) .await? - .text() - .await?; - debug!("debug resp raw text: \n{:#?}", &debug_raw); - if debug_raw.is_empty() { - return Err(Error::CookieError); - } - - // debug json deserializing - let debug_json: Result = from_str(&debug_raw); - debug!("debug json deserializing: \n{:#?}", &debug_json); + ).await?; - Ok(debug_json?) + Ok(json) } /// Exec problem filter —— Test or Submit @@ -328,7 +327,7 @@ impl Cache { .clone() .run_code(json.clone(), url.clone(), refer.clone()) .await? - .json() + .json() // does not require LEETCODE_SESSION (very oddly) .await?; trace!("Run code result {:#?}", run_res);