Skip to content

Commit 13d5416

Browse files
authored
Feat: support leetcode.cn (clearloop#158)
* feat: support leetcode.cn * feat: add site config at cookies section * feat: use translatedContent instead of content
1 parent 39789f2 commit 13d5416

File tree

7 files changed

+132
-31
lines changed

7 files changed

+132
-31
lines changed

src/cache/models.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,10 +151,10 @@ impl Question {
151151
}
152152

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

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

src/cache/parser.rs

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,17 @@ pub fn problem(problems: &mut Vec<Problem>, v: Value) -> Option<()> {
1010
let total_acs = stat.get("total_acs")?.as_f64()? as f32;
1111
let total_submitted = stat.get("total_submitted")?.as_f64()? as f32;
1212

13+
let fid_obj = stat.get("frontend_question_id")?;
14+
let fid = match fid_obj.as_i64() {
15+
// Handle on leetcode-com
16+
Some(s) => s as i32,
17+
// Handle on leetcode-cn
18+
None => fid_obj.as_str()?.split(" ").last()?.parse::<i32>().ok()?,
19+
};
20+
1321
problems.push(Problem {
1422
category: v.get("category_slug")?.as_str()?.to_string(),
15-
fid: stat.get("frontend_question_id")?.as_i64()? as i32,
23+
fid: fid,
1624
id: stat.get("question_id")?.as_i64()? as i32,
1725
level: p.get("difficulty")?.as_object()?.get("level")?.as_i64()? as i32,
1826
locked: p.get("paid_only")?.as_bool()?,
@@ -89,17 +97,20 @@ pub fn tags(v: Value) -> Option<Vec<String>> {
8997
/// daily parser
9098
pub fn daily(v: Value) -> Option<i32> {
9199
trace!("Parse daily...");
92-
v.as_object()?
93-
.get("data")?
94-
.as_object()?
95-
.get("activeDailyCodingChallengeQuestion")?
96-
.as_object()?
97-
.get("question")?
98-
.as_object()?
99-
.get("questionFrontendId")?
100-
.as_str()?
101-
.parse()
102-
.ok()
100+
let v_obj = v.as_object()?.get("data")?.as_object()?;
101+
match v_obj.get("dailyQuestionRecord") {
102+
// Handle on leetcode-com
103+
Some(v) => v,
104+
// Handle on leetcode-cn
105+
None => v_obj.get("todayRecord")?.as_array()?.get(0)?,
106+
}
107+
.as_object()?
108+
.get("question")?
109+
.as_object()?
110+
.get("questionFrontendId")?
111+
.as_str()?
112+
.parse()
113+
.ok()
103114
}
104115

105116
/// user parser

src/config/cookies.rs

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,52 @@
11
//! Cookies in config
2+
use std::str::FromStr;
3+
24
use serde::{Deserialize, Serialize};
35

6+
#[derive(Clone, Debug, Deserialize, Serialize)]
7+
pub enum LeetcodeSite {
8+
#[serde(rename = "leetcode.com")]
9+
LeetcodeCom,
10+
#[serde(rename = "leetcode.cn")]
11+
LeetcodeCn,
12+
}
13+
14+
impl FromStr for LeetcodeSite {
15+
type Err = String;
16+
fn from_str(s: &str) -> Result<Self, Self::Err> {
17+
match s {
18+
"leetcode.com" => Ok(LeetcodeSite::LeetcodeCom),
19+
"leetcode.cn" => Ok(LeetcodeSite::LeetcodeCn),
20+
_ => Err("Invalid site key".to_string()),
21+
}
22+
}
23+
}
24+
25+
impl ToString for LeetcodeSite {
26+
fn to_string(&self) -> String {
27+
match self {
28+
LeetcodeSite::LeetcodeCom => "leetcode.com".to_string(),
29+
LeetcodeSite::LeetcodeCn => "leetcode.cn".to_string(),
30+
}
31+
}
32+
}
33+
434
/// Cookies settings
5-
#[derive(Default, Clone, Debug, Deserialize, Serialize)]
35+
#[derive(Clone, Debug, Deserialize, Serialize)]
636
pub struct Cookies {
737
pub csrf: String,
838
pub session: String,
39+
pub site: LeetcodeSite,
40+
}
41+
42+
impl Default for Cookies {
43+
fn default() -> Self {
44+
Self {
45+
csrf: "".to_string(),
46+
session: "".to_string(),
47+
site: LeetcodeSite::LeetcodeCom,
48+
}
49+
}
950
}
1051

1152
impl std::string::ToString for Cookies {

src/config/mod.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ mod cookies;
1717
mod storage;
1818
mod sys;
1919

20+
pub use cookies::LeetcodeSite;
21+
2022
/// Sync with `~/.leetcode/leetcode.toml`
2123
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
2224
pub struct Config {
@@ -44,7 +46,14 @@ impl Config {
4446

4547
let s = fs::read_to_string(&conf)?;
4648
match toml::from_str::<Config>(&s) {
47-
Ok(config) => Ok(config),
49+
Ok(config) => match config.cookies.site {
50+
cookies::LeetcodeSite::LeetcodeCom => Ok(config),
51+
cookies::LeetcodeSite::LeetcodeCn => {
52+
let mut config = config;
53+
config.sys.urls = sys::Urls::new_with_leetcode_cn();
54+
Ok(config)
55+
}
56+
},
4857
Err(e) => {
4958
let tmp = Self::root()?.join("leetcode.tmp.toml");
5059
Self::write_default(tmp)?;

src/config/sys.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,25 @@ impl Default for Urls {
5252
}
5353

5454
impl Urls {
55+
pub fn new_with_leetcode_cn() -> Self {
56+
Self {
57+
base: "https://leetcode.cn".into(),
58+
graphql: "https://leetcode.cn/graphql".into(),
59+
login: "https://leetcode.cn/accounts/login/".into(),
60+
problems: "https://leetcode.cn/api/problems/$category/".into(),
61+
problem: "https://leetcode.cn/problems/$slug/description/".into(),
62+
tag: "https://leetcode.cn/tag/$slug/".into(),
63+
test: "https://leetcode.cn/problems/$slug/interpret_solution/".into(),
64+
session: "https://leetcode.cn/session/".into(),
65+
submit: "https://leetcode.cn/problems/$slug/submit/".into(),
66+
submissions: "https://leetcode.cn/submissions/detail/$id/".into(),
67+
submission: "https://leetcode.cn/submissions/detail/$id/".into(),
68+
verify: "https://leetcode.cn/submissions/detail/$id/check/".into(),
69+
favorites: "https://leetcode.cn/list/api/questions".into(),
70+
favorite_delete: "https://leetcode.cn/list/api/questions/$hash/$id".into(),
71+
}
72+
}
73+
5574
/// problem url with specific `$slug`
5675
pub fn problem(&self, slug: &str) -> String {
5776
self.problem.replace("$slug", slug)

src/plugins/chrome.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ pub fn cookies() -> Result<Ident> {
6464
debug!("Chrome Cookies path is {:?}", &p);
6565
let mut conn = cache::conn(p.to_string_lossy().to_string());
6666
let res = cookies
67-
.filter(host_key.like("%leetcode.com"))
67+
.filter(host_key.like(format!("#{}", ccfg.site.to_string())))
6868
.load::<Cookies>(&mut conn)
6969
.expect("Loading cookies from google chrome failed.");
7070

src/plugins/leetcode.rs

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -145,20 +145,41 @@ impl LeetCode {
145145
trace!("Requesting daily problem...");
146146
let url = &self.conf.sys.urls.graphql;
147147
let mut json: Json = HashMap::new();
148-
json.insert("operationName", "daily".to_string());
149-
json.insert(
150-
"query",
151-
vec![
152-
"query daily {",
153-
" activeDailyCodingChallengeQuestion {",
154-
" question {",
155-
" questionFrontendId",
156-
" }",
157-
" }",
158-
"}",
159-
]
160-
.join("\n"),
161-
);
148+
149+
match self.conf.cookies.site {
150+
config::LeetcodeSite::LeetcodeCom => {
151+
json.insert("operationName", "daily".to_string());
152+
json.insert(
153+
"query",
154+
vec![
155+
"query daily {",
156+
" activeDailyCodingChallengeQuestion {",
157+
" question {",
158+
" questionFrontendId",
159+
" }",
160+
" }",
161+
"}",
162+
]
163+
.join("\n"),
164+
);
165+
}
166+
config::LeetcodeSite::LeetcodeCn => {
167+
json.insert("operationName", "questionOfToday".to_string());
168+
json.insert(
169+
"query",
170+
vec![
171+
"query questionOfToday {",
172+
" todayRecord {",
173+
" question {",
174+
" questionFrontendId",
175+
" }",
176+
" }",
177+
"}",
178+
]
179+
.join("\n"),
180+
);
181+
}
182+
}
162183

163184
Req {
164185
default_headers: self.default_headers,

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy