Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit db06498

Browse files
refs skygragon#80: fixes "Unknown language" error
* update to support recent leetcode.com change. Signed-off-by: Eric Wang <skygragon@gmail.com>
1 parent 4f44195 commit db06498

File tree

4 files changed

+63
-54
lines changed

4 files changed

+63
-54
lines changed

‎lib/config.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ var DEFAULT_SYS_CONFIG = {
77
URL_BASE: 'https://leetcode.com',
88
URL_LOGIN: 'https://leetcode.com/accounts/login/',
99
URL_PROBLEMS: 'https://leetcode.com/api/problems/$category/',
10-
URL_PROBLEM: 'https://leetcode.com/problems/$slug',
10+
URL_PROBLEM: 'https://leetcode.com/graphql',
1111
URL_TEST: 'https://leetcode.com/problems/$slug/interpret_solution/',
1212
URL_SUBMIT: 'https://leetcode.com/problems/$slug/submit/',
1313
URL_SUBMISSIONS: 'https://leetcode.com/api/submissions/$slug',

‎lib/plugins/leetcode.js‎

Lines changed: 37 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -114,45 +114,47 @@ plugin.getCategoryProblems = function(category, cb) {
114114

115115
plugin.getProblem = function(problem, cb) {
116116
log.debug('running leetcode.getProblem');
117-
var opts = makeOpts(problem.link);
118-
119-
request(opts, function(e, resp, body) {
120-
e = checkError(e, resp, 200);
121-
if (e) return cb(e);
122-
123-
var $ = cheerio.load(body);
124-
var spans = $('ul[class=side-bar-list] li[class=list-item] span');
125-
126-
problem.totalAC = $(spans[3]).text();
127-
problem.totalSubmit = $(spans[5]).text();
117+
var user = session.getUser();
118+
if (problem.locked && !user.paid) return cb('failed to load locked problem!');
128119

129-
// TODO: revisit this if later leetcode remove this element.
130-
// Then we need parse the body to get the description.
131-
problem.desc = $('meta[name="description"]').attr('content');
132-
problem.desc = he.decode(problem.desc);
120+
var opts = makeOpts(config.URL_PROBLEM);
121+
opts.headers.Origin = config.URL_BASE;
122+
opts.headers.Referer = problem.link;
133123

134-
var pageData;
135-
var r = /(varpageData[^;]+;)/m;
136-
var re = body.match(r);
137-
if (!re) {
138-
var user = session.getUser();
139-
if (problem.locked && user.paid) {
140-
e = session.errors.EXPIRED;
141-
} else {
142-
e = 'failed to load' + (problem.locked ? ' locked' : '') + ' problem!';
143-
}
144-
return cb(e);
145-
}
124+
opts.json = true;
125+
opts.body = {
126+
query: [
127+
'query getQuestionDetail($titleSlug: String!) {',
128+
' question(titleSlug: $titleSlug) {',
129+
' content',
130+
' stats',
131+
' codeDefinition',
132+
' sampleTestCase',
133+
' enableRunCode',
134+
' metaData',
135+
' discussCategoryId',
136+
' }',
137+
'}'
138+
].join('\n'),
139+
variables: {titleSlug: problem.slug},
140+
operationName: 'getQuestionDetail'
141+
};
146142

147-
eval(re[1]);
148-
problem.templates = pageData.codeDefinition;
149-
problem.testcase = pageData.sampleTestCase;
150-
problem.testable = pageData.enableRunCode;
151-
problem.templateMeta = eval(pageData.metaData);
143+
request.post(opts, function(e, resp, body) {
144+
e = checkError(e, resp, 200);
145+
if (e) return cb(e);
152146

153-
r = /https:\/\/discuss.leetcode.com\/category\/(\d+)/;
154-
re = body.match(r);
155-
if (re) problem.discuss = re[1];
147+
var q = body.data.question;
148+
if (!q) return cb('failed to load problem!');
149+
150+
problem.totalAC = JSON.parse(q.stats).totalAccepted;
151+
problem.totalSubmit = JSON.parse(q.stats).totalSubmission;
152+
problem.desc = he.decode(cheerio.load(q.content).root().text());
153+
problem.templates = JSON.parse(q.codeDefinition);
154+
problem.testcase = q.sampleTestCase;
155+
problem.testable = q.enableRunCode;
156+
problem.templateMeta = JSON.parse(q.metaData);
157+
problem.discuss = q.discussCategoryId;
156158

157159
return cb(null, problem);
158160
});
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"data":{"question":{"content":"<p>\r\nGiven two strings <b><i>s</i></b> and <b><i>t</i></b> which consist of only lowercase letters.</p>\r\n\r\n<p>String <b><i>t</i></b> is generated by random shuffling string <b><i>s</i></b> and then add one more letter at a random position.</p>\r\n\r\n<p>Find the letter that was added in <b><i>t</i></b>.</p>\r\n\r\n<p><b>Example:</b>\r\n<pre>\r\nInput:\r\ns = \"abcd\"\r\nt = \"abcde\"\r\n\r\nOutput:\r\ne\r\n\r\nExplanation:\r\n'e' is the letter that was added.\r\n</pre>","stats":"{\"totalAccepted\": \"89.7K\", \"totalSubmission\": \"175.7K\"}","codeDefinition":"[{\"text\": \"C++\", \"value\": \"cpp\", \"defaultCode\": \"class Solution {\\r\\npublic:\\r\\n char findTheDifference(string s, string t) {\\r\\n \\r\\n }\\r\\n};\"}, {\"text\": \"Java\", \"value\": \"java\", \"defaultCode\": \"class Solution {\\r\\n public char findTheDifference(String s, String t) {\\r\\n \\r\\n }\\r\\n}\"}, {\"text\": \"Python\", \"value\": \"python\", \"defaultCode\": \"class Solution(object):\\r\\n def findTheDifference(self, s, t):\\r\\n \\\"\\\"\\\"\\r\\n :type s: str\\r\\n :type t: str\\r\\n :rtype: str\\r\\n \\\"\\\"\\\"\\r\\n \"}, {\"text\": \"Python3\", \"value\": \"python3\", \"defaultCode\": \"class Solution:\\r\\n def findTheDifference(self, s, t):\\r\\n \\\"\\\"\\\"\\r\\n :type s: str\\r\\n :type t: str\\r\\n :rtype: str\\r\\n \\\"\\\"\\\"\\r\\n \"}, {\"text\": \"C\", \"value\": \"c\", \"defaultCode\": \"char findTheDifference(char* s, char* t) {\\r\\n \\r\\n}\"}, {\"text\": \"C#\", \"value\": \"csharp\", \"defaultCode\": \"public class Solution {\\r\\n public char FindTheDifference(string s, string t) {\\r\\n \\r\\n }\\r\\n}\"}, {\"text\": \"JavaScript\", \"value\": \"javascript\", \"defaultCode\": \"/**\\r\\n * @param {string} s\\r\\n * @param {string} t\\r\\n * @return {character}\\r\\n */\\r\\nvar findTheDifference = function(s, t) {\\r\\n \\r\\n};\"}, {\"text\": \"Ruby\", \"value\": \"ruby\", \"defaultCode\": \"# @param {String} s\\r\\n# @param {String} t\\r\\n# @return {Character}\\r\\ndef find_the_difference(s, t)\\r\\n \\r\\nend\"}, {\"text\": \"Swift\", \"value\": \"swift\", \"defaultCode\": \"class Solution {\\r\\n func findTheDifference(_ s: String, _ t: String) -> Character {\\r\\n \\r\\n }\\r\\n}\"}, {\"text\": \"Go\", \"value\": \"golang\", \"defaultCode\": \"func findTheDifference(s string, t string) byte {\\r\\n \\r\\n}\"}, {\"text\": \"Scala\", \"value\": \"scala\", \"defaultCode\": \"object Solution {\\n def findTheDifference(s: String, t: String): Char = {\\n \\n }\\n}\"}, {\"text\": \"Kotlin\", \"value\": \"kotlin\", \"defaultCode\": \"class Solution {\\n fun findTheDifference(s: String, t: String): Char {\\n \\n }\\n}\"}]","sampleTestCase":"\"abcd\"\n\"abcde\"","enableRunCode":true,"metaData":"{\r\n \"name\": \"findTheDifference\",\r\n \"params\": [\r\n {\r\n \"name\": \"s\",\r\n \"type\": \"string\"\r\n },\r\n {\r\n \"name\": \"t\",\r\n \"type\": \"string\"\r\n }\r\n ],\r\n \"return\": {\r\n \"type\": \"character\"\r\n }\r\n}","discussCategoryId":"511"}}}

‎test/plugins/test_leetcode.js‎

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -171,15 +171,19 @@ describe('plugin:leetcode', function() {
171171
}); // #getCategoryProblems
172172

173173
describe('#getProblem', function() {
174+
beforeEach(function() {
175+
PROBLEM.locked = false;
176+
});
177+
174178
it('should ok', function(done) {
175179
nock('https://leetcode.com')
176-
.get('/problems/find-the-difference')
177-
.replyWithFile(200, './test/mock/find-the-difference.html.20170714');
180+
.post('/graphql')
181+
.replyWithFile(200, './test/mock/find-the-difference.json.20171216');
178182

179183
plugin.getProblem(PROBLEM, function(e, problem) {
180184
assert.equal(e, null);
181-
assert.equal(problem.totalAC, '73.2K');
182-
assert.equal(problem.totalSubmit, '142K');
185+
assert.equal(problem.totalAC, '89.7K');
186+
assert.equal(problem.totalSubmit, '175.7K');
183187
assert.equal(problem.desc,
184188
[
185189
'',
@@ -203,7 +207,7 @@ describe('plugin:leetcode', function() {
203207
''
204208
].join('\r\n'));
205209

206-
assert.equal(problem.templates.length, 11);
210+
assert.equal(problem.templates.length, 12);
207211

208212
assert.equal(problem.templates[0].value, 'cpp');
209213
assert.equal(problem.templates[0].text, 'C++');
@@ -221,7 +225,7 @@ describe('plugin:leetcode', function() {
221225
assert.equal(problem.templates[1].text, 'Java');
222226
assert.equal(problem.templates[1].defaultCode,
223227
[
224-
'public class Solution {',
228+
'class Solution {',
225229
' public char findTheDifference(String s, String t) {',
226230
' ',
227231
' }',
@@ -333,15 +337,23 @@ describe('plugin:leetcode', function() {
333337
'}'
334338
].join('\n'));
335339

340+
assert.equal(problem.templates[11].value, 'kotlin');
341+
assert.equal(problem.templates[11].text, 'Kotlin');
342+
assert.equal(problem.templates[11].defaultCode,
343+
[
344+
'class Solution {',
345+
' fun findTheDifference(s: String, t: String): Char {',
346+
' ',
347+
' }',
348+
'}'
349+
].join('\n'));
350+
336351
done();
337352
});
338353
});
339354

340355
it('should fail if no permission for locked', function(done) {
341356
PROBLEM.locked = true;
342-
nock('https://leetcode.com')
343-
.get('/problems/find-the-difference')
344-
.replyWithFile(200, './test/mock/locked.html.20161015');
345357

346358
plugin.getProblem(PROBLEM, function(e, problem) {
347359
assert.equal(e, 'failed to load locked problem!');
@@ -350,9 +362,7 @@ describe('plugin:leetcode', function() {
350362
});
351363

352364
it('should fail if session expired', function(done) {
353-
nock('https://leetcode.com')
354-
.get('/problems/find-the-difference')
355-
.reply(403);
365+
nock('https://leetcode.com').post('/graphql').reply(403);
356366

357367
plugin.getProblem(PROBLEM, function(e, problem) {
358368
assert.equal(e, session.errors.EXPIRED);
@@ -361,9 +371,7 @@ describe('plugin:leetcode', function() {
361371
});
362372

363373
it('should fail if http error', function(done) {
364-
nock('https://leetcode.com')
365-
.get('/problems/find-the-difference')
366-
.reply(500);
374+
nock('https://leetcode.com').post('/graphql').reply(500);
367375

368376
plugin.getProblem(PROBLEM, function(e, problem) {
369377
assert.deepEqual(e, {msg: 'http error', statusCode: 500});
@@ -372,9 +380,7 @@ describe('plugin:leetcode', function() {
372380
});
373381

374382
it('should fail if unknown error', function(done) {
375-
nock('https://leetcode.com')
376-
.get('/problems/find-the-difference')
377-
.replyWithError('unknown error!');
383+
nock('https://leetcode.com').post('/graphql').replyWithError('unknown error!');
378384

379385
plugin.getProblem(PROBLEM, function(e, problem) {
380386
assert.equal(e.message, 'unknown error!');

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /