diff --git a/lib/config.js b/lib/config.js index 049d20d4..9fc26945 100644 --- a/lib/config.js +++ b/lib/config.js @@ -31,25 +31,36 @@ const DEFAULT_CONFIG = { 'swift' ], urls: { - base: 'https://leetcode.com', - graphql: 'https://leetcode.com/graphql', - login: 'https://leetcode.com/accounts/login/', - // third part login base urls. TODO facebook google - github_login: 'https://leetcode.com/accounts/github/login/?next=%2F', - facebook_login: 'https://leetcode.com/accounts/facebook/login/?next=%2F', - linkedin_login: 'https://leetcode.com/accounts/linkedin_oauth2/login/?next=%2F', - problems: 'https://leetcode.com/api/problems/$category/', - problem: 'https://leetcode.com/problems/$slug/description/', - test: 'https://leetcode.com/problems/$slug/interpret_solution/', - session: 'https://leetcode.com/session/', - submit: 'https://leetcode.com/problems/$slug/submit/', - submissions: 'https://leetcode.com/api/submissions/$slug', - submission: 'https://leetcode.com/submissions/detail/$id/', - verify: 'https://leetcode.com/submissions/detail/$id/check/', - favorites: 'https://leetcode.com/list/api/questions', - favorite_delete: 'https://leetcode.com/list/api/questions/$hash/$id', - plugin: 'https://raw.githubusercontent.com/leetcode-tools/leetcode-cli-plugins/master/plugins/$name.js' - } + // base urls + base: 'https://leetcode.com', + graphql: 'https://leetcode.com/graphql', + login: 'https://leetcode.com/accounts/login/', + // third part login base urls. TODO facebook google + github_login: 'https://leetcode.com/accounts/github/login/?next=%2F', + facebook_login: 'https://leetcode.com/accounts/facebook/login/?next=%2F', + linkedin_login: 'https://leetcode.com/accounts/linkedin_oauth2/login/?next=%2F', + // redirect urls + leetcode_redirect: 'https://leetcode.com/', + github_tf_redirect: 'https://github.com/sessions/two-factor', + // simulate login urls + github_login_request: 'https://github.com/login', + github_session_request: 'https://github.com/session', + github_tf_session_request: 'https://github.com/sessions/two-factor', + linkedin_login_request: 'https://www.linkedin.com', + linkedin_session_request: 'https://www.linkedin.com/uas/login-submit', + // questions urls + problems: 'https://leetcode.com/api/problems/$category/', + problem: 'https://leetcode.com/problems/$slug/description/', + test: 'https://leetcode.com/problems/$slug/interpret_solution/', + session: 'https://leetcode.com/session/', + submit: 'https://leetcode.com/problems/$slug/submit/', + submissions: 'https://leetcode.com/api/submissions/$slug', + submission: 'https://leetcode.com/submissions/detail/$id/', + verify: 'https://leetcode.com/submissions/detail/$id/check/', + favorites: 'https://leetcode.com/list/api/questions', + favorite_delete: 'https://leetcode.com/list/api/questions/$hash/$id', + plugin: 'https://raw.githubusercontent.com/leetcode-tools/leetcode-cli-plugins/master/plugins/$name.js' + }, }, // but you will want change these diff --git a/lib/plugins/leetcode.cn.js b/lib/plugins/leetcode.cn.js index 4df528f3..07c78f98 100644 --- a/lib/plugins/leetcode.cn.js +++ b/lib/plugins/leetcode.cn.js @@ -17,20 +17,24 @@ var plugin = new Plugin(15, 'leetcode.cn', '2018.11.25', plugin.init = function() { config.app = 'leetcode.cn'; - config.sys.urls.base = 'https://leetcode-cn.com'; - config.sys.urls.login = 'https://leetcode-cn.com/accounts/login/'; - config.sys.urls.problems = 'https://leetcode-cn.com/api/problems/$category/'; - config.sys.urls.problem = 'https://leetcode-cn.com/problems/$slug/description/'; - config.sys.urls.graphql = 'https://leetcode-cn.com/graphql'; - config.sys.urls.problem_detail = 'https://leetcode-cn.com/graphql'; - config.sys.urls.test = 'https://leetcode-cn.com/problems/$slug/interpret_solution/'; - config.sys.urls.session = 'https://leetcode-cn.com/session/'; - config.sys.urls.submit = 'https://leetcode-cn.com/problems/$slug/submit/'; - config.sys.urls.submissions = 'https://leetcode-cn.com/api/submissions/$slug'; - config.sys.urls.submission = 'https://leetcode-cn.com/submissions/detail/$id/'; - config.sys.urls.verify = 'https://leetcode-cn.com/submissions/detail/$id/check/'; - config.sys.urls.favorites = 'https://leetcode-cn.com/list/api/questions'; - config.sys.urls.favorite_delete = 'https://leetcode-cn.com/list/api/questions/$hash/$id'; + config.sys.urls.base = 'https://leetcode-cn.com'; + config.sys.urls.login = 'https://leetcode-cn.com/accounts/login/'; + config.sys.urls.problems = 'https://leetcode-cn.com/api/problems/$category/'; + config.sys.urls.problem = 'https://leetcode-cn.com/problems/$slug/description/'; + config.sys.urls.graphql = 'https://leetcode-cn.com/graphql'; + config.sys.urls.problem_detail = 'https://leetcode-cn.com/graphql'; + config.sys.urls.test = 'https://leetcode-cn.com/problems/$slug/interpret_solution/'; + config.sys.urls.session = 'https://leetcode-cn.com/session/'; + config.sys.urls.submit = 'https://leetcode-cn.com/problems/$slug/submit/'; + config.sys.urls.submissions = 'https://leetcode-cn.com/api/submissions/$slug'; + config.sys.urls.submission = 'https://leetcode-cn.com/submissions/detail/$id/'; + config.sys.urls.verify = 'https://leetcode-cn.com/submissions/detail/$id/check/'; + config.sys.urls.favorites = 'https://leetcode-cn.com/list/api/questions'; + config.sys.urls.favorite_delete = 'https://leetcode-cn.com/list/api/questions/$hash/$id'; + // third parties + config.sys.urls.github_login = 'https://leetcode-cn.com/accounts/github/login/?next=%2F'; + config.sys.urls.linkedin_login = 'https://leetcode-cn.com/accounts/linkedin_oauth2/login/?next=%2F'; + config.sys.urls.leetcode_redirect = 'https://leetcode-cn.com/'; }; // FIXME: refactor those diff --git a/lib/plugins/leetcode.js b/lib/plugins/leetcode.js index 004a6034..12d7ccbe 100644 --- a/lib/plugins/leetcode.js +++ b/lib/plugins/leetcode.js @@ -333,7 +333,7 @@ plugin.getSubmissions = function(problem, cb) { // FIXME: this only return the 1st 20 submissions, we should get next if necessary. const submissions = JSON.parse(body).submissions_dump; - for (let submission of submissions) + for (const submission of submissions) submission.id = _.last(_.compact(submission.url.split('/'))); return cb(null, submissions); @@ -471,8 +471,8 @@ plugin.deleteSession = function(session, cb) { }; plugin.signin = function(user, cb) { - log.debug('running leetcode.signin'); - const spin = h.spin('Signing in leetcode.com'); + const isCN = config.app === 'leetcode.cn'; + const spin = isCN ? h.spin('Signing in leetcode-cn.com') : h.spin('Signing in leetcode.com'); request(config.sys.urls.login, function(e, resp, body) { spin.stop(); e = plugin.checkError(e, resp, 200); @@ -538,11 +538,18 @@ plugin.login = function(user, cb) { }); }; -function parseCookie(cookie, cb) { +function parseCookie(cookie, body, cb) { + const isCN = config.app === 'leetcode.cn'; const SessionPattern = /LEETCODE_SESSION=(.+?)(;|$)/; - const csrfPattern = /csrftoken=(.+?)(;|$)/; + let csrfPattern; + // leetcode-cn.com Cookie is not the same as leetcode.com in third parties + if (isCN) { + csrfPattern = /name="csrfmiddlewaretoken" value="(.*?)"/; + } else { + csrfPattern = /csrftoken=(.+?)(;|$)/; + } const reSessionResult = SessionPattern.exec(cookie); - const reCsrfResult = csrfPattern.exec(cookie); + const reCsrfResult = csrfPattern.exec(isCN? body: cookie); if (reSessionResult === null || reCsrfResult === null) { return cb('invalid cookie?'); } @@ -552,11 +559,18 @@ function parseCookie(cookie, cb) { }; } -function saveAndGetUser(user, cb, cookieData) { - user.sessionId = cookieData.sessionId; - user.sessionCSRF = cookieData.sessionCSRF; - session.saveUser(user); - plugin.getUser(user, cb); +function requestLeetcodeAndSave(request, leetcodeUrl, user, cb) { + request.get({url: leetcodeUrl}, function(e, resp, body) { + const redirectUri = resp.request.uri.href; + if (redirectUri !== config.sys.urls.leetcode_redirect) { + return cb('Login failed. Please make sure the credential is correct.'); + } + const cookieData = parseCookie(resp.request.headers.cookie, body, cb); + user.sessionId = cookieData.sessionId; + user.sessionCSRF = cookieData.sessionCSRF; + session.saveUser(user); + plugin.getUser(user, cb); + }); } plugin.cookieLogin = function(user, cb) { @@ -568,15 +582,16 @@ plugin.cookieLogin = function(user, cb) { }; plugin.githubLogin = function(user, cb) { - const leetcodeUrl = config.sys.urls.github_login; + const urls = config.sys.urls; + const leetcodeUrl = urls.github_login; const _request = request.defaults({jar: true}); - _request('https://github.com/login', function(e, resp, body) { + _request(urls.github_login_request, function(e, resp, body) { const authenticityToken = body.match(/name="authenticity_token" value="(.*?)"/); if (authenticityToken === null) { return cb('Get GitHub token failed'); } const options = { - url: 'https://github.com/session', + url: urls.github_session_request, method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', @@ -594,27 +609,48 @@ plugin.githubLogin = function(user, cb) { if (resp.statusCode !== 200) { return cb('GitHub login failed'); } - _request.get({url: leetcodeUrl}, function(e, resp, body) { - const redirectUri = resp.request.uri.href; - if (redirectUri !== 'https://leetcode.com/') { - return cb('GitHub login failed or GitHub did not link to LeetCode'); + if (resp.request.uri.href !== urls.github_tf_redirect) { + return requestLeetcodeAndSave(_request, leetcodeUrl, user, cb); + } + // read two-factor code must be sync. + const twoFactorcode = require('prompt-sync')()('Please enter your two-factor code: '); + const authenticityTokenTwoFactor = body.match(/name="authenticity_token" value="(.*?)"/); + if (authenticityTokenTwoFactor === null) { + return cb('Get GitHub two-factor token failed'); + } + const optionsTwoFactor = { + url: urls.github_tf_session_request, + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + followAllRedirects: true, + form: { + 'otp': twoFactorcode, + 'authenticity_token': authenticityTokenTwoFactor[1], + 'utf8': encodeURIComponent('✓'), + }, + }; + _request(optionsTwoFactor, function(e, resp, body) { + if (resp.request.uri.href === urls.github_tf_session_request) { + return cb('Invalid two-factor code please check'); } - const cookieData = parseCookie(resp.request.headers.cookie, cb); - saveAndGetUser(user, cb, cookieData); + requestLeetcodeAndSave(_request, leetcodeUrl, user, cb); }); }); }); }; plugin.linkedinLogin = function(user, cb) { - const leetcodeUrl = config.sys.urls.linkedin_login; + const urls = config.sys.urls; + const leetcodeUrl = urls.linkedin_login; const _request = request.defaults({ jar: true, headers: { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36' } }); - _request('https://www.linkedin.com', function(e, resp, body) { + _request(urls.linkedin_login_request, function(e, resp, body) { if ( resp.statusCode !== 200) { return cb('Get LinkedIn session failed'); } @@ -623,7 +659,7 @@ plugin.linkedinLogin = function(user, cb) { return cb('Get LinkedIn token failed'); } const options = { - url: 'https://www.linkedin.com/uas/login-submit', + url: urls.linkedin_session_request, method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', @@ -640,14 +676,7 @@ plugin.linkedinLogin = function(user, cb) { if (resp.statusCode !== 200) { return cb('LinkedIn login failed'); } - _request.get({url: leetcodeUrl}, function(e, resp, body) { - const redirectUri = resp.request.uri.href; - if (redirectUri !== 'https://leetcode.com/') { - return cb('LinkedIn login failed or LinkedIn did not link to LeetCode'); - } - const cookieData = parseCookie(resp.request.headers.cookie, cb); - saveAndGetUser(user, cb, cookieData); - }); + requestLeetcodeAndSave(_request, leetcodeUrl, user, cb); }); }); }; diff --git a/package.json b/package.json index 3d8286e5..14371304 100644 --- a/package.json +++ b/package.json @@ -71,6 +71,7 @@ "nock": "10.0.2", "nyc": "^13.3.0", "pkg": "^4.3.4", + "prompt-sync": "^4.2.0", "rewire": "4.0.1" } }
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: