Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit a3ce68a

Browse files
committed
update
1 parent 922ca44 commit a3ce68a

File tree

4 files changed

+226
-27
lines changed

4 files changed

+226
-27
lines changed

lib/commands/show.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ const cmd = {
5757
default: false,
5858
describe: 'view content in browser'
5959
})
60+
.option('m', {
61+
alias: 'markdown',
62+
type: 'boolean',
63+
default: false,
64+
describe: 'show url as markdown'
65+
})
6066
.option('l', {
6167
alias: 'lang',
6268
type: 'string',
@@ -162,6 +168,11 @@ function showProblem(problem, argv) {
162168
open(problem.link);
163169
}
164170

171+
if (argv.markdown) {
172+
log.printf('- [ ] %s.%s %s', problem.fid, problem.translatedName, problem.link);
173+
return;
174+
}
175+
165176
log.printf('[%s] %s %s', problem.fid, problem.name,
166177
(problem.starred ? chalk.yellow(icon.like) : icon.empty));
167178
log.info();

lib/commands/submit.js

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -60,29 +60,13 @@ cmd.handler = function(argv) {
6060
const result = results[0];
6161

6262
printResult(result, 'state');
63-
printLine(result, '%d/%d cases passed (%s)',
64-
result.passed, result.total, result.runtime);
63+
printLine(result, '%d/%d cases passed (%s)', result.passed, result.total, result.runtime);
6564

6665
if (result.ok) {
6766
session.updateStat('ac', 1);
6867
session.updateStat('ac.set', problem.fid);
69-
core.getSubmission({id: result.id}, function(e, submission) {
70-
if (e || !submission || !submission.distributionChart)
71-
return log.warn('Failed to get submission beat ratio.');
72-
73-
const lang = submission.distributionChart.lang;
74-
const scores = submission.distributionChart.distribution;
75-
const myRuntime = parseFloat(result.runtime);
76-
77-
let ratio = 0.0;
78-
for (let score of scores) {
79-
if (parseFloat(score[0]) >= myRuntime)
80-
ratio += parseFloat(score[1]);
81-
}
82-
83-
printLine(result, 'Your runtime beats %d %% of %s submissions',
84-
ratio.toFixed(2), lang);
85-
});
68+
printLine(result, 'Your cpu runtime (%s) beats %d %% of %s submissions', result.runtime, result.runtime_percentile.toFixed(2), result.lang);
69+
printLine(result, 'Your memory runtime (%s) beats %d %% of %s submissions', result.memory, result.memory_percentile.toFixed(2), result.lang);
8670
} else {
8771
printResult(result, 'error');
8872
printResult(result, 'testcase');

lib/plugins/cookie.edge.js

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
var path = require('path');
2+
3+
var log = require('../log');
4+
var Plugin = require('../plugin');
5+
var Queue = require('../queue');
6+
var session = require('../session');
7+
var config = require('../config');
8+
9+
// [Usage]
10+
//
11+
// https://github.com/skygragon/leetcode-cli-plugins/blob/master/docs/cookie.edge.md
12+
//
13+
var plugin = new Plugin(13, 'cookie.edge', '2018.11.18',
14+
'Plugin to reuse Edge\'s leetcode cookie.',
15+
['ffi:win32', 'keytar:darwin', 'ref:win32', 'ref-struct:win32', 'sqlite3']);
16+
17+
plugin.help = function() {
18+
switch (process.platform) {
19+
case 'darwin':
20+
break;
21+
case 'linux':
22+
log.warn('To complete the install: sudo apt install libsecret-tools');
23+
break;
24+
case 'win32':
25+
break;
26+
}
27+
};
28+
29+
var Chrome = {};
30+
31+
var ChromeMAC = {
32+
getDBPath: function() {
33+
return `${process.env.HOME}/Library/Application Support/Microsoft Edge/${this.profile}/Cookies`;
34+
},
35+
iterations: 1003,
36+
getPassword: function(cb) {
37+
var keytar = require('keytar');
38+
keytar.getPassword('Microsoft Edge Safe Storage', 'Microsoft Edge').then(cb);
39+
}
40+
};
41+
42+
var ChromeLinux = {
43+
getDBPath: function() {
44+
return `${process.env.HOME}/.config/google-edge/${this.profile}/Cookies`;
45+
},
46+
iterations: 1,
47+
getPassword: function(cb) {
48+
// FIXME: keytar failed to read gnome-keyring on ubuntu??
49+
var cmd = 'secret-tool lookup application edge';
50+
var password = require('child_process').execSync(cmd).toString();
51+
return cb(password);
52+
}
53+
};
54+
55+
var ChromeWindows = {
56+
getDBPath: function() {
57+
return path.resolve(process.env.APPDATA || '', `../Local/Google/Chrome/User Data/${this.profile}/Cookies`);
58+
},
59+
getPassword: function(cb) { cb(); }
60+
};
61+
62+
Object.setPrototypeOf(ChromeMAC, Chrome);
63+
Object.setPrototypeOf(ChromeLinux, Chrome);
64+
Object.setPrototypeOf(ChromeWindows, Chrome);
65+
66+
Chrome.getInstance = function() {
67+
switch (process.platform) {
68+
case 'darwin': return ChromeMAC;
69+
case 'linux': return ChromeLinux;
70+
case 'win32': return ChromeWindows;
71+
}
72+
};
73+
var my = Chrome.getInstance();
74+
75+
ChromeWindows.decodeCookie = function(cookie, cb) {
76+
var ref = require('ref');
77+
var ffi = require('ffi');
78+
var Struct = require('ref-struct');
79+
80+
var DATA_BLOB = Struct({
81+
cbData: ref.types.uint32,
82+
pbData: ref.refType(ref.types.byte)
83+
});
84+
var PDATA_BLOB = new ref.refType(DATA_BLOB);
85+
var Crypto = new ffi.Library('Crypt32', {
86+
'CryptUnprotectData': ['bool', [PDATA_BLOB, 'string', 'string', 'void *', 'string', 'int', PDATA_BLOB]]
87+
});
88+
89+
var inBlob = new DATA_BLOB();
90+
inBlob.pbData = cookie;
91+
inBlob.cbData = cookie.length;
92+
var outBlob = ref.alloc(DATA_BLOB);
93+
94+
Crypto.CryptUnprotectData(inBlob.ref(), null, null, null, null, 0, outBlob);
95+
var outDeref = outBlob.deref();
96+
var buf = ref.reinterpret(outDeref.pbData, outDeref.cbData, 0);
97+
98+
return cb(null, buf.toString('utf8'));
99+
};
100+
101+
Chrome.decodeCookie = function(cookie, cb) {
102+
var crypto = require('crypto');
103+
crypto.pbkdf2(my.password, 'saltysalt', my.iterations, 16, 'sha1', function(e, key) {
104+
if (e) return cb(e);
105+
106+
var iv = new Buffer(' '.repeat(16));
107+
var decipher = crypto.createDecipheriv('aes-128-cbc', key, iv);
108+
decipher.setAutoPadding(false);
109+
110+
var buf = decipher.update(cookie.slice(3)); // remove prefix "v10" or "v11"
111+
var final = decipher.final();
112+
final.copy(buf, buf.length - 1);
113+
114+
var padding = buf[buf.length - 1];
115+
if (padding) buf = buf.slice(0, buf.length - padding);
116+
117+
return cb(null, buf.toString('utf8'));
118+
});
119+
};
120+
121+
function doDecode(key, queue, cb) {
122+
var ctx = queue.ctx;
123+
var cookie = ctx[key];
124+
if (!cookie) return cb('Not found cookie: ' + key);
125+
126+
my.decodeCookie(cookie, function(e, cookie) {
127+
ctx[key] = cookie;
128+
return cb();
129+
});
130+
}
131+
132+
Chrome.getCookies = function(cb) {
133+
var sqlite3 = require('sqlite3');
134+
var db = new sqlite3.Database(my.getDBPath());
135+
db.on('error', cb);
136+
var KEYS = ['csrftoken', 'LEETCODE_SESSION'];
137+
138+
let host = config.sys.cookie_host;
139+
if (!host) {
140+
host = 'leetcode.com'; // default host key
141+
}
142+
143+
db.serialize(function() {
144+
var cookies = {};
145+
var sql = 'select name, encrypted_value from cookies where host_key like "%' + host +'"';
146+
db.each(sql, function(e, x) {
147+
if (e) return cb(e);
148+
if (KEYS.indexOf(x.name) < 0) return;
149+
cookies[x.name] = x.encrypted_value;
150+
});
151+
152+
db.close(function() {
153+
my.getPassword(function(password) {
154+
my.password = password;
155+
var q = new Queue(KEYS, cookies, doDecode);
156+
q.run(null, cb);
157+
});
158+
});
159+
});
160+
};
161+
162+
plugin.signin = function(user, cb) {
163+
log.debug('running cookie.edge.signin');
164+
log.debug('try to copy leetcode cookies from edge ...');
165+
166+
my.profile = plugin.config.profile || 'Default';
167+
my.getCookies(function(e, cookies) {
168+
if (e) {
169+
log.error(`Failed to copy cookies from profile "${my.profile}"`);
170+
log.error(e);
171+
return plugin.next.signin(user, cb);
172+
}
173+
174+
log.debug('Successfully copied leetcode cookies!');
175+
user.sessionId = cookies.LEETCODE_SESSION;
176+
user.sessionCSRF = cookies.csrftoken;
177+
session.saveUser(user);
178+
return cb(null, user);
179+
});
180+
};
181+
182+
plugin.login = function(user, cb) {
183+
log.debug('running cookie.edge.login');
184+
plugin.signin(user, function(e, user) {
185+
if (e) return cb(e);
186+
plugin.getUser(user, cb);
187+
});
188+
};
189+
190+
module.exports = plugin;

lib/plugins/leetcode.js

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -130,20 +130,21 @@ plugin.getProblem = function(problem, cb) {
130130
opts.json = true;
131131
opts.body = {
132132
query: [
133-
'query getQuestionDetail($titleSlug: String!) {',
133+
'query questionData($titleSlug: String!) {',
134134
' question(titleSlug: $titleSlug) {',
135135
' content',
136136
' stats',
137-
' codeDefinition',
137+
' codeSnippets {lang langSlug code}',
138138
' sampleTestCase',
139139
' enableRunCode',
140140
' metaData',
141+
' topicTags {name slug translatedName },',
141142
' translatedContent',
142143
' }',
143144
'}'
144145
].join('\n'),
145146
variables: {titleSlug: problem.slug},
146-
operationName: 'getQuestionDetail'
147+
operationName: 'questionData'
147148
};
148149

149150
const spin = h.spin('Downloading ' + problem.slug);
@@ -161,7 +162,14 @@ plugin.getProblem = function(problem, cb) {
161162
problem.desc = q.content;
162163
problem.translatedDesc = q.translatedContent ? q.translatedContent: q.content;
163164

164-
problem.templates = JSON.parse(q.codeDefinition);
165+
problem.templates = [];
166+
for (var i = 0; i < q.codeSnippets.length; i++) {
167+
problem.templates[i] = {};
168+
problem.templates[i].value = q.codeSnippets[i].langSlug;
169+
problem.templates[i].text = q.codeSnippets[i].lang;
170+
problem.templates[i].defaultCode = q.codeSnippets[i].code;
171+
}
172+
165173
problem.testcase = q.sampleTestCase;
166174
problem.testable = q.enableRunCode;
167175
problem.templateMeta = JSON.parse(q.metaData);
@@ -190,15 +198,18 @@ function runCode(opts, problem, cb) {
190198
const spin = h.spin('Sending code to judge');
191199
request(opts, function(e, resp, body) {
192200
spin.stop();
201+
202+
const isRespStatusCodeRetry = (resp.statusCode == 429);
203+
193204
e = plugin.checkError(e, resp, 200);
194-
if (e) return cb(e);
205+
if (e && !isRespStatusCodeRetry) return cb(e);
195206

196-
if (body.error) {
197-
if (!body.error.includes('too soon'))
207+
if (isRespStatusCodeRetry || body && body.error) {
208+
if (body && !body.error.includes('too soon'))
198209
return cb(body.error);
199210

200211
// hit 'run code too soon' error, have to wait a bit
201-
log.debug(body.error);
212+
body && log.debug(body.error);
202213

203214
// linear wait
204215
++opts._delay;
@@ -350,6 +361,9 @@ plugin.getSubmission = function(submission, cb) {
350361
});
351362
};
352363

364+
365+
366+
353367
plugin.starProblem = function(problem, starred, cb) {
354368
log.debug('running leetcode.starProblem');
355369
const opts = plugin.makeOpts();

0 commit comments

Comments
 (0)