Skip to content

Feat: support leetcode.cn #158

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/cache/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,10 +151,10 @@ impl Question {
}

pub fn desc_comment(&self, conf: &Config) -> String {
let desc = self.content.render();
let desc = self.t_content.render();

let mut res = desc.lines().fold("\n".to_string(), |acc, e| {
acc + " " + conf.code.comment_leading.as_str() + " " + e + "\n"
acc + "" + conf.code.comment_leading.as_str() + " " + e + "\n"
});
res += " \n";

Expand Down
35 changes: 23 additions & 12 deletions src/cache/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,17 @@ pub fn problem(problems: &mut Vec<Problem>, v: Value) -> Option<()> {
let total_acs = stat.get("total_acs")?.as_f64()? as f32;
let total_submitted = stat.get("total_submitted")?.as_f64()? as f32;

let fid_obj = stat.get("frontend_question_id")?;
let fid = match fid_obj.as_i64() {
// Handle on leetcode-com
Some(s) => s as i32,
// Handle on leetcode-cn
None => fid_obj.as_str()?.split(" ").last()?.parse::<i32>().ok()?,
};

problems.push(Problem {
category: v.get("category_slug")?.as_str()?.to_string(),
fid: stat.get("frontend_question_id")?.as_i64()? as i32,
fid: fid,
id: stat.get("question_id")?.as_i64()? as i32,
level: p.get("difficulty")?.as_object()?.get("level")?.as_i64()? as i32,
locked: p.get("paid_only")?.as_bool()?,
Expand Down Expand Up @@ -89,17 +97,20 @@ pub fn tags(v: Value) -> Option<Vec<String>> {
/// daily parser
pub fn daily(v: Value) -> Option<i32> {
trace!("Parse daily...");
v.as_object()?
.get("data")?
.as_object()?
.get("activeDailyCodingChallengeQuestion")?
.as_object()?
.get("question")?
.as_object()?
.get("questionFrontendId")?
.as_str()?
.parse()
.ok()
let v_obj = v.as_object()?.get("data")?.as_object()?;
match v_obj.get("dailyQuestionRecord") {
// Handle on leetcode-com
Some(v) => v,
// Handle on leetcode-cn
None => v_obj.get("todayRecord")?.as_array()?.get(0)?,
}
.as_object()?
.get("question")?
.as_object()?
.get("questionFrontendId")?
.as_str()?
.parse()
.ok()
}

/// user parser
Expand Down
43 changes: 42 additions & 1 deletion src/config/cookies.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,52 @@
//! Cookies in config
use std::str::FromStr;

use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum LeetcodeSite {
#[serde(rename = "leetcode.com")]
LeetcodeCom,
#[serde(rename = "leetcode.cn")]
LeetcodeCn,
}

impl FromStr for LeetcodeSite {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"leetcode.com" => Ok(LeetcodeSite::LeetcodeCom),
"leetcode.cn" => Ok(LeetcodeSite::LeetcodeCn),
_ => Err("Invalid site key".to_string()),
}
}
}

impl ToString for LeetcodeSite {
fn to_string(&self) -> String {
match self {
LeetcodeSite::LeetcodeCom => "leetcode.com".to_string(),
LeetcodeSite::LeetcodeCn => "leetcode.cn".to_string(),
}
}
}

/// Cookies settings
#[derive(Default, Clone, Debug, Deserialize, Serialize)]
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Cookies {
pub csrf: String,
pub session: String,
pub site: LeetcodeSite,
}

impl Default for Cookies {
fn default() -> Self {
Self {
csrf: "".to_string(),
session: "".to_string(),
site: LeetcodeSite::LeetcodeCom,
}
}
}

impl std::string::ToString for Cookies {
Expand Down
11 changes: 10 additions & 1 deletion src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ mod cookies;
mod storage;
mod sys;

pub use cookies::LeetcodeSite;

/// Sync with `~/.leetcode/leetcode.toml`
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct Config {
Expand Down Expand Up @@ -44,7 +46,14 @@ impl Config {

let s = fs::read_to_string(&conf)?;
match toml::from_str::<Config>(&s) {
Ok(config) => Ok(config),
Ok(config) => match config.cookies.site {
cookies::LeetcodeSite::LeetcodeCom => Ok(config),
cookies::LeetcodeSite::LeetcodeCn => {
let mut config = config;
config.sys.urls = sys::Urls::new_with_leetcode_cn();
Ok(config)
}
},
Err(e) => {
let tmp = Self::root()?.join("leetcode.tmp.toml");
Self::write_default(tmp)?;
Expand Down
19 changes: 19 additions & 0 deletions src/config/sys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,25 @@ impl Default for Urls {
}

impl Urls {
pub fn new_with_leetcode_cn() -> Self {
Self {
base: "https://leetcode.cn".into(),
graphql: "https://leetcode.cn/graphql".into(),
login: "https://leetcode.cn/accounts/login/".into(),
problems: "https://leetcode.cn/api/problems/$category/".into(),
problem: "https://leetcode.cn/problems/$slug/description/".into(),
tag: "https://leetcode.cn/tag/$slug/".into(),
test: "https://leetcode.cn/problems/$slug/interpret_solution/".into(),
session: "https://leetcode.cn/session/".into(),
submit: "https://leetcode.cn/problems/$slug/submit/".into(),
submissions: "https://leetcode.cn/submissions/detail/$id/".into(),
submission: "https://leetcode.cn/submissions/detail/$id/".into(),
verify: "https://leetcode.cn/submissions/detail/$id/check/".into(),
favorites: "https://leetcode.cn/list/api/questions".into(),
favorite_delete: "https://leetcode.cn/list/api/questions/$hash/$id".into(),
}
}

/// problem url with specific `$slug`
pub fn problem(&self, slug: &str) -> String {
self.problem.replace("$slug", slug)
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/chrome.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ pub fn cookies() -> Result<Ident> {
debug!("Chrome Cookies path is {:?}", &p);
let mut conn = cache::conn(p.to_string_lossy().to_string());
let res = cookies
.filter(host_key.like("%leetcode.com"))
.filter(host_key.like(format!("#{}", ccfg.site.to_string())))
.load::<Cookies>(&mut conn)
.expect("Loading cookies from google chrome failed.");

Expand Down
49 changes: 35 additions & 14 deletions src/plugins/leetcode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,20 +145,41 @@ impl LeetCode {
trace!("Requesting daily problem...");
let url = &self.conf.sys.urls.graphql;
let mut json: Json = HashMap::new();
json.insert("operationName", "daily".to_string());
json.insert(
"query",
vec![
"query daily {",
" activeDailyCodingChallengeQuestion {",
" question {",
" questionFrontendId",
" }",
" }",
"}",
]
.join("\n"),
);

match self.conf.cookies.site {
config::LeetcodeSite::LeetcodeCom => {
json.insert("operationName", "daily".to_string());
json.insert(
"query",
vec![
"query daily {",
" activeDailyCodingChallengeQuestion {",
" question {",
" questionFrontendId",
" }",
" }",
"}",
]
.join("\n"),
);
}
config::LeetcodeSite::LeetcodeCn => {
json.insert("operationName", "questionOfToday".to_string());
json.insert(
"query",
vec![
"query questionOfToday {",
" todayRecord {",
" question {",
" questionFrontendId",
" }",
" }",
"}",
]
.join("\n"),
);
}
}

Req {
default_headers: self.default_headers,
Expand Down
Loading