UNPKG

leetcode-fetcher-cli

Version:

A CLi Application for local fetching of leetcode problems

231 lines (230 loc) 9.82 kB
"use strict"; /** * @author Riccardo La Marca * * @brief User-releated commands: * - login [Login into leetcode to start a session] * - inspect [Inspect the overall statistics of a user, or the current logged one] */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.inspect_command = exports.login_command = void 0; const crypto_1 = require("crypto"); const pprint_1 = require("../pprint"); const general_1 = require("../utils/general"); const formatter_1 = require("../utils/formatter"); const printer_1 = require("../utils/printer"); const lc = __importStar(require("../leetcode")); const constants_1 = __importDefault(require("../constants")); const chalk_1 = __importDefault(require("chalk")); const SetLoginDetails = (state, params, user_str, pwd_str) => { let content = {}; for (const [key, value] of params.entries()) { if ([user_str, pwd_str].includes(key)) { content[key] = value; } } // Creates the salt and hash the password const salt = (0, crypto_1.randomBytes)(16).toString('hex'); const hash_pwd = (0, general_1.HashPassword)(content[pwd_str], salt); state.userLogin = { username: content[user_str], password: hash_pwd, salt: salt }; state.selectedUser = content[user_str]; }; const HandleResponse = async (state, response) => { // Classical login through LeetCode if (response.url().startsWith(constants_1.default.SITES.LOGIN_PAGE.URL)) { const request = response.request(); // Get the corresponding request // We dont want the navigation request since most likely it would // be the naviagation request we have done when opening the page. // Moreover, we also need the response to be successful and the // request to be a POST request, thus with POST data. if (request.isNavigationRequest() || !request.hasPostData() || !response.ok()) return; // Get the username and the password provided and compute the hash const result = await response.json(); const username = result.form.fields.login.value; const password = result.form.fields.password.value; const salt = (0, crypto_1.randomBytes)(16).toString('hex'); const hash_pawd = (0, general_1.HashPassword)(password, salt); state.userLogin = { username: username, password: hash_pawd, salt: salt }; state.selectedUser = username; const headers = response.headers(); const matches = headers['set-cookie'].matchAll(/^([\w_]+)=([^;]+)/gm); state.cookies = { csrftoken: "", messages: "", LEETCODE_SESSION: "" }; Array.from(matches).forEach((value) => { state.cookies[value[1]] = value[2]; }); return; } // Login using Github if (response.url().startsWith(constants_1.default.SITES.THIRD_PARTY.GITHUB.SESSION)) { const response_status_code = response.status(); const request = response.request(); // If the response is not a redirect or the request has no post data if (response_status_code !== 302 || !request.hasPostData()) return; const request_post_data = request.postData(); const params = new URLSearchParams(request_post_data); // From the (key, value) pairs select those corresponding to username and password SetLoginDetails(state, params, "login", "password"); return; } if ((response.url().startsWith(constants_1.default.SITES.THIRD_PARTY.GITHUB.CALLBACK) || response.url().startsWith(constants_1.default.SITES.THIRD_PARTY.LINKEDIN.CALLBACK))) { const headers = response.headers(); const matches = headers['set-cookie'].matchAll(/^([\w_]+)=([^;]+)/gm); state.cookies = { csrftoken: "", messages: "", LEETCODE_SESSION: "" }; Array.from(matches).forEach((value) => { state.cookies[value[1]] = value[2]; }); return; } // Login using Linkedin if (response.url().startsWith(constants_1.default.SITES.THIRD_PARTY.LINKEDIN.SESSION)) { const response_status_code = response.status(); const request = response.request(); // If the response is not a redirect or the request has no post data if (response_status_code !== 303 || !request.hasPostData()) return; const request_post_data = request.postData(); const params = new URLSearchParams(request_post_data); SetLoginDetails(state, params, "session_key", "session_password"); return; } }; const HandleRequest = async (request) => { if (request.isInterceptResolutionHandled()) return; if (request.url() !== constants_1.default.SITES.HOME_PAGE.URL) { request.continue(); return; } request.respond(constants_1.default.SITES.HOME_PAGE.REDIRECT); }; const CheckUserSession = async (spinner, state) => { if (state.userLogin !== undefined && state.cookies !== undefined) { spinner.changeMessage("Session found. Checking validity"); spinner.start(); const result = await lc.CheckUserSession(state.cookies); spinner.stop(); if (result) { console.log((0, formatter_1.FormatString)("\rThe current {0} is still {1}!", chalk_1.default.bold("session"), chalk_1.default.greenBright("active"))); } else { state.userLogin = undefined; state.cookies = undefined; } } }; const LoginCommand = async (data, state) => { const spinner = new pprint_1.Spinner("User Logging in"); const forced = data[0] !== undefined; if (!forced) await CheckUserSession(spinner, state); else { console.warn(chalk_1.default.yellowBright("[WARNING] Login has been forced. No check on current session.")); } if (!(state.userLogin && state.cookies) || forced) { spinner.changeMessage("User Logging in"); spinner.start(); try { await (0, general_1.OpenLoginBrowser)((r) => HandleResponse(state, r), HandleRequest); } catch (error) { } spinner.stop(); } if (!(state.userLogin && state.cookies)) { console.log(chalk_1.default.red('User login has been stopped.')); state.userLogin = undefined; state.cookies = undefined; return state; } let headers = (0, formatter_1.FormatCookies)(state.cookies); const username = await lc.FetchUsername(headers); if (!username) { console.error(chalk_1.default.redBright(`User ${state.userLogin.username} does not have access.`)); state.userLogin = undefined; state.cookies = undefined; return state; } state.userLogin.username = username; const success = chalk_1.default.greenBright("successfully"); console.log(`User ${username} has ${success} logged.`); // Fetch user details const user_data = await lc.GetUserData(state.userLogin.username, state); state.profile = user_data; if (user_data) (0, printer_1.PrintUserSummary)(user_data); return state; }; // Login command - Login into leetcode to start a session exports.login_command = { group: 'User', name: 'Login Command', command: 'login', syntax: /^login(?:\s+(force))?$/, callback: LoginCommand, help: 'login [force] - Login into leetcode to start a session. `force` parameter\n' + 'forces the application to attempt the login instead of checking the\n' + 'current session validity (if any).' }; const InspectCommand = async (data, state) => { if (data.length < 1) { // Check if a user is logged if (!state.selectedUser) { console.error(chalk_1.default.redBright("No current logged user. A username must be provided.")); return state; } data = [state.selectedUser]; } const result = await lc.GetUserData(data[0], state); if (result) (0, printer_1.PrintUserSummary)(result); return state; }; // Inspect command - Inspect a user exports.inspect_command = { group: 'User', name: 'Inspect Command', command: 'inspect', syntax: /^inspect(?:\s(.*?))$/, callback: InspectCommand, help: 'inspect [USERNAME] - Inspect a given username if it exists' + ', or the current logged one.\n' };