Skip to content

Commit db06498

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

File tree

4 files changed

+63
-54
lines changed

4 files changed

+63
-54
lines changed

lib/config.js

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

+37-35
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 = /(var pageData[^;]+;)/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
});
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

+24-18
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)