Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: skygragon/leetcode-cli
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: master
Choose a base ref
...
head repository: unl-pal/leetcode-cli
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref
  • 6 commits
  • 6 files changed
  • 1 contributor

Commits on Jul 13, 2024

  1. get CLI working

    psybers committed Jul 13, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    psybers Robert Dyer
    Copy the full SHA
    f34db81 View commit details
  2. handle internal failures

    psybers committed Jul 13, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    psybers Robert Dyer
    Copy the full SHA
    2858460 View commit details
  3. Verified

    This commit was signed with the committer’s verified signature.
    psybers Robert Dyer
    Copy the full SHA
    1ad457c View commit details

Commits on Jul 19, 2024

  1. better output

    psybers committed Jul 19, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    psybers Robert Dyer
    Copy the full SHA
    4345127 View commit details
  2. output in proper JSON

    psybers committed Jul 19, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    psybers Robert Dyer
    Copy the full SHA
    215905d View commit details

Commits on Nov 4, 2024

  1. Copy the full SHA
    fcfadc1 View commit details
Showing with 244 additions and 25 deletions.
  1. +23 −15 lib/commands/submit.js
  2. +13 −5 lib/commands/test.js
  3. +2 −1 lib/config.js
  4. +2 −1 lib/helper.js
  5. +190 −0 lib/plugins/cookie.chrome.js
  6. +14 −3 lib/plugins/leetcode.js
38 changes: 23 additions & 15 deletions lib/commands/submit.js
Original file line number Diff line number Diff line change
@@ -9,11 +9,21 @@ var core = require('../core');
var session = require('../session');

const cmd = {
command: 'submit <filename>',
command: 'submit <id> <lang> <filename>',
aliases: ['push', 'commit'],
desc: 'Submit code',
builder: function(yargs) {
return yargs
.positional('id', {
type: 'number',
default: '',
describe: 'Problem identifier number'
})
.positional('lang', {
type: 'string',
default: '',
describe: 'Programming language'
})
.positional('filename', {
type: 'string',
describe: 'Code file to submit',
@@ -46,22 +56,20 @@ cmd.handler = function(argv) {
if (!file.exist(argv.filename))
return log.fatal('File ' + argv.filename + ' not exist!');

const meta = file.meta(argv.filename);

core.getProblem(meta.id, function(e, problem) {
core.getProblem(argv.id, function(e, problem) {
if (e) return log.fail(e);

problem.file = argv.filename;
problem.lang = meta.lang;
problem.lang = argv.lang;

core.submitProblem(problem, function(e, results) {
if (e) return log.fail(e);

const result = results[0];

printResult(result, 'state');
printLine(result, '%d/%d cases passed (%s)',
result.passed, result.total, result.runtime);
//printResult(result, 'state');
//printLine(result, '%d/%d cases passed (%s)',
// result.passed, result.total, result.runtime);

if (result.ok) {
session.updateStat('ac', 1);
@@ -80,15 +88,15 @@ cmd.handler = function(argv) {
ratio += parseFloat(score[1]);
}

printLine(result, 'Your runtime beats %d %% of %s submissions',
ratio.toFixed(2), lang);
//printLine(result, 'Your runtime beats %d %% of %s submissions',
// ratio.toFixed(2), lang);
});
} else {
printResult(result, 'error');
printResult(result, 'testcase');
printResult(result, 'answer');
printResult(result, 'expected_answer');
printResult(result, 'stdout');
//printResult(result, 'error');
//printResult(result, 'testcase');
//printResult(result, 'answer');
//printResult(result, 'expected_answer');
//printResult(result, 'stdout');
}

// update this problem status in local cache
18 changes: 13 additions & 5 deletions lib/commands/test.js
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@ var core = require('../core');
var session = require('../session');

const cmd = {
command: 'test <filename>',
command: 'test <id> <lang> <filename>',
aliases: ['run'],
desc: 'Test code',
builder: function(yargs) {
@@ -26,6 +26,16 @@ const cmd = {
default: '',
describe: 'Provide test case'
})
.positional('id', {
type: 'number',
default: '',
describe: 'Problem identifier number'
})
.positional('lang', {
type: 'string',
default: '',
describe: 'Programming language'
})
.positional('filename', {
type: 'string',
default: '',
@@ -56,9 +66,7 @@ function runTest(argv) {
if (!file.exist(argv.filename))
return log.fatal('File ' + argv.filename + ' not exist!');

const meta = file.meta(argv.filename);

core.getProblem(meta.id, function(e, problem) {
core.getProblem(argv.id, function(e, problem) {
if (e) return log.fail(e);

if (!problem.testable)
@@ -71,7 +79,7 @@ function runTest(argv) {
return log.fail('missing testcase?');

problem.file = argv.filename;
problem.lang = meta.lang;
problem.lang = argv.lang;

log.info('\nInput data:');
log.info(problem.testcase);
3 changes: 2 additions & 1 deletion lib/config.js
Original file line number Diff line number Diff line change
@@ -28,7 +28,8 @@ const DEFAULT_CONFIG = {
'ruby',
'rust',
'scala',
'swift'
'swift',
'typescript'
],
urls: {
base: 'https://leetcode.com',
3 changes: 2 additions & 1 deletion lib/helper.js
Original file line number Diff line number Diff line change
@@ -45,7 +45,8 @@ const LANGS = [
{lang: 'ruby', ext: '.rb', style: '#'},
{lang: 'rust', ext: '.rs', style: 'c'},
{lang: 'scala', ext: '.scala', style: 'c'},
{lang: 'swift', ext: '.swift', style: 'c'}
{lang: 'swift', ext: '.swift', style: 'c'},
{lang: 'typescript', ext: '.ts', style: 'c'}
];

const h = {};
190 changes: 190 additions & 0 deletions lib/plugins/cookie.chrome.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
var path = require('path');

var log = require('../log');
var Plugin = require('../plugin');
var Queue = require('../queue');
var session = require('../session');

// [Usage]
//
// https://github.com/skygragon/leetcode-cli-plugins/blob/master/docs/cookie.chrome.md
//
var plugin = new Plugin(13, 'cookie.chrome', '2018.11.18',
'Plugin to reuse Chrome\'s leetcode cookie.',
['ffi:win32', 'keytar:darwin', 'ref:win32', 'ref-struct:win32', 'sqlite3']);

plugin.help = function() {
switch (process.platform) {
case 'darwin':
break;
case 'linux':
log.warn('To complete the install: sudo apt install libsecret-tools');
break;
case 'win32':
break;
}
};

var Chrome = {};

var ChromeMAC = {
getDBPath: function() {
return `${process.env.HOME}/Library/Application Support/Google/Chrome/${this.profile}/Cookies`;
},
iterations: 1003,
getPassword: function(cb) {
var keytar = require('keytar');
keytar.getPassword('Chrome Safe Storage', 'Chrome').then(cb);
}
};

var ChromeLinux = {
getDBPath: function() {
return `${process.env.HOME}/.config/google-chrome/${this.profile}/Cookies`;
},
iterations: 1,
getPassword: function(cb) {
// FIXME: keytar failed to read gnome-keyring on ubuntu??
var cmd = 'secret-tool lookup application chrome';
var password = require('child_process').execSync(cmd).toString();
return cb(password);
}
};

var ChromeWindows = {
getDBPath: function() {
return path.resolve(process.env.APPDATA || '', `../Local/Google/Chrome/User Data/${this.profile}/Cookies`);
},
getPassword: function(cb) { cb(); }
};

Object.setPrototypeOf(ChromeMAC, Chrome);
Object.setPrototypeOf(ChromeLinux, Chrome);
Object.setPrototypeOf(ChromeWindows, Chrome);

Chrome.getInstance = function() {
switch (process.platform) {
case 'darwin': return ChromeMAC;
case 'linux': return ChromeLinux;
case 'win32': return ChromeWindows;
}
};
var my = Chrome.getInstance();

ChromeWindows.decodeCookie = function(cookie, cb) {
var ref = require('ref');
var ffi = require('ffi');
var Struct = require('ref-struct');

var DATA_BLOB = Struct({
cbData: ref.types.uint32,
pbData: ref.refType(ref.types.byte)
});
var PDATA_BLOB = new ref.refType(DATA_BLOB);
var Crypto = new ffi.Library('Crypt32', {
'CryptUnprotectData': ['bool', [PDATA_BLOB, 'string', 'string', 'void *', 'string', 'int', PDATA_BLOB]]
});

var inBlob = new DATA_BLOB();
inBlob.pbData = cookie;
inBlob.cbData = cookie.length;
var outBlob = ref.alloc(DATA_BLOB);

Crypto.CryptUnprotectData(inBlob.ref(), null, null, null, null, 0, outBlob);
var outDeref = outBlob.deref();
var buf = ref.reinterpret(outDeref.pbData, outDeref.cbData, 0);

return cb(null, buf.toString('utf8'));
};

Chrome.decodeCookie = function(cookie, cb) {
var crypto = require('crypto');
crypto.pbkdf2(my.password, 'saltysalt', my.iterations, 16, 'sha1', function(e, key) {
if (e) return cb(e);

var iv = new Buffer(' '.repeat(16));
var decipher = crypto.createDecipheriv('aes-128-cbc', key, iv);
decipher.setAutoPadding(false);

var buf = decipher.update(cookie.slice(3)); // remove prefix "v10" or "v11"
var final = decipher.final();
final.copy(buf, buf.length - 1);

var padding = buf[buf.length - 1];
if (padding) buf = buf.slice(0, buf.length - padding);

var result = buf.toString('utf8');
if (result.match(/[^a-zA-Z0-9.-_]+/g)) {
buf = buf.slice(32, buf.length);
result = buf.toString('utf8');
}

return cb(null, result);
});
};

function doDecode(key, queue, cb) {
var ctx = queue.ctx;
var cookie = ctx[key];
if (!cookie) return cb('Not found cookie: ' + key);

my.decodeCookie(cookie, function(e, cookie) {
ctx[key] = cookie;
return cb();
});
}

Chrome.getCookies = function(cb) {
var sqlite3 = require('sqlite3');
var db = new sqlite3.Database(my.getDBPath());
db.on('error', cb);
var KEYS = ['csrftoken', 'LEETCODE_SESSION'];

db.serialize(function() {
var cookies = {};
var sql = 'select name, encrypted_value from cookies where host_key like "%leetcode.com"';
db.each(sql, function(e, x) {
if (e) return cb(e);
if (KEYS.indexOf(x.name) < 0) return;
cookies[x.name] = x.encrypted_value;
});

db.close(function() {
my.getPassword(function(password) {
my.password = password;
var q = new Queue(KEYS, cookies, doDecode);
q.run(null, cb);
});
});
});
};

plugin.signin = function(user, cb) {
log.debug('running cookie.chrome.signin');
log.debug('try to copy leetcode cookies from chrome ...');

my.profile = plugin.config.profile || 'Default';
my.getCookies(function(e, cookies) {
if (e) {
log.error(`Failed to copy cookies from profile "${my.profile}"`);
log.error(e);
return plugin.next.signin(user, cb);
}

log.debug('Successfully copied leetcode cookies!');
user.sessionId = cookies.LEETCODE_SESSION;
user.sessionCSRF = cookies.csrftoken;
session.saveUser(user);
return cb(null, user);
});
};

plugin.login = function(user, cb) {
log.debug('running cookie.chrome.login');
plugin.signin(user, function(e, user) {
if (e) return cb(e);
plugin.getUser(user, cb);
});
};

module.exports = plugin;
17 changes: 14 additions & 3 deletions lib/plugins/leetcode.js
Original file line number Diff line number Diff line change
@@ -213,7 +213,8 @@ function runCode(opts, problem, cb) {
opts.json = false;
opts.body = null;

return cb(null, body);
const reRun2 = _.partial(cb, null, body);
return setTimeout(reRun2, opts._delay * 250);
});
}

@@ -229,11 +230,20 @@ function verifyResult(task, queue, cb) {
if (e) return cb(e);

let result = JSON.parse(body);
console.error(task);
if (result.state === 'SUCCESS') {
console.error('DONE HERE')
console.log(JSON.stringify(result))
result = formatResult(result);
_.extendOwn(result, task);
queue.ctx.results.push(result);
} else if (result.state === 'FAILURE') {
console.error('FAILED')
console.log(JSON.stringify(result))
_.extendOwn(result, task);
queue.ctx.results.push(result);
} else {
console.error(result);
queue.addTask(task);
}
return cb();
@@ -287,9 +297,10 @@ plugin.testProblem = function(problem, cb) {
if (e) return cb(e);

const tasks = [
{type: 'Actual', id: task.interpret_id},
{type: 'Expected', id: task.interpret_expected_id}
{type: 'Actual', id: task.interpret_id}
];
if (task.interpret_expected_id)
tasks.push({type: 'Expected', id: task.interpret_expected_id});
const q = new Queue(tasks, {opts: opts, results: []}, verifyResult);
q.run(null, function(e, ctx) {
return cb(e, ctx.results);