8000 get CLI working · unl-pal/leetcode-cli@f34db81 · GitHub
[go: up one dir, main page]

Skip to content

Commit f34db81

Browse files
committed
get CLI working
1 parent 5245886 commit f34db81

File tree

4 files changed

+197
-5
lines changed

4 files changed

+197
-5
lines changed

lib/config.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ const DEFAULT_CONFIG = {
2828
'ruby',
2929
'rust',
3030
'scala',
31-
'swift'
31+
'swift',
32+
'typescript'
3233
],
3334
urls: {
3435
base: 'https://leetcode.com',

lib/helper.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ const LANGS = [
4545
{lang: 'ruby', ext: '.rb', style: '#'},
4646
{lang: 'rust', ext: '.rs', style: 'c'},
4747
{lang: 'scala', ext: '.scala', style: 'c'},
48-
{lang: 'swift', ext: '.swift', style: 'c'}
48+
{lang: 'swift', ext: '.swift', style: 'c'},
49+
{lang: 'typescript', ext: '.ts', style: 'c'}
4950
];
5051

5152
const h = {};

lib/plugins/cookie.chrome.js

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

lib/plugins/leetcode.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line chan 10000 ge
@@ -213,7 +213,8 @@ function runCode(opts, problem, cb) {
213213
opts.json = false;
214214
opts.body = null;
215215

216-
return cb(null, body);
216+
const reRun2 = _.partial(cb, null, body);
217+
return setTimeout(reRun2, opts._delay * 250);
217218
});
218219
}
219220

@@ -229,8 +230,12 @@ function verifyResult(task, queue, cb) {
229230
if (e) return cb(e);
230231

231232
let result = JSON.parse(body);
233+
console.log(task);
234+
console.log(result);
232235
if (result.state === 'SUCCESS') {
236+
console.log('DONE HERE')
233237
result = formatResult(result);
238+
console.log(result)
234239
_.extendOwn(result, task);
235240
queue.ctx.results.push(result);
236241
} else {
@@ -287,9 +292,10 @@ plugin.testProblem = function(problem, cb) {
287292
if (e) return cb(e);
288293

289294
const tasks = [
290-
{type: 'Actual', id: task.interpret_id},
291-
{type: 'Expected', id: task.interpret_expected_id}
295+
{type: 'Actual', id: task.interpret_id}
292296
];
297+
if (task.interpret_expected_id)
298+
tasks.push({type: 'Expected', id: task.interpret_expected_id});
293299
const q = new Queue(tasks, {opts: opts, results: []}, verifyResult);
294300
q.run(null, function(e, ctx) {
295301
return cb(e, ctx.results);

0 commit comments

Comments
 (0)
0