diff --git a/CHANGELOG.md b/CHANGELOG.md index f062c17..0a1da1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## version 2.18.2 + +- 修改部分登录流程 +- 部分请求使用 asiox 替换 request + ## version 2.18.1 - 增加周赛场次信息展示 diff --git a/package-lock.json b/package-lock.json index 7147e8b..17b5060 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,16 @@ { "name": "vscode-leetcode-problem-rating", - "version": "2.14.2", + "version": "2.19.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "vscode-leetcode-problem-rating", - "version": "2.14.2", + "version": "2.19.1", "license": "MIT", "dependencies": { "ansi-styles": "3.2.1", + "axios": "^1.3.4", "cheerio": "1.0.0-rc.12", "fs-extra": "^10.0.0", "he": "1.2.0", @@ -580,6 +581,29 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==" }, + "node_modules/axios": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz", + "integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1395,6 +1419,25 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -2297,6 +2340,11 @@ "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==" }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", @@ -3478,6 +3526,28 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==" }, + "axios": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz", + "integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==", + "requires": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + }, + "dependencies": { + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } + } + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -4095,6 +4165,11 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, + "follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" + }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -4773,6 +4848,11 @@ } } }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", diff --git a/package.json b/package.json index fd17b0d..1551cbd 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "vscode-leetcode-problem-rating", "displayName": "LeetCode", "description": "%main.description%", - "version": "2.18.1", + "version": "2.19.1", "author": "ccagml", "publisher": "ccagml", "license": "MIT", @@ -28,7 +28,7 @@ "preview": true, "activationEvents": [ "onCommand:lcpr.toggleLeetCodeCn", - "onCommand:lcpr.signin", + "onCommand:lcpr.newlogin", "onCommand:lcpr.signout", "onCommand:lcpr.refreshExplorer", "onCommand:lcpr.pickOne", @@ -57,8 +57,8 @@ "icon": "$(globe)" }, { - "command": "lcpr.signin", - "title": "%main.contributes.commands.lcpr.signin.title%", + "command": "lcpr.newlogin", + "title": "%main.contributes.commands.lcpr.newlogin.title%", "category": "LeetCode", "icon": "$(sign-in)" }, @@ -355,7 +355,7 @@ "group": "navigation@0" }, { - "command": "lcpr.signin", + "command": "lcpr.newlogin", "when": "view == QuestionExplorer", "group": "navigation@1" }, @@ -1165,6 +1165,7 @@ }, "dependencies": { "ansi-styles": "3.2.1", + "axios": "^1.3.4", "cheerio": "1.0.0-rc.12", "fs-extra": "^10.0.0", "he": "1.2.0", diff --git a/package.nls.json b/package.nls.json index 7e92505..e3ee15c 100644 --- a/package.nls.json +++ b/package.nls.json @@ -3,7 +3,7 @@ "main.name": "vscode-leetcode-problem-rating", "main.contributes.commands.lcpr.deleteCache.title": "Delete Cache", "main.contributes.commands.lcpr.toggleLeetCodeCn.title": "Switch Endpoint", - "main.contributes.commands.lcpr.signin.title": "Sign In", + "main.contributes.commands.lcpr.newlogin.title": "Sign In", "main.contributes.commands.lcpr.signout.title": "Sign Out", "main.contributes.commands.lcpr.refreshExplorer.title": "refresh", "main.contributes.commands.lcpr.pickOne.title": "pickOne", diff --git a/package.nls.zh-cn.json b/package.nls.zh-cn.json index 7c5f804..95fb9f5 100644 --- a/package.nls.zh-cn.json +++ b/package.nls.zh-cn.json @@ -3,7 +3,7 @@ "main.name": "vscode-leetcode-problem-rating", "main.contributes.commands.lcpr.deleteCache.title": "删除缓存", "main.contributes.commands.lcpr.toggleLeetCodeCn.title": "切换站点", - "main.contributes.commands.lcpr.signin.title": "登录", + "main.contributes.commands.lcpr.newlogin.title": "登录", "main.contributes.commands.lcpr.signout.title": "登出", "main.contributes.commands.lcpr.refreshExplorer.title": "刷新", "main.contributes.commands.lcpr.pickOne.title": "手气一下", diff --git a/resources/bin/leetcode b/resources/bin/leetcode index 02f42bd..25d197c 100755 --- a/resources/bin/leetcode +++ b/resources/bin/leetcode @@ -1,3 +1,3 @@ #!/usr/bin/env node -require("../../out/src/childProcessCall/childMain"); +require("../../out/src/rpc/childMain"); diff --git a/resources/templates/codeonly.tpl b/resources/templates/codeonly.tpl index 1c8ee25..0cc46e0 100644 --- a/resources/templates/codeonly.tpl +++ b/resources/templates/codeonly.tpl @@ -1,6 +1,6 @@ <%=comment.start%> <%=comment.line%> @lc app=<%=app%> id=<%=fid%> lang=<%=lang%> -<%=comment.line%> @lcpr version=<%=LCPTCTX.version%> +<%=comment.line%> @lcpr version=<%=LCPRCTX.version%> <%=comment.line%> <%=comment.line%> [<%=fid%>] <%=name%> <%=comment.end%> diff --git a/resources/templates/detailed.tpl b/resources/templates/detailed.tpl index 8d2cd66..2ea653e 100644 --- a/resources/templates/detailed.tpl +++ b/resources/templates/detailed.tpl @@ -1,6 +1,6 @@ <%=comment.start%> <%=comment.line%> @lc app=<%=app%> id=<%=fid%> lang=<%=lang%> -<%=comment.line%> @lcpr version=<%=LCPTCTX.version%> +<%=comment.line%> @lcpr version=<%=LCPRCTX.version%> <%=comment.line%> <%=comment.line%> [<%=fid%>] <%=name%> <%=comment.line%> diff --git a/src/controller/LoginController.ts b/src/controller/LoginController.ts index 5054a0f..02d6792 100644 --- a/src/controller/LoginController.ts +++ b/src/controller/LoginController.ts @@ -10,7 +10,7 @@ import * as cp from "child_process"; import * as systemUtils from "../utils/SystemUtils"; import { executeService } from "../service/ExecuteService"; -import { OutPutType, Endpoint, IQuickItemEx, loginArgsMapping, UserStatus } from "../model/Model"; +import { OutPutType, Endpoint, IQuickItemEx, UserStatus } from "../model/Model"; import { createEnvOption } from "../utils/CliUtils"; import { logOutput, promptForOpenOutputChannel } from "../utils/OutputUtils"; import { eventService } from "../service/EventService"; @@ -23,35 +23,32 @@ import { bricksDataService } from "../service/BricksDataService"; // 登录控制器 class LoginContorller { constructor() {} - commandArg: string | undefined; + loginMethod: string; - public async getUserName(): Promise { + public async getUserName(login_info: any): Promise { let result: string = ""; await window.withProgress({ location: ProgressLocation.Notification }, async (p: Progress<{}>) => { return new Promise( async (resolve: (res: string | undefined) => void, reject: (e: Error) => void): Promise => { - if (this.commandArg == undefined) { - reject(new Error("not commandArg")); - return; - } const leetCodeBinaryPath: string = await executeService.getLeetCodeBinaryPath(); let childProc: cp.ChildProcess; if (systemUtils.useVscodeNode()) { - childProc = cp.fork(await executeService.getLeetCodeBinaryPath(), ["user", this.commandArg], { + let newargs: string[] = ["user", login_info.cmd]; + childProc = cp.fork(leetCodeBinaryPath, newargs, { silent: true, - env: createEnvOption(), + env: createEnvOption(JSON.stringify(login_info)), }); } else { if (systemUtils.useWsl()) { - childProc = cp.spawn("wsl", [executeService.node, leetCodeBinaryPath, "user", this.commandArg], { - shell: true, - }); + let newargs: string[] = [executeService.node, leetCodeBinaryPath, "user", login_info.cmd]; + childProc = cp.spawn("wsl", newargs, { shell: true, env: createEnvOption(JSON.stringify(login_info)) }); } else { - childProc = cp.spawn(executeService.node, [leetCodeBinaryPath, "user", this.commandArg], { + let newargs: string[] = [leetCodeBinaryPath, "user", login_info.cmd]; + childProc = cp.spawn(executeService.node, newargs, { shell: true, - env: createEnvOption(), + env: createEnvOption(JSON.stringify(login_info)), }); } } @@ -92,31 +89,6 @@ class LoginContorller { childProc.stderr?.on("data", (data: string | Buffer) => logOutput.append(data.toString())); childProc.on("error", reject); - - const name: string | undefined = await window.showInputBox({ - prompt: "Enter username or E-mail.", - ignoreFocusOut: true, - validateInput: (s: string): string | undefined => - s && s.trim() ? undefined : "The input must not be empty", - }); - if (!name) { - childProc.kill(); - return resolve(undefined); - } - childProc.stdin?.write(`${name}\n`); - const isByCookie: boolean = this.loginMethod === "Cookie"; - const pwd: string | undefined = await window.showInputBox({ - prompt: isByCookie ? "Enter cookie" : "Enter password.", - password: true, - ignoreFocusOut: true, - validateInput: (s: string): string | undefined => - s ? undefined : isByCookie ? "Cookie must not be empty" : "Password must not be empty", - }); - if (!pwd) { - childProc.kill(); - return resolve(undefined); - } - childProc.stdin?.write(`${pwd}\n`); p.report({ message: "正在登录中~~~~" }); } ); @@ -127,7 +99,7 @@ class LoginContorller { /* A login function. */ // 登录操作 - public async signIn(): Promise { + public async NewLogin(): Promise { const picks: Array> = []; let qpOpiton: QuickPickOptions = { title: "正在登录leetcode.com", @@ -140,6 +112,7 @@ class LoginContorller { label: "LeetCode Account", detail: "只能登录leetcode.cn", value: "LeetCode", + cmd: "-l", }); qpOpiton.title = "正在登录中文版leetcode.cn"; qpOpiton.placeHolder = "请选择登录方式 正在登录中文版leetcode.cn"; @@ -149,16 +122,19 @@ class LoginContorller { label: "Third-Party: GitHub", detail: "Use GitHub account to login", value: "GitHub", + cmd: "-g", }, { label: "Third-Party: LinkedIn", detail: "Use LinkedIn account to login", value: "LinkedIn", + cmd: "-i", }, { label: "LeetCode Cookie", detail: "Use LeetCode cookie copied from browser to login", value: "Cookie", + cmd: "-c", } ); const choice: IQuickItemEx | undefined = await window.showQuickPick(picks, qpOpiton); @@ -166,20 +142,46 @@ class LoginContorller { return; } this.loginMethod = choice.value; - this.commandArg = loginArgsMapping.get(this.loginMethod); - if (!this.commandArg) { + + if (!choice.cmd) { throw new Error(`不支持 "${this.loginMethod}" 方式登录`); + return; } + + let login_info: any = {}; + login_info.cmd = choice.cmd; + + const name: string | undefined = await window.showInputBox({ + prompt: "Enter username or E-mail.", + ignoreFocusOut: true, + validateInput: (s: string): string | undefined => (s && s.trim() ? undefined : "The input must not be empty"), + }); + if (!name) { + return; + } + login_info.name = name; + const isByCookie: boolean = this.loginMethod === "Cookie"; - const inMessage: string = isByCookie ? " 通过cookie登录" : "登录"; + const passWord: string | undefined = await window.showInputBox({ + prompt: isByCookie ? "Enter cookie" : "Enter password.", + password: true, + ignoreFocusOut: true, + validateInput: (s: string): string | undefined => + s ? undefined : isByCookie ? "Cookie must not be empty" : "Password must not be empty", + }); + if (!passWord) { + return; + } + login_info.pass = passWord; + try { - const userName: string | undefined = await this.getUserName(); + const userName: string | undefined = await this.getUserName(login_info); if (userName) { eventService.emit("statusChanged", UserStatus.SignedIn, userName); - window.showInformationMessage(`${inMessage} 成功`); + window.showInformationMessage(`登录成功`); } } catch (error) { - promptForOpenOutputChannel(`${inMessage}失败. 请看看控制台输出信息`, OutPutType.error); + promptForOpenOutputChannel(`登录失败. 请看看控制台输出信息`, OutPutType.error); } } diff --git a/src/controller/MainController.ts b/src/controller/MainController.ts index 0f02b97..53d380e 100644 --- a/src/controller/MainController.ts +++ b/src/controller/MainController.ts @@ -58,7 +58,7 @@ class MainContorller { cur_version_num *= 100; cur_version_num += Number(e); }); - logOutput.setLCPTCTX("version", cur_version_num); + logOutput.setLCPRCTX("version", cur_version_num); } // 初始化上下文 diff --git a/src/controller/TreeViewController.ts b/src/controller/TreeViewController.ts index ea810df..56b2394 100644 --- a/src/controller/TreeViewController.ts +++ b/src/controller/TreeViewController.ts @@ -527,12 +527,12 @@ class TreeViewController implements Disposable { label: `周赛期数查询`, detail: `周赛期数查询`, value: `contest`, + }, + { + label: `测试api`, + detail: `测试api`, + value: `testapi`, } - // { - // label: `测试api`, - // detail: `测试api`, - // value: `testapi`, - // } ); const choice: IQuickItemEx | undefined = await vscode.window.showQuickPick(picks, { title: "选择查询选项", @@ -628,10 +628,7 @@ class TreeViewController implements Disposable { public async testapi(): Promise { try { - let so = {}; - - const solution: string = JSON.stringify(so); - solutionService.show(solution); + executeService.getTestApi("ss"); } catch (error) { logOutput.appendLine(error.toString()); await promptForOpenOutputChannel("Failed to fetch today question. 请查看控制台信息~", OutPutType.error); diff --git a/src/dao/scoreDao.ts b/src/dao/scoreDao.ts index 48aae3e..3ca600b 100644 --- a/src/dao/scoreDao.ts +++ b/src/dao/scoreDao.ts @@ -15,13 +15,13 @@ class ScoreDao { public getScoreData(onlineData?): Map { let nameSiteMapping = new Map(); let temp = this.scoreBase as IScoreData[]; - if (onlineData) { - temp = onlineData; + if (onlineData && Array.isArray(onlineData)) { + onlineData.forEach((element) => { + element.score = "" + Math.floor(element.Rating || 0); + nameSiteMapping.set("" + element.ID, element); + }); } temp.forEach((element) => { - // Rating - // ID - // ContestSlug element.score = "" + Math.floor(element.Rating || 0); nameSiteMapping.set("" + element.ID, element); }); diff --git a/src/extension.ts b/src/extension.ts index b5d0d2d..ff6609f 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -65,7 +65,7 @@ export async function activate(context: ExtensionContext): Promise { window.createTreeView("BricksExplorer", { treeDataProvider: bricksDataService, showCollapseAll: true }), commands.registerCommand("lcpr.deleteCache", () => mainContorller.deleteCache()), commands.registerCommand("lcpr.toggleLeetCodeCn", () => treeViewController.switchEndpoint()), - commands.registerCommand("lcpr.signin", () => loginContorller.signIn()), + commands.registerCommand("lcpr.newlogin", () => loginContorller.NewLogin()), commands.registerCommand("lcpr.signout", () => loginContorller.signOut()), commands.registerCommand("lcpr.previewProblem", (node: NodeModel) => treeViewController.previewProblem(node)), commands.registerCommand("lcpr.showProblem", (node: NodeModel) => treeViewController.showProblem(node)), diff --git a/src/model/Model.ts b/src/model/Model.ts index 6adc1da..4e40d65 100644 --- a/src/model/Model.ts +++ b/src/model/Model.ts @@ -21,6 +21,7 @@ import { getDayNowM, getRemakeName } from "../utils/SystemUtils"; export interface IQuickItemEx extends QuickPickItem { value: T; + cmd?: string; } export enum UserStatus { @@ -28,13 +29,6 @@ export enum UserStatus { SignedOut = 2, } -export const loginArgsMapping: Map = new Map([ - ["LeetCode", "-l"], - ["Cookie", "-c"], - ["GitHub", "-g"], - ["LinkedIn", "-i"], -]); - export const AllProgramLanguage: string[] = [ "bash", "c", diff --git a/src/rpc/actionChain/chainNode/cache.ts b/src/rpc/actionChain/chainNode/cache.ts index 3f7d8b7..b036e03 100644 --- a/src/rpc/actionChain/chainNode/cache.ts +++ b/src/rpc/actionChain/chainNode/cache.ts @@ -58,14 +58,14 @@ problems from the next layer. */ if (cacheRantingData) { return cb(null, cacheRantingData); } - this.next.getRatingOnline(function (e, ratingData) { + this.next.getRatingOnline(function (e, ratingObj) { if (e) return cb(e); - let ratingObj; - try { - ratingObj = JSON.parse(ratingData); - } catch (error) { - return cb("JSON.parse(ratingData) error"); - } + // let ratingObj; + // try { + // ratingObj = JSON.parse(ratingData); + // } catch (error) { + // return cb("JSON.parse(ratingData) error"); + // } storageUtils.setCache(commUtils.KEYS.ranting_path, ratingObj); return cb(null, ratingObj); }); @@ -113,9 +113,9 @@ problems from the next layer. */ }; /* Logging out the user and then logging in the user. */ - login = (user, cb) => { - this.logout(user, false); - this.next.login(user, function (e, user) { + normalLogin = (login_info, cb) => { + this.logout(login_info, false); + this.next.normalLogin(login_info, function (e, user) { if (e) return cb(e); sessionUtils.saveUser(user); return cb(null, user); diff --git a/src/rpc/actionChain/chainNode/core.ts b/src/rpc/actionChain/chainNode/core.ts index 24ba0d2..1b1d67c 100644 --- a/src/rpc/actionChain/chainNode/core.ts +++ b/src/rpc/actionChain/chainNode/core.ts @@ -17,10 +17,6 @@ import { configUtils } from "../../utils/configUtils"; import { ChainNodeBase } from "../chainNodeBase"; -// function hasTag(o, tag) { -// return Array.isArray(o) && o.some((x) => x.indexOf(tag.toLowerCase()) >= 0); -// } - /* It's a class that extends the ChainNodeBase class, and it has a bunch of methods that are called by the LeetCode CLI */ class CorePlugin extends ChainNodeBase { @@ -36,18 +32,6 @@ class CorePlugin extends ChainNodeBase { this.getProblems(!opts.dontTranslate, function (e, problems) { if (e) return cb(e); - // for (let q of (opts.query || "").split("")) { - // const f = QUERY_HANDLERS[q]; - // if (!f) continue; - // problems = problems.filter((x) => f(x, q)); - // } - - // for (let t of opts.tag || []) { - // problems = problems.filter(function (x) { - // return x.category === t || hasTag(x.companies, t) || hasTag(x.tags, t); - // }); - // } - return cb(null, problems); }); }; @@ -86,7 +70,7 @@ class CorePlugin extends ChainNodeBase { const data = _.extend({}, problem); // 增加版本信息 - data.LCPTCTX = configUtils.LCPTCTX; + data.LCPRCTX = configUtils.LCPRCTX; data.allCaseList = storageUtils.getAllCase(problem.desc); // unify format before rendering @@ -144,25 +128,4 @@ class CorePlugin extends ChainNodeBase { }; } -// const isLevel = (x, q) => x.level[0].toLowerCase() === q.toLowerCase(); -// const isACed = (x) => x.state === "ac"; -// const isLocked = (x) => x.locked; -// const isStarred = (x) => x.starred; - -/* It's a dictionary that maps the query to the function that filters the problems. */ -// const QUERY_HANDLERS = { -// e: isLevel, -// E: _.negate(isLevel), -// m: isLevel, -// M: _.negate(isLevel), -// h: isLevel, -// H: _.negate(isLevel), -// l: isLocked, -// L: _.negate(isLocked), -// d: isACed, -// D: _.negate(isACed), -// s: isStarred, -// S: _.negate(isStarred), -// }; - export const corePlugin: CorePlugin = new CorePlugin(); diff --git a/src/rpc/actionChain/chainNode/leetcode.cn.ts b/src/rpc/actionChain/chainNode/leetcode.cn.ts index 8ae05e3..5f568b6 100644 --- a/src/rpc/actionChain/chainNode/leetcode.cn.ts +++ b/src/rpc/actionChain/chainNode/leetcode.cn.ts @@ -9,12 +9,22 @@ import { ChainNodeBase } from "../chainNodeBase"; -let request = require("request"); +import axios, { AxiosError, AxiosResponse } from "axios"; import { configUtils } from "../../utils/configUtils"; import { sessionUtils } from "../../utils/sessionUtils"; import { reply } from "../../utils/ReplyUtils"; +import { + getProblemsTitleCNBody, + getQuestionOfTodayCNBody, + getSolutionArticlesSlugListCNBody, + getSolutionBySlugCNBody, + getUserContestPCNBody, +} from "../../utils/graphqlUtils"; + +// import axios from "axios"; + class LeetCodeCn extends ChainNodeBase { id = 15; name = "leetcode.cn"; @@ -32,7 +42,6 @@ class LeetCodeCn extends ChainNodeBase { if (e) return cb(e); if (needTranslation) { - // only translate titles of the list if user requested that.getProblemsTitle(function (e, titles) { if (e) return cb(e); @@ -56,31 +65,27 @@ class LeetCodeCn extends ChainNodeBase { opts.headers.Referer = "https://leetcode.cn/api/problems/algorithms/"; opts.json = true; - opts.body = { - query: [ - "query getQuestionTranslation($lang: String) {", - " translations: allAppliedQuestionTranslations(lang: $lang) {", - " title", - " questionId", - " __typename", - " }", - "}", - ].join("\n"), - variables: {}, - operationName: "getQuestionTranslation", - }; - - request.post(opts, function (e, resp, body) { - e = checkError(e, resp, 200); - if (e) return cb(e); + opts.body = getProblemsTitleCNBody(); - const titles: Object = []; - body.data.translations.forEach(function (x) { - titles[x.questionId] = x.title; - }); + axios + .post(opts.url, opts.body, opts) + .then(function (_response: AxiosResponse) { + const json_data = JSON.parse(_response.data); - return cb(null, titles); - }); + const titles: Object = []; + json_data.translations.forEach(function (x) { + titles[x.questionId] = x.title; + }); + + return cb(null, titles); + }) + .catch(function (response: AxiosError) { + if (response.status == 403 || response.status == 401) { + cb(sessionUtils.errors.EXPIRED); + } else { + cb({ msg: response.message, statusCode: response.status }); + } + }); }; /* A function that gets the question of the day from leetcode. */ @@ -90,46 +95,26 @@ class LeetCodeCn extends ChainNodeBase { opts.headers.Referer = "https://leetcode.cn/"; opts.json = true; - opts.body = { - operationName: "questionOfToday", - variables: {}, - query: [ - "query questionOfToday {", - " todayRecord {", - " date", - " userStatus", - " question {", - " titleSlug", - " questionId", - " questionFrontendId", - // ' content', - // ' stats', - // ' likes', - // ' dislikes', - // ' codeDefinition', - // ' sampleTestCase', - // ' enableRunCode', - // ' metaData', - // ' translatedContent', - " __typename", - " }", - " __typename", - " }", - "}", - ].join("\n"), - }; - - request.post(opts, function (e, resp, body) { - e = checkError(e, resp, 200); - if (e) return cb(e); - let result: any = {}; - result.titleSlug = body.data.todayRecord[0].question.titleSlug; - result.questionId = body.data.todayRecord[0].question.questionId; - result.fid = body.data.todayRecord[0].question.questionFrontendId; - result.date = body.data.todayRecord[0].data; - result.userStatus = body.data.todayRecord[0].userStatus; - return cb(null, result); - }); + opts.body = getQuestionOfTodayCNBody(); + axios + .post(opts.url, opts.body, opts) + .then(function (_response: AxiosResponse) { + const json_data = JSON.parse(_response.data); + let result: any = {}; + result.titleSlug = json_data.todayRecord[0].question.titleSlug; + result.questionId = json_data.todayRecord[0].question.questionId; + result.fid = json_data.todayRecord[0].question.questionFrontendId; + result.date = json_data.todayRecord[0].data; + result.userStatus = json_data.todayRecord[0].userStatus; + return cb(null, result); + }) + .catch(function (response: AxiosError) { + if (response.status == 403 || response.status == 401) { + cb(sessionUtils.errors.EXPIRED); + } else { + cb({ msg: response.message, statusCode: response.status }); + } + }); }; /* A function that is used to get the user contest ranking information. */ getUserContestP = (username, cb) => { @@ -138,68 +123,45 @@ class LeetCodeCn extends ChainNodeBase { opts.headers.Referer = configUtils.sys.urls.u.replace("$username", username); opts.json = true; - opts.body = { - variables: { - userSlug: username, - }, - query: [ - " query userContestRankingInfo($userSlug: String!) {", - " userContestRanking(userSlug: $userSlug) {", - " attendedContestsCount", - " rating", - " globalRanking", - " localRanking", - " globalTotalParticipants", - " localTotalParticipants", - " topPercentage", - " }", - // ' userContestRankingHistory(userSlug: $userSlug) {', - // ' attended', - // ' totalProblems', - // ' trendingDirection', - // ' finishTimeInSeconds', - // ' rating', - // ' score', - // ' ranking', - // ' contest {', - // ' title', - // ' titleCn', - // ' startTime', - // ' }', - // ' }', - " }", - ].join("\n"), - }; - - request.post(opts, function (e, resp, body) { - e = checkError(e, resp, 200); - if (e) return cb(e); - - return cb(null, body.data); - }); + opts.body = getUserContestPCNBody(username); + + axios + .post(opts.url, opts.body, opts) + .then(function (_response: AxiosResponse) { + const json_data = JSON.parse(_response.data); + + return cb(null, json_data); + }) + .catch(function (response: AxiosError) { + if (response.status == 403 || response.status == 401) { + cb(sessionUtils.errors.EXPIRED); + } else { + cb({ msg: response.message, statusCode: response.status }); + } + }); }; /* A function that is used to get the rating of the problems. */ getRatingOnline = (cb) => { - const _request = request.defaults({ timeout: 2000, jar: true }); - _request("https://zerotrac.github.io/leetcode_problem_rating/data.json", function (error: any, _, body: any) { - // console.log(error); - // console.log(info); - cb(error, body); - }); + axios + .get("https://zerotrac.github.io/leetcode_problem_rating/data.json", { timeout: 2000 }) + .then(function (response: AxiosResponse) { + cb(null, response.data); + }) + .catch(function (error: AxiosError) { + let error_info: any = {}; + error_info.msg = error.message; + if (error.response) { + error_info.statusCode = error.response?.status; + } + cb(error_info); + }); }; /* A function that is used to test the api. */ // eslint-disable-next-line @typescript-eslint/no-unused-vars getTestApi = (value: any, _) => { console.log(value); - let question_slug = "determine-color-of-a-chessboard-square"; - let lang = "cpp"; - - getSolutionArticlesSlugList(question_slug, lang, (e, articles_slug) => { - if (e) return; - getSolutionBySlug(question_slug, articles_slug, lang); - }); }; getHelpOnline = (problem, cn_flag, lang) => { @@ -221,144 +183,90 @@ function getSolutionBySlug(question_slug: string, articles_slug: string, lang: s opts.headers.Referer = URL_DISCUSS.replace("$slug", question_slug).replace("$articles_slug", articles_slug); opts.json = true; - opts.body = { - operationName: "solutionDetailArticle", - variables: { slug: articles_slug, orderBy: "DEFAULT" }, - query: [ - "query solutionDetailArticle($slug: String!, $orderBy: SolutionArticleOrderBy!) {", - " solutionArticle(slug: $slug, orderBy: $orderBy) {", - " ...solutionArticle", - " content", - " question {", - " questionTitleSlug", - " __typename", - " }", - " __typename", - "}", - "}", - "fragment solutionArticle on SolutionArticleNode {", - " uuid", - " title", - " slug", - " identifier", - "author {", - " username", - " profile {", - " realName", - " __typename", - " }", - " __typename", - "}", - "byLeetcode", - "__typename", - "}", - ].join("\n"), - }; + opts.body = getSolutionBySlugCNBody(articles_slug); + + axios + .post(opts.url, opts.body, opts) + .then(function (_response: AxiosResponse) { + const json_data = JSON.parse(_response.data); - request.post(opts, function (_, __, body) { - // let bbb = body; - // console.log(bbb); - let solution = body.data.solutionArticle; - if (!solution) return reply.error("本题没有题解"); - - let link = URL_DISCUSS.replace("$slug", question_slug).replace("$articles_slug", articles_slug); - // let content = solution.content.replace(/\\n/g, "\n").replace(/\\t/g, "\t"); - let content = solution.content.replace(/\\n/g, "\n"); - - // content = content.replace(/\$\\textit/g, "$"); - // content = content.replace(/\$\\texttt/g, "$"); - // content = content.replace(/\$\\text/g, "$"); - content = content.replace(/\\textit{/g, "{"); - content = content.replace(/\\texttt{/g, "{"); - content = content.replace(/\\text{/g, "{"); - - let solution_result: any = {}; - solution_result.problem_name = solution.title; - solution_result.title = solution.title; - solution_result.url = link; - solution_result.lang = lang; - solution_result.author = solution.author.username; - solution_result.votes = solution.voteCount; - solution_result.body = content; - solution_result.is_cn = true; - reply.info(JSON.stringify({ code: 100, solution: solution_result })); - }); + let solution = json_data.solutionArticle; + if (!solution) { + return reply.error("本题没有题解"); + } + let link = URL_DISCUSS.replace("$slug", question_slug).replace("$articles_slug", articles_slug); + // let content = solution.content.replace(/\\n/g, "\n").replace(/\\t/g, "\t"); + let content = solution.content.replace(/\\n/g, "\n"); + + content = content.replace(/\\textit{/g, "{"); + content = content.replace(/\\texttt{/g, "{"); + content = content.replace(/\\text{/g, "{"); + + let solution_result: any = {}; + solution_result.problem_name = solution.title; + solution_result.title = solution.title; + solution_result.url = link; + solution_result.lang = lang; + solution_result.author = solution.author.username; + solution_result.votes = solution.voteCount; + solution_result.body = content; + solution_result.is_cn = true; + reply.info(JSON.stringify({ code: 100, solution: solution_result })); + }) + .catch(function (response: AxiosError) { + if (response.status == 403 || response.status == 401) { + reply.info(JSON.stringify(sessionUtils.errors.EXPIRED)); + } else { + reply.info(JSON.stringify({ msg: response.message, statusCode: response.status })); + } + }); } function getSolutionArticlesSlugList(question_slug: string, lang: string, cb) { const opts = makeOpts(configUtils.sys.urls.graphql); opts.headers.Origin = configUtils.sys.urls.base; - // let URL_DISCUSSES = "https://leetcode.com/graphql"; let URL_DISCUSS = "https://leetcode.cn/problems/$slug/solution"; opts.headers.Referer = URL_DISCUSS.replace("$slug", question_slug); opts.json = true; - opts.body = { - operationName: "questionSolutionArticles", - variables: { questionSlug: question_slug, first: 1, skip: 0, orderBy: "DEFAULT", tagSlugs: [lang] }, - query: [ - "query questionSolutionArticles($questionSlug: String!, $skip: Int, $first: Int, $orderBy: SolutionArticleOrderBy, $userInput: String, $tagSlugs: [String!]) {", - "questionSolutionArticles(questionSlug: $questionSlug, skip: $skip, first: $first, orderBy: $orderBy, userInput: $userInput, tagSlugs: $tagSlugs) {", - " totalNum", - " edges {", - " node {", - " ...solutionArticle", - " __typename", - " }", - " __typename", - " }", - " __typename", - " }", - "}", - "fragment solutionArticle on SolutionArticleNode {", - " uuid", - " slug", - " byLeetcode", - " __typename", - "}", - ].join("\n"), - }; + opts.body = getSolutionArticlesSlugListCNBody(question_slug, lang); + + axios + .post(opts.url, opts.body, opts) + .then(function (_response: AxiosResponse) { + const json_data = JSON.parse(_response.data); + let edges = json_data.questionSolutionArticles?.edges || []; + let temp_result; + edges.forEach((element) => { + if (element?.node?.slug) { + temp_result = element?.node?.slug; + return; + } + }); - request.post(opts, function (e, _, body) { - let edges = body?.data?.questionSolutionArticles?.edges || []; - let temp_result; - edges.forEach((element) => { - if (element?.node?.slug) { - temp_result = element?.node?.slug; - return; + cb(null, temp_result); + }) + .catch(function (response: AxiosError) { + if (response.status == 403 || response.status == 401) { + cb(sessionUtils.errors.EXPIRED); + } else { + cb({ msg: response.message, statusCode: response.status }); } }); - - cb(e, temp_result); - }); } -function signOpts(opts: any, user: any) { - opts.headers.Cookie = "LEETCODE_SESSION=" + user.sessionId + ";csrftoken=" + user.sessionCSRF + ";"; - opts.headers["X-CSRFToken"] = user.sessionCSRF; - opts.headers["X-Requested-With"] = "XMLHttpRequest"; -} - -function makeOpts(url: any) { - let opts: any = {}; +function makeOpts(url) { + const opts: any = {}; opts.url = url; opts.headers = {}; - if (sessionUtils.isLogin()) signOpts(opts, sessionUtils.getUser()); - return opts; -} - -function checkError(e: any, resp: any, expectedStatus: any) { - if (!e && resp && resp.statusCode !== expectedStatus) { - const code = resp.statusCode; - - if (code === 403 || code === 401) { - e = sessionUtils.errors.EXPIRED; - } else { - e = { msg: "http error", statusCode: code }; - } + if (sessionUtils.isLogin()) { + let user = sessionUtils.getUser(); + opts.headers.Cookie = "LEETCODE_SESSION=" + user.sessionId + ";csrftoken=" + user.sessionCSRF + ";"; + opts.headers["X-CSRFToken"] = user.sessionCSRF; + opts.headers["X-Requested-With"] = "XMLHttpRequest"; } - return e; + return opts; } export const pluginObj: LeetCodeCn = new LeetCodeCn(); diff --git a/src/rpc/actionChain/chainNode/leetcode.ts b/src/rpc/actionChain/chainNode/leetcode.ts index 908da31..d237fbb 100644 --- a/src/rpc/actionChain/chainNode/leetcode.ts +++ b/src/rpc/actionChain/chainNode/leetcode.ts @@ -13,6 +13,7 @@ let underscore = require("underscore"); let request = require("request"); let prompt_out = require("prompt"); +import axios, { AxiosError, AxiosResponse } from "axios"; import { configUtils } from "../../utils/configUtils"; import { commUtils } from "../../utils/commUtils"; import { storageUtils } from "../../utils/storageUtils"; @@ -20,6 +21,13 @@ import { reply } from "../../utils/ReplyUtils"; import { sessionUtils } from "../../utils/sessionUtils"; import { ChainNodeBase } from "../chainNodeBase"; import { Queue } from "../../utils/queueUtils"; +import { + getAddQuestionToFavoriteBody, + getGetHelpEnBody, + getQuestionDetailBody, + getRemoveQuestionFromFavoriteBody, + getUserInfoBody, +} from "../../utils/graphqlUtils"; class LeetCode extends ChainNodeBase { id = 10; @@ -57,39 +65,45 @@ class LeetCode extends ChainNodeBase { getCategoryProblems = (category, cb) => { const opts = makeOpts(configUtils.sys.urls.problems.replace("$category", category)); - request(opts, function (e, resp, body) { - e = checkError(e, resp, 200); - if (e) return cb(e); + axios + .get(opts.url, opts) + .then(function (_response: AxiosResponse) { + const json_data = JSON.parse(_response.data); - const json = JSON.parse(body); + if (json_data.user_name.length === 0) { + return cb(sessionUtils.errors.EXPIRED); + } - if (json.user_name.length === 0) { - return cb(sessionUtils.errors.EXPIRED); - } + const problems = json_data.stat_status_pairs + .filter((p) => !p.stat.question__hide) + .map(function (p) { + return { + state: p.status || "None", + id: p.stat.question_id, + fid: p.stat.frontend_question_id, + name: p.stat.question__title, + slug: p.stat.question__title_slug, + link: configUtils.sys.urls.problem.replace("$slug", p.stat.question__title_slug), + locked: p.paid_only, + percent: (p.stat.total_acs * 100) / p.stat.total_submitted, + level: commUtils.getNameByLevel(p.difficulty.level), + starred: p.is_favor, + category: json_data.category_slug, + }; + }); - const problems = json.stat_status_pairs - .filter((p) => !p.stat.question__hide) - .map(function (p) { - return { - state: p.status || "None", - id: p.stat.question_id, - fid: p.stat.frontend_question_id, - name: p.stat.question__title, - slug: p.stat.question__title_slug, - link: configUtils.sys.urls.problem.replace("$slug", p.stat.question__title_slug), - locked: p.paid_only, - percent: (p.stat.total_acs * 100) / p.stat.total_submitted, - level: commUtils.getNameByLevel(p.difficulty.level), - starred: p.is_favor, - category: json.category_slug, - }; - }); - - return cb(null, problems); - }); + return cb(null, problems); + }) + .catch(function (response: AxiosError) { + if (response.status == 403 || response.status == 401) { + cb(sessionUtils.errors.EXPIRED); + } else { + cb({ msg: response.message, statusCode: response.status }); + } + }); }; - /* A function that takes in a problem and a callback function. It then makes a request to the leetcode + /* A function that takes in a problem and a callback function. It then makes to the leetcode server to get the problem's description, test cases, and other information. */ getProblem = (problem, needTranslation, cb) => { const user = sessionUtils.getUser(); @@ -100,49 +114,35 @@ server to get the problem's description, test cases, and other information. */ opts.headers.Referer = problem.link; opts.json = true; - opts.body = { - query: [ - "query getQuestionDetail($titleSlug: String!) {", - " question(titleSlug: $titleSlug) {", - " content", - " stats", - " likes", - " dislikes", - " codeDefinition", - " sampleTestCase", - " enableRunCode", - " metaData", - " translatedContent", - " }", - "}", - ].join("\n"), - variables: { titleSlug: problem.slug }, - operationName: "getQuestionDetail", - }; - - request.post(opts, function (e, resp, body) { - e = checkError(e, resp, 200); - if (e) return cb(e); - - const q = body.data.question; - if (!q) return cb("failed to load problem!"); - - problem.totalAC = JSON.parse(q.stats).totalAccepted; - problem.totalSubmit = JSON.parse(q.stats).totalSubmission; - problem.likes = q.likes; - problem.dislikes = q.dislikes; - - problem.desc = q.translatedContent && needTranslation ? q.translatedContent : q.content; - - problem.templates = JSON.parse(q.codeDefinition); - problem.testcase = q.sampleTestCase; - problem.testable = q.enableRunCode; - problem.templateMeta = JSON.parse(q.metaData); - // @si-yao: seems below property is never used. - // problem.discuss = q.discussCategoryId; - - return cb(null, problem); - }); + opts.body = getQuestionDetailBody(problem.slug); + + axios + .post(opts.url, opts.body, opts) + .then(function (_response: AxiosResponse) { + const q = _response.data.data.question; + if (!q) return cb("failed to load problem!"); + + problem.totalAC = JSON.parse(q.stats).totalAccepted; + problem.totalSubmit = JSON.parse(q.stats).totalSubmission; + problem.likes = q.likes; + problem.dislikes = q.dislikes; + + problem.desc = q.translatedContent && needTranslation ? q.translatedContent : q.content; + + problem.templates = JSON.parse(q.codeDefinition); + problem.testcase = q.sampleTestCase; + problem.testable = q.enableRunCode; + problem.templateMeta = JSON.parse(q.metaData); + + return cb(null, problem); + }) + .catch(function (response: AxiosError) { + if (response.status == 403 || response.status == 401) { + cb(sessionUtils.errors.EXPIRED); + } else { + cb({ msg: response.message, statusCode: response.status }); + } + }); }; /* A function that is used to run the code on the server. */ runCode = (opts, problem, cb) => { @@ -161,24 +161,29 @@ server to get the problem's description, test cases, and other information. */ }); let that = this; - request(opts, function (e, resp, body) { - e = checkError(e, resp, 200); - if (e) return cb(e); - - if (body.error) { - if (!body.error.includes("too soon")) return cb(body.error); - - ++opts._delay; - const reRun = underscore.partial(that.runCode, opts, problem, cb); - return setTimeout(reRun, opts._delay * 1000); - } - - opts.json = false; - opts.body = null; + axios + .post(opts.url, opts.body, opts) + .then(function (_response: AxiosResponse) { + let json_data = JSON.parse(_response.data); + if (json_data.error) { + if (!json_data.error.includes("too soon")) { + return cb(json_data.error); + } + ++opts._delay; - return cb(null, body); - }); + const reRun = underscore.partial(that.runCode, opts, problem, cb); + return setTimeout(reRun, opts._delay * 1000); + } + return cb(null, json_data); + }) + .catch(function (response: AxiosError) { + if (response.status == 403 || response.status == 401) { + cb(sessionUtils.errors.EXPIRED); + } else { + cb({ msg: response.message, statusCode: response.status }); + } + }); }; /* A function that is used to verify the result of a task. */ @@ -188,20 +193,27 @@ server to get the problem's description, test cases, and other information. */ opts.url = configUtils.sys.urls.verify.replace("$id", task.id); let that = this; - request(opts, function (e, resp, body) { - e = checkError(e, resp, 200); - if (e) return cb(e); - let result = JSON.parse(body); - if (result.state === "SUCCESS") { - result = that.formatResult(result); - underscore.extendOwn(result, task); - queue.ctx.results.push(result); - } else { - queue.addTask(task); - } - return cb(); - }); + axios + .get(opts.url, opts) + .then(function (_response: AxiosResponse) { + let result = JSON.parse(_response.data); + if (result.state === "SUCCESS") { + result = that.formatResult(result); + underscore.extendOwn(result, task); + queue.ctx.results.push(result); + } else { + queue.addTask(task); + } + return cb(); + }) + .catch(function (response: AxiosError) { + if (response.status == 403 || response.status == 401) { + cb(sessionUtils.errors.EXPIRED); + } else { + cb({ msg: response.message, statusCode: response.status }); + } + }); }; /* Formatting the result of the submission. */ @@ -292,160 +304,165 @@ server to get the problem's description, test cases, and other information. */ const opts = makeOpts(configUtils.sys.urls.submissions.replace("$slug", problem.slug)); opts.headers.Referer = configUtils.sys.urls.problem.replace("$slug", problem.slug); - request(opts, function (e, resp, body) { - e = checkError(e, resp, 200); - if (e) return cb(e); - - // FIXME: this only return the 1st 20 submissions, we should get next if necessary. - const submissions = JSON.parse(body).submissions_dump; - for (const submission of submissions) - submission.id = underscore.last(underscore.compact(submission.url.split("/"))); - - return cb(null, submissions); - }); + axios + .get(opts.url, opts) + .then(function (_response: AxiosResponse) { + const submissions = JSON.parse(_response.data).submissions_dump; + for (const submission of submissions) + submission.id = underscore.last(underscore.compact(submission.url.split("/"))); + + return cb(null, submissions); + }) + .catch(function (response: AxiosError) { + if (response.status == 403 || response.status == 401) { + cb(sessionUtils.errors.EXPIRED); + } else { + cb({ msg: response.message, statusCode: response.status }); + } + }); }; /* Getting the submission code and the runtime distribution chart. */ getSubmission = (submission, cb) => { const opts = makeOpts(configUtils.sys.urls.submission.replace("$id", submission.id)); - request(opts, function (e, resp, body) { - e = checkError(e, resp, 200); - if (e) return cb(e); - - let re = body.match(/submissionCode:\s('[^']*')/); - if (re) submission.code = eval(re[1]); - - re = body.match(/runtimeDistributionFormatted:\s('[^']+')/); - if (re) submission.distributionChart = JSON.parse(eval(re[1])); - return cb(null, submission); - }); + axios + .get(opts.url, opts) + .then(function (_response: AxiosResponse) { + let re = _response.data.match(/submissionCode:\s('[^']*')/); + if (re) submission.code = eval(re[1]); + + re = _response.data.match(/runtimeDistributionFormatted:\s('[^']+')/); + if (re) submission.distributionChart = JSON.parse(eval(re[1])); + return cb(null, submission); + }) + .catch(function (response: AxiosError) { + if (response.status == 403 || response.status == 401) { + cb(sessionUtils.errors.EXPIRED); + } else { + cb({ msg: response.message, statusCode: response.status }); + } + }); }; /* A function that is used to star a problem. */ starProblem = (problem, starred, cb) => { const user = sessionUtils.getUser(); - const operationName = starred ? "addQuestionToFavorite" : "removeQuestionFromFavorite"; const opts = makeOpts(configUtils.sys.urls.graphql); opts.headers.Origin = configUtils.sys.urls.base; opts.headers.Referer = problem.link; - opts.json = true; - opts.body = { - query: `mutation ${operationName}($favoriteIdHash: String!, $questionId: String!) {\n ${operationName}(favoriteIdHash: $favoriteIdHash, questionId: $questionId) {\n ok\n error\n favoriteIdHash\n questionId\n __typename\n }\n}\n`, - variables: { favoriteIdHash: user.hash, questionId: "" + problem.id }, - operationName: operationName, - }; - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - request.post(opts, function (e: any, resp: any, _) { - e = checkError(e, resp, 200); - if (e) return cb(e); - return cb(null, starred); - }); + opts.body = starred + ? getAddQuestionToFavoriteBody(user.hash, problem.id) + : getRemoveQuestionFromFavoriteBody(user.hash, problem.id); // getStarProblem(user.hash, problem.id); + + axios + .post(opts.url, opts.body, opts) + .then(function (_response: AxiosResponse) { + return cb(null, starred); + }) + .catch(function (response: AxiosError) { + if (response.status == 403 || response.status == 401) { + cb(sessionUtils.errors.EXPIRED); + } else { + cb({ msg: response.message, statusCode: response.status }); + } + }); }; - /* Making a request to the server to get the favorites. */ + /* Making a to the server to get the favorites. */ getFavorites = (cb: any) => { const opts = makeOpts(configUtils.sys.urls.favorites); - request(opts, function (e, resp, body) { - e = checkError(e, resp, 200); - if (e) return cb(e); - - const favorites = JSON.parse(body); - return cb(null, favorites); - }); + axios + .get(opts.url, opts) + .then(function (_response: AxiosResponse) { + const favorites = JSON.parse(_response.data); + return cb(null, favorites); + }) + .catch(function (response: AxiosError) { + if (response.status == 403 || response.status == 401) { + cb(sessionUtils.errors.EXPIRED); + } else { + cb({ msg: response.message, statusCode: response.status }); + } + }); }; - /* Making a POST request to the GraphQL API. */ + /* Making a POST to the GraphQL API. */ getUserInfo = (cb: any) => { const opts = makeOpts(configUtils.sys.urls.graphql); opts.headers.Origin = configUtils.sys.urls.base; opts.headers.Referer = configUtils.sys.urls.base; opts.json = true; - opts.body = { - query: ["{", " user {", " username", " isCurrentUserPremium", " }", "}"].join("\n"), - variables: {}, - }; - - request.post(opts, function (e, resp, body) { - e = checkError(e, resp, 200); - if (e) return cb(e); - - const user = body.data.user; - return cb(null, user); - }); - }; - - /* Making a request to the server and returning the response. */ - runSession = (method: any, data: any, cb: any) => { - const opts = makeOpts(configUtils.sys.urls.session); - opts.json = true; - opts.method = method; - opts.body = data; - - request(opts, function (e, resp, body) { - e = checkError(e, resp, 200); - if (e && e.statusCode === 302) e = sessionUtils.errors.EXPIRED; - - return e ? cb(e) : cb(null, body.sessions); - }); - }; + opts.body = getUserInfoBody(); - getSessions = (cb) => { - this.runSession("POST", {}, cb); - }; - - activateSession = (session, cb) => { - const data = { func: "activate", target: session.id }; - this.runSession("PUT", data, cb); - }; - - createSession = (name, cb) => { - const data = { func: "create", name: name }; - this.runSession("PUT", data, cb); - }; - - deleteSession = (session, cb) => { - const data = { target: session.id }; - this.runSession("DELETE", data, cb); + axios + .post(opts.url, getUserInfoBody(), opts) + .then(function (_response: AxiosResponse) { + const user = JSON.parse(_response.data.user); + return cb(null, user); + }) + .catch(function (response: AxiosError) { + if (response.status == 403 || response.status == 401) { + cb(sessionUtils.errors.EXPIRED); + } else { + cb({ msg: response.message, statusCode: response.status }); + } + }); }; - /* A function that takes in a user object and a callback function. It then makes a request to the login -page and gets the csrf token. It then makes a post request to the login page with the csrf token and + /* A function that takes in a user object and a callback function. It then makes a to the login +page and gets the csrf token. It then makes a post to the login page with the csrf token and the user's login and password. If the response status code is 302, it saves the user's session id and csrf token to the user object and saves the user object to the session. */ - signin = (user: any, cb: any) => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - request(configUtils.sys.urls.login, function (e: any, resp: any, _) { - e = checkError(e, resp, 200); - if (e) return cb(e); - user.loginCSRF = commUtils.getSetCookieValue(resp, "csrftoken"); - const opts = { - url: configUtils.sys.urls.login, - headers: { - Origin: configUtils.sys.urls.base, - Referer: configUtils.sys.urls.login, - Cookie: "csrftoken=" + user.loginCSRF + ";", - }, - form: { - csrfmiddlewaretoken: user.loginCSRF, - login: user.login, - password: user.pass, - }, - }; - // eslint-disable-next-line @typescript-eslint/no-unused-vars - request.post(opts, function (e: any, resp: any, _) { - if (e) return cb(e); - if (resp.statusCode !== 302) return cb("invalid password?"); - - user.sessionCSRF = commUtils.getSetCookieValue(resp, "csrftoken"); - user.sessionId = commUtils.getSetCookieValue(resp, "LEETCODE_SESSION"); - sessionUtils.saveUser(user); - return cb(null, user); + signin = (login_info: any, cb: any) => { + axios + .get(configUtils.sys.urls.login) + .then(function (response: AxiosResponse) { + login_info.loginCSRF = commUtils.getSetCookieValue(response, "csrftoken"); + axios + .post( + configUtils.sys.urls.login, + { + csrfmiddlewaretoken: login_info.loginCSRF, + login: login_info.login, + password: login_info.pass, + }, + { + headers: { + "Content-Type": "multipart/form-data", + Origin: configUtils.sys.urls.base, + Referer: configUtils.sys.urls.login, + Cookie: "csrftoken=" + login_info.loginCSRF + ";", + }, + } + ) + // eslint-disable-next-line @typescript-eslint/no-unused-vars + .then(function (_response: AxiosResponse) { + //handle success + // console.log(response); + return cb("invalid password?"); + }) + .catch(function (response: AxiosError) { + //handle error + if (response.response?.status !== 302) { + return cb("invalid password?"); + } + login_info.sessionCSRF = commUtils.getSetCookieValue(response, "csrftoken"); + login_info.sessionId = commUtils.getSetCookieValue(response, "LEETCODE_SESSION"); + sessionUtils.saveUser(login_info); + return cb(null, login_info); + }); + }) + .catch(function (error: AxiosError) { + let error_info: any = {}; + error_info.msg = error.message; + if (error.response) { + error_info.statusCode = error.response?.status; + } + cb(error_info); }); - }); }; /* Retrieving the user's favorites and user info. */ @@ -457,12 +474,7 @@ and csrf token to the user object and saves the user object to the session. */ if (f) { user.hash = f.id_hash; user.name = favorites.user_name; - } else { - // reply.warn("Favorite not found?"); } - } else { - // return cb(e); - // reply.warn("Failed to retrieve user favorites: " + e); } that.getUserInfo(function (e, _user) { @@ -476,9 +488,9 @@ and csrf token to the user object and saves the user object to the session. */ }); }; - login = (user, cb) => { + normalLogin = (login_info, cb) => { let that = this; - that.signin(user, function (e, user) { + that.signin(login_info, function (e, user) { if (e) return cb(e); that.getUser(user, cb); }); @@ -500,7 +512,7 @@ and csrf token to the user object and saves the user object to the session. */ }; /* A function that is used to login to leetcode. */ - requestLeetcodeAndSave = (request, leetcodeUrl, user, cb) => { + callLeetcodeAndSave = (request, leetcodeUrl, user, cb) => { let that = this; // eslint-disable-next-line @typescript-eslint/no-unused-vars request.get({ url: leetcodeUrl }, function (_, resp, __) { @@ -570,7 +582,7 @@ and csrf token to the user object and saves the user object to the session. */ return cb("GitHub login failed"); } if (resp.request.uri.href !== urls.github_tf_redirect) { - return that.requestLeetcodeAndSave(_request, leetcodeUrl, user, cb); + return that.callLeetcodeAndSave(_request, leetcodeUrl, user, cb); } prompt_out.colors = false; prompt_out.message = ""; @@ -606,7 +618,7 @@ and csrf token to the user object and saves the user object to the session. */ if (resp.request.uri.href === urls.github_tf_session_request) { return cb("Invalid two-factor code please check"); } - that.requestLeetcodeAndSave(_request, leetcodeUrl, user, cb); + that.callLeetcodeAndSave(_request, leetcodeUrl, user, cb); }); } ); @@ -668,68 +680,30 @@ and csrf token to the user object and saves the user object to the session. */ if (resp.statusCode !== 200) { return cb("LinkedIn login failed"); } - that.requestLeetcodeAndSave(_request, leetcodeUrl, user, cb); + that.callLeetcodeAndSave(_request, leetcodeUrl, user, cb); }); }); }; /* A function that is used to get the rating of the problems. */ getRatingOnline = (cb) => { - const _request = request.defaults({ timeout: 2000, jar: true }); - _request("https://zerotrac.github.io/leetcode_problem_rating/data.json", function (error: any, _, body: any) { - // console.log(error); - // console.log(info); - cb(error, body); - }); + axios + .get("https://zerotrac.github.io/leetcode_problem_rating/data.json", { timeout: 2000 }) + .then(function (response: AxiosResponse) { + cb(null, response.data); + }) + .catch(function (error: AxiosError) { + let error_info: any = {}; + error_info.msg = error.message; + if (error.response) { + error_info.statusCode = error.response?.status; + } + cb(error_info); + }); }; /* A function that gets the question of the day from leetcode. */ getQuestionOfToday = (cb) => { - const opts = makeOpts(configUtils.sys.urls.graphql); - opts.headers.Origin = configUtils.sys.urls.base; - opts.headers.Referer = "https://leetcode.com/"; - - opts.json = true; - opts.body = { - operationName: "questionOfToday", - variables: {}, - query: [ - "query questionOfToday {", - " todayRecord {", - " date", - " userStatus", - " question {", - " titleSlug", - " questionId", - " questionFrontendId", - // ' content', - // ' stats', - // ' likes', - // ' dislikes', - // ' codeDefinition', - // ' sampleTestCase', - // ' enableRunCode', - // ' metaData', - // ' translatedContent', - " __typename", - " }", - " __typename", - " }", - "}", - ].join("\n"), - }; - - // request.post(opts, function (e, resp, body) { - // e = checkError(e, resp, 200); - // if (e) return cb(e); - // let result: any = {}; - // result.titleSlug = body.data.todayRecord[0].question.titleSlug; - // result.questionId = body.data.todayRecord[0].question.questionId; - // result.fid = body.data.todayRecord[0].question.questionFrontendId; - // result.date = body.data.todayRecord[0].data; - // result.userStatus = body.data.todayRecord[0].userStatus; - // return cb(null, result); - // }); cb(null, {}); }; @@ -738,47 +712,6 @@ and csrf token to the user object and saves the user object to the session. */ const opts = makeOpts(configUtils.sys.urls.noj_go); opts.headers.Origin = configUtils.sys.urls.base; opts.headers.Referer = configUtils.sys.urls.u.replace("$username", username); - - opts.json = true; - opts.body = { - variables: { - userSlug: username, - }, - query: [ - " query userContestRankingInfo($userSlug: String!) {", - " userContestRanking(userSlug: $userSlug) {", - " attendedContestsCount", - " rating", - " globalRanking", - " localRanking", - " globalTotalParticipants", - " localTotalParticipants", - " topPercentage", - " }", - // ' userContestRankingHistory(userSlug: $userSlug) {', - // ' attended', - // ' totalProblems', - // ' trendingDirection', - // ' finishTimeInSeconds', - // ' rating', - // ' score', - // ' ranking', - // ' contest {', - // ' title', - // ' titleCn', - // ' startTime', - // ' }', - // ' }', - " }", - ].join("\n"), - }; - - // request.post(opts, function (e, resp, body) { - // e = checkError(e, resp, 200); - // if (e) return cb(e); - - // return cb(null, body.data); - // }); cb(null, {}); }; @@ -805,7 +738,7 @@ and csrf token to the user object and saves the user object to the session. */ } /** - * It takes a problem object, a language, and a callback. It then makes a request to the LeetCode + * It takes a problem object, a language, and a callback. It then makes a to the LeetCode * Discuss API to get the top voted solution for that problem in that language * @param problem - the problem object * @param lang - The language of the solution. @@ -817,80 +750,51 @@ function getHelpEn(problem, lang, cb) { let URL_DISCUSSES = "https://leetcode.com/graphql"; if (lang === "python3") lang = "python"; - - let opts = { - url: URL_DISCUSSES, - json: true, - body: { - query: [ - "query questionTopicsList($questionId: String!, $orderBy: TopicSortingOption, $skip: Int, $query: String, $first: Int!, $tags: [String!]) {", - " questionTopicsList(questionId: $questionId, orderBy: $orderBy, skip: $skip, query: $query, first: $first, tags: $tags) {", - " ...TopicsList", - " }", - "}", - "fragment TopicsList on TopicConnection {", - " totalNum", - " edges {", - " node {", - " id", - " title", - " post {", - " content", - " voteCount", - " author {", - " username", - " }", - " }", - " }", - " }", - "}", - ].join("\n"), - - operationName: "questionTopicsList", - variables: JSON.stringify({ - query: "", - first: 1, - skip: 0, - orderBy: "most_votes", - questionId: "" + problem.id, - tags: [lang], - }), - }, - }; - request(opts, function (e, resp, body) { - if (e) return cb(e); - if (resp.statusCode !== 200) return cb({ msg: "http error", statusCode: resp.statusCode }); - - const solutions = body.data.questionTopicsList.edges; - const solution = solutions.length > 0 ? solutions[0].node : null; - return cb(null, solution); - }); + axios + .post(URL_DISCUSSES, { + url: URL_DISCUSSES, + json: true, + body: getGetHelpEnBody(lang, problem.id), + }) + .then(function (_response: AxiosResponse) { + const solutions = _response.data.questionTopicsList.edges; + const solution = solutions.length > 0 ? solutions[0].node : null; + return cb(null, solution); + }) + .catch(function (response: AxiosError) { + return cb({ msg: response.message, statusCode: response.status }, null); + }); } function makeOpts(url) { const opts: any = {}; opts.url = url; opts.headers = {}; - if (sessionUtils.isLogin()) signOpts(opts, sessionUtils.getUser()); + if (sessionUtils.isLogin()) { + let user = sessionUtils.getUser(); + opts.headers.Cookie = "LEETCODE_SESSION=" + user.sessionId + ";csrftoken=" + user.sessionCSRF + ";"; + opts.headers["X-CSRFToken"] = user.sessionCSRF; + opts.headers["X-Requested-With"] = "XMLHttpRequest"; + } return opts; } -function signOpts(opts, user) { - opts.headers.Cookie = "LEETCODE_SESSION=" + user.sessionId + ";csrftoken=" + user.sessionCSRF + ";"; - opts.headers["X-CSRFToken"] = user.sessionCSRF; - opts.headers["X-Requested-With"] = "XMLHttpRequest"; -} -function checkError(e, resp, expectedStatus) { - if (!e && resp && resp.statusCode !== expectedStatus) { - const code = resp.statusCode; - - if (code === 403 || code === 401) { - e = sessionUtils.errors.EXPIRED; - } else { - e = { msg: "http error", statusCode: code }; - } - } - return e; -} +// function signOpts(opts, user) { +// opts.headers.Cookie = "LEETCODE_SESSION=" + user.sessionId + ";csrftoken=" + user.sessionCSRF + ";"; +// opts.headers["X-CSRFToken"] = user.sessionCSRF; +// opts.headers["X-Requested-With"] = "XMLHttpRequest"; +// } +// function checkError(e, resp, expectedStatus) { +// if (!e && resp && resp.statusCode !== expectedStatus) { +// const code = resp.statusCode; + +// if (code === 403 || code === 401) { +// e = sessionUtils.errors.EXPIRED; +// } else { +// e = { msg: "http error", statusCode: code }; +// } +// } +// return e; +// } export const pluginObj: LeetCode = new LeetCode(); diff --git a/src/rpc/actionChain/chainNode/retry.ts b/src/rpc/actionChain/chainNode/retry.ts index bc90bb4..378be38 100644 --- a/src/rpc/actionChain/chainNode/retry.ts +++ b/src/rpc/actionChain/chainNode/retry.ts @@ -27,12 +27,8 @@ class RetryPlugin extends ChainNodeBase { /* A wrapper for the API. */ init = () => { const names = [ - "activateSession", - "createSession", - "deleteSession", "getProblems", "getProblem", - "getSessions", "getSubmissions", "getSubmission", "getFavorites", @@ -80,7 +76,7 @@ login function. */ return cb(); } - this.next.login(user, function (e) { + this.next.normalLogin(user, function (e) { if (e) { // } else { diff --git a/src/rpc/actionChain/chainNodeBase.ts b/src/rpc/actionChain/chainNodeBase.ts index 4d5c8c7..863ca73 100644 --- a/src/rpc/actionChain/chainNodeBase.ts +++ b/src/rpc/actionChain/chainNodeBase.ts @@ -44,18 +44,7 @@ export class ChainNodeBase { public getProblemsTitle(cb: Function): void { this.next.getProblemsTitle(cb); } - public createSession(a, cb: Function): void { - this.next.createSession(a, cb); - } - public getSessions(cb: Function): void { - this.next.getSessions(cb); - } - public activateSession(s, cb: Function): void { - this.next.activateSession(s, cb); - } - public deleteSession(s, cb: Function): void { - this.next.deleteSession(s, cb); - } + public updateProblem(a, b): void { this.next.updateProblem(a, b); } @@ -71,8 +60,8 @@ export class ChainNodeBase { public testProblem(s, cb: Function): void { this.next.testProblem(s, cb); } - public login(user, cb: Function): void { - this.next.login(user, cb); + public normalLogin(login_info, cb: Function): void { + this.next.normalLogin(login_info, cb); } public logout(user, cb): void { this.next.logout(user, cb); diff --git a/src/rpc/childMain.ts b/src/rpc/childMain.ts index e024edf..5311b81 100644 --- a/src/rpc/childMain.ts +++ b/src/rpc/childMain.ts @@ -23,7 +23,8 @@ class Main { process.stdout.on("error", function (e) { if (e.code === "EPIPE") process.exit(); }); - configUtils.init(JSON.parse(process.env.ccagml || "{}")); + configUtils.init_env("LCPRCTX", JSON.parse(process.env.ccagml || "{}")); + configUtils.init_env("LCPRENVEXTRA", JSON.parse(process.env.extra || "{}")); reply.init(); storageUtils.init(); chainMgr.init(corePlugin); diff --git a/src/rpc/factory/api/userApi.ts b/src/rpc/factory/api/userApi.ts index 2937ba7..86518dd 100644 --- a/src/rpc/factory/api/userApi.ts +++ b/src/rpc/factory/api/userApi.ts @@ -15,6 +15,7 @@ import { sessionUtils } from "../../utils/sessionUtils"; import { ApiBase } from "../apiBase"; import { chainMgr } from "../../actionChain/chainManager"; +import { configUtils } from "../../utils/configUtils"; class UserApi extends ApiBase { constructor() { @@ -53,7 +54,6 @@ class UserApi extends ApiBase { default: false, describe: "Logout", }); - argv_config.parseArgFromCmd(argv); return argv_config.get_result(); @@ -63,28 +63,36 @@ class UserApi extends ApiBase { sessionUtils.argv = argv; let user: any = null; if (argv.login) { - // login - prompt_out.colors = false; - prompt_out.message = ""; - prompt_out.start(); - prompt_out.get( - [ - { name: "login", required: true }, - { name: "pass", required: true, hidden: true }, - ], - function (e, user) { - if (e) { - return reply.info(JSON.stringify({ code: -1, msg: e.msg || e })); - } - - chainMgr.getChainHead().login(user, function (e, user) { - if (e) { - return reply.info(JSON.stringify({ code: -2, msg: e.msg || e })); - } - reply.info(JSON.stringify({ code: 100, user_name: user.name })); - }); + let login_info: any = {}; + login_info.login = configUtils.LCPRENVEXTRA?.name || ""; + login_info.pass = configUtils.LCPRENVEXTRA?.pass || ""; + chainMgr.getChainHead().normalLogin(login_info, function (e, user) { + if (e) { + return reply.info(JSON.stringify({ code: -2, msg: e.msg || e })); } - ); + reply.info(JSON.stringify({ code: 100, user_name: user.name })); + }); + // // login + // prompt_out.colors = false; + // prompt_out.message = ""; + // prompt_out.start(); + // prompt_out.get( + // [ + // { name: "login", required: true }, + // { name: "pass", required: true, hidden: true }, + // ], + // function (e, user) { + // if (e) { + // return reply.info(JSON.stringify({ code: -1, msg: e.msg || e })); + // } + // chainMgr.getChainHead().login(user, function (e, user) { + // if (e) { + // return reply.info(JSON.stringify({ code: -2, msg: e.msg || e })); + // } + // reply.info(JSON.stringify({ code: 100, user_name: user.name })); + // }); + // } + // ); } else if (argv.logout) { // logout user = chainMgr.getChainHead().logout(user, true); diff --git a/src/rpc/utils/configUtils.ts b/src/rpc/utils/configUtils.ts index 7b7f124..b7faa25 100644 --- a/src/rpc/utils/configUtils.ts +++ b/src/rpc/utils/configUtils.ts @@ -10,7 +10,8 @@ let underscore = require("underscore"); class Config { - LCPTCTX: any; + LCPRCTX: any; + LCPRENVEXTRA: any; app: any; sys: any; autologin: any; @@ -103,8 +104,8 @@ class Config { this.plugins = {}; } - init(ctx) { - this.LCPTCTX = ctx; + init_env(key, value) { + this[key] = value; } getAll(useronly) { diff --git a/src/rpc/utils/graphqlUtils.ts b/src/rpc/utils/graphqlUtils.ts new file mode 100644 index 0000000..715d114 --- /dev/null +++ b/src/rpc/utils/graphqlUtils.ts @@ -0,0 +1,248 @@ +export function getQuestionDetailBody(titleSlug_value) { + return { + query: [ + "query getQuestionDetail($titleSlug: String!) {", + " question(titleSlug: $titleSlug) {", + " content", + " stats", + " likes", + " dislikes", + " codeDefinition", + " sampleTestCase", + " enableRunCode", + " metaData", + " translatedContent", + " }", + "}", + ].join("\n"), + variables: { titleSlug: titleSlug_value }, + operationName: "getQuestionDetail", + }; +} + +export function getAddQuestionToFavoriteBody(user_hash, problem_id) { + return { + query: [ + "mutation addQuestionToFavorite($favoriteIdHash: String!, $questionId: String!) {", + " addQuestionToFavorite(favoriteIdHash: $favoriteIdHash, questionId: $questionId) {", + " ok", + " error", + " favoriteIdHash", + " questionId", + " __typename", + " }", + "}", + ].join("\n"), + variables: { favoriteIdHash: user_hash, questionId: "" + problem_id }, + operationName: "addQuestionToFavorite", + }; +} + +export function getRemoveQuestionFromFavoriteBody(user_hash, problem_id) { + return { + query: [ + "mutation removeQuestionFromFavorite($favoriteIdHash: String!, $questionId: String!) {", + " removeQuestionFromFavorite(favoriteIdHash: $favoriteIdHash, questionId: $questionId) {", + " ok", + " error", + " favoriteIdHash", + " questionId", + " __typename", + " }", + "}", + ].join("\n"), + variables: { favoriteIdHash: user_hash, questionId: "" + problem_id }, + operationName: "removeQuestionFromFavorite", + }; +} + +export function getUserInfoBody() { + return { + query: ["{", " user {", " username", " isCurrentUserPremium", " }", "}"].join("\n"), + variables: {}, + }; +} + +export function getGetHelpEnBody(lang, problem_id) { + return { + query: [ + "query questionTopicsList($questionId: String!, $orderBy: TopicSortingOption, $skip: Int, $query: String, $first: Int!, $tags: [String!]) {", + " questionTopicsList(questionId: $questionId, orderBy: $orderBy, skip: $skip, query: $query, first: $first, tags: $tags) {", + " ...TopicsList", + " }", + "}", + "fragment TopicsList on TopicConnection {", + " totalNum", + " edges {", + " node {", + " id", + " title", + " post {", + " content", + " voteCount", + " author {", + " username", + " }", + " }", + " }", + " }", + "}", + ].join("\n"), + + operationName: "questionTopicsList", + variables: JSON.stringify({ + query: "", + first: 1, + skip: 0, + orderBy: "most_votes", + questionId: "" + problem_id, + tags: [lang], + }), + }; +} + +export function getProblemsTitleCNBody() { + return { + query: [ + "query getQuestionTranslation($lang: String) {", + " translations: allAppliedQuestionTranslations(lang: $lang) {", + " title", + " questionId", + " __typename", + " }", + "}", + ].join("\n"), + variables: {}, + operationName: "getQuestionTranslation", + }; +} + +export function getQuestionOfTodayCNBody() { + return { + operationName: "questionOfToday", + variables: {}, + query: [ + "query questionOfToday {", + " todayRecord {", + " date", + " userStatus", + " question {", + " titleSlug", + " questionId", + " questionFrontendId", + // ' content', + // ' stats', + // ' likes', + // ' dislikes', + // ' codeDefinition', + // ' sampleTestCase', + // ' enableRunCode', + // ' metaData', + // ' translatedContent', + " __typename", + " }", + " __typename", + " }", + "}", + ].join("\n"), + }; +} + +export function getUserContestPCNBody(username) { + return { + variables: { + userSlug: username, + }, + query: [ + " query userContestRankingInfo($userSlug: String!) {", + " userContestRanking(userSlug: $userSlug) {", + " attendedContestsCount", + " rating", + " globalRanking", + " localRanking", + " globalTotalParticipants", + " localTotalParticipants", + " topPercentage", + " }", + // ' userContestRankingHistory(userSlug: $userSlug) {', + // ' attended', + // ' totalProblems', + // ' trendingDirection', + // ' finishTimeInSeconds', + // ' rating', + // ' score', + // ' ranking', + // ' contest {', + // ' title', + // ' titleCn', + // ' startTime', + // ' }', + // ' }', + " }", + ].join("\n"), + }; +} + +export function getSolutionBySlugCNBody(articles_slug) { + return { + operationName: "solutionDetailArticle", + variables: { slug: articles_slug, orderBy: "DEFAULT" }, + query: [ + "query solutionDetailArticle($slug: String!, $orderBy: SolutionArticleOrderBy!) {", + " solutionArticle(slug: $slug, orderBy: $orderBy) {", + " ...solutionArticle", + " content", + " question {", + " questionTitleSlug", + " __typename", + " }", + " __typename", + "}", + "}", + "fragment solutionArticle on SolutionArticleNode {", + " uuid", + " title", + " slug", + " identifier", + "author {", + " username", + " profile {", + " realName", + " __typename", + " }", + " __typename", + "}", + "byLeetcode", + "__typename", + "}", + ].join("\n"), + }; +} + +export function getSolutionArticlesSlugListCNBody(question_slug, lang) { + return { + operationName: "questionSolutionArticles", + variables: { questionSlug: question_slug, first: 1, skip: 0, orderBy: "DEFAULT", tagSlugs: [lang] }, + query: [ + "query questionSolutionArticles($questionSlug: String!, $skip: Int, $first: Int, $orderBy: SolutionArticleOrderBy, $userInput: String, $tagSlugs: [String!]) {", + "questionSolutionArticles(questionSlug: $questionSlug, skip: $skip, first: $first, orderBy: $orderBy, userInput: $userInput, tagSlugs: $tagSlugs) {", + " totalNum", + " edges {", + " node {", + " ...solutionArticle", + " __typename", + " }", + " __typename", + " }", + " __typename", + " }", + "}", + "fragment solutionArticle on SolutionArticleNode {", + " uuid", + " slug", + " byLeetcode", + " __typename", + "}", + ].join("\n"), + }; +} diff --git a/src/service/BricksDataService.ts b/src/service/BricksDataService.ts index c126421..1fea3d0 100644 --- a/src/service/BricksDataService.ts +++ b/src/service/BricksDataService.ts @@ -38,7 +38,7 @@ export class BricksDataService implements TreeDataProvider { label: element.name, collapsibleState: element.collapsibleState, // 没有子节点 command: { - command: "lcpr.signin", + command: "lcpr.newlogin", title: "工头说你不是我们工地的人", }, }; diff --git a/src/service/TreeDataService.ts b/src/service/TreeDataService.ts index e35cf06..ca797ed 100644 --- a/src/service/TreeDataService.ts +++ b/src/service/TreeDataService.ts @@ -60,7 +60,7 @@ export class TreeDataService implements vscode.TreeDataProvider { label: element.name, collapsibleState: vscode.TreeItemCollapsibleState.None, command: { - command: "lcpr.signin", + command: "lcpr.newlogin", title: "未登录", }, }; diff --git a/src/utils/CliUtils.ts b/src/utils/CliUtils.ts index 36b9b56..014445f 100644 --- a/src/utils/CliUtils.ts +++ b/src/utils/CliUtils.ts @@ -96,19 +96,16 @@ export async function executeCommandWithProgress( return result; } -function childLCPTCTX(): string { - return JSON.stringify(logOutput.getLCPTCTXAll()); -} - // clone process.env and add http proxy -export function createEnvOption(): {} { +export function createEnvOption(extra?: string): {} { const proxy: string | undefined = getHttpAgent(); const env: any = Object.create(process.env); if (proxy) { env.http_proxy = proxy; return env; } - env.ccagml = childLCPTCTX(); + env.ccagml = JSON.stringify(logOutput.getLCPRCTXAll()); + env.extra = extra; return env; } diff --git a/src/utils/ConfigUtils.ts b/src/utils/ConfigUtils.ts index 5a07dc9..e17957f 100644 --- a/src/utils/ConfigUtils.ts +++ b/src/utils/ConfigUtils.ts @@ -158,19 +158,19 @@ export function enableSideMode(): boolean { return getVsCodeConfig().get("enableSideMode", true); } -export function getNodePath() { +export function getNodePath(): string { return getVsCodeConfig().get("nodePath", "node" /* default value */); } -export function isShowLocked() { +export function isShowLocked(): boolean { return !!getVsCodeConfig().get("showLocked"); } -export function isUseVscodeNode() { +export function isUseVscodeNode(): boolean { return getVsCodeConfig().get("useVscodeNode") === true; } -export function isUseWsl() { +export function isUseWsl(): boolean { return getVsCodeConfig().get("useWsl") === true; } @@ -178,7 +178,7 @@ export function getSortingStrategy(): SortingStrategy { return getVsCodeConfig().get("problems.sortStrategy", SortingStrategy.None); } -export async function updateSortingStrategy(value: string, flag: boolean) { +export async function updateSortingStrategy(value: string, flag: boolean): Promise { await getVsCodeConfig().update("problems.sortStrategy", value, flag); } diff --git a/src/utils/OutputUtils.ts b/src/utils/OutputUtils.ts index d69157e..b85203b 100644 --- a/src/utils/OutputUtils.ts +++ b/src/utils/OutputUtils.ts @@ -45,7 +45,7 @@ export async function promptForSignIn(): Promise { ); switch (choice) { case DialogOptions.yes: - await vscode.commands.executeCommand("lcpr.signin"); + await vscode.commands.executeCommand("lcpr.newlogin"); break; case DialogOptions.singUp: if (getLeetCodeEndpoint()) { @@ -83,7 +83,7 @@ export async function promptForOpenOutputChannel(message: string, type: OutPutTy class LogOutput implements vscode.Disposable { private readonly channel: vscode.OutputChannel = vscode.window.createOutputChannel("LeetCodeProblemRating"); - private LCPTCTX = {}; + private LCPRCTX = {}; public appendLine(message: string): void { this.channel.appendLine(message); } @@ -100,16 +100,16 @@ class LogOutput implements vscode.Disposable { this.channel.dispose(); } - public setLCPTCTX(k, v) { - this.LCPTCTX[k] = v; + public setLCPRCTX(k, v) { + this.LCPRCTX[k] = v; } - public getLCPTCTX(k) { - return this.LCPTCTX[k]; + public getLCPRCTX(k) { + return this.LCPRCTX[k]; } - public getLCPTCTXAll() { - return this.LCPTCTX; + public getLCPRCTXAll() { + return this.LCPRCTX; } }