Skip to content

Commit 13d5416

Browse files
authored
Feat: support leetcode.cn (#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

+2-2
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

+23-12
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

+42-1
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

+10-1
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

+19
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

+1-1
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

+35-14
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)