From 3750401f6570a4317d93e85f514160aabc1bcd38 Mon Sep 17 00:00:00 2001 From: Itish Date: Tue, 10 Feb 2026 11:22:17 +0530 Subject: [PATCH 1/5] feat(mcp): add optional auth cookie injection for authenticated LeetCode API requests Reads session cookies from a JSON file specified by the LEETCODE_COOKIES_FILE env var and injects Cookie + x-csrftoken headers into all GraphQL requests. When no cookie file is configured, behavior is unchanged (unauthenticated). This unlocks authenticated endpoints: submission history, private problem lists, contest participation, and user-specific profile data. Co-Authored-By: Claude Opus 4.6 --- mcp/serverUtils.ts | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/mcp/serverUtils.ts b/mcp/serverUtils.ts index 833dc30..301fd66 100644 --- a/mcp/serverUtils.ts +++ b/mcp/serverUtils.ts @@ -1,18 +1,41 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; +import { readFileSync } from 'fs'; import { GraphQLParams, GraphQLClientError, ToolResponse, ToolExecutor, ToolModule } from './types'; const GRAPHQL_ENDPOINT = 'https://leetcode.com/graphql'; export const SERVER_VERSION = '1.0.0'; +// Loads auth cookies from LEETCODE_COOKIES_FILE env var. +function loadAuthData(): { cookie: string; csrftoken: string } { + const cookieFile = process.env.LEETCODE_COOKIES_FILE; + if (!cookieFile) return { cookie: '', csrftoken: '' }; + try { + const data = JSON.parse(readFileSync(cookieFile, 'utf-8')); + const parts: string[] = []; + if (data.LEETCODE_SESSION) parts.push(`LEETCODE_SESSION=${data.LEETCODE_SESSION}`); + if (data.csrftoken) parts.push(`csrftoken=${data.csrftoken}`); + if (data.cf_clearance) parts.push(`cf_clearance=${data.cf_clearance}`); + return { cookie: parts.join('; '), csrftoken: data.csrftoken || '' }; + } catch { + return { cookie: '', csrftoken: '' }; + } +} + // Executes a GraphQL query against the LeetCode API. export async function executeGraphQL(query: string, variables: GraphQLParams = {}): Promise { + const auth = loadAuthData(); + const headers: Record = { + 'Content-Type': 'application/json', + Referer: 'https://leetcode.com', + }; + if (auth.cookie) { + headers['Cookie'] = auth.cookie; + headers['x-csrftoken'] = auth.csrftoken; + } const requestInit: RequestInit = { method: 'POST', - headers: { - 'Content-Type': 'application/json', - Referer: 'https://leetcode.com', - }, + headers, body: JSON.stringify({ query, variables }), }; From c3b45d8da717d1b21d71ca43bb82ff702d74b17c Mon Sep 17 00:00:00 2001 From: Itish Date: Tue, 10 Feb 2026 11:23:19 +0530 Subject: [PATCH 2/5] docs: add authenticated requests section to MCP documentation Documents the LEETCODE_COOKIES_FILE env var for optional cookie-based authentication, including cookie file format and MCP client config example. Co-Authored-By: Claude Opus 4.6 --- README.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/README.md b/README.md index 1123aaf..94253ab 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,40 @@ The build step produces `dist/mcp/index.js`, the entry point used by MCP clients To run only a subset of tools, append the module name (`users`, `problems`, or `discussions`) as an extra argument or set the `MCP_SERVER_MODE` environment variable. +### Authenticated requests (optional) + +By default, the MCP server makes unauthenticated requests to the LeetCode GraphQL API. To access user-specific data (submission history, private problem lists, contest participation, etc.), you can provide session cookies via a JSON file: + +1. Create a JSON file with your LeetCode session cookies: + + ```json + { + "LEETCODE_SESSION": "", + "csrftoken": "", + "cf_clearance": "" + } + ``` + +2. Set the `LEETCODE_COOKIES_FILE` environment variable in your MCP client config: + + ```json + { + "mcpServers": { + "leetcode-suite": { + "command": "node", + "args": ["C:\\path\\to\\alfa-leetcode-api\\dist\\mcp\\index.js"], + "env": { + "LEETCODE_COOKIES_FILE": "C:\\path\\to\\leetcode-cookies.json" + } + } + } + } + ``` + +When the env var is set, the server injects `Cookie` and `x-csrftoken` headers into all GraphQL requests. When unset, it falls back to unauthenticated mode silently. + +> **Tip:** You can extract `LEETCODE_SESSION` and `csrftoken` from your browser's developer tools after logging in to leetcode.com. The session cookie typically expires after ~14 days. + ### MCP Inspector Use the Inspector to debug tools locally: From 282074a2460517de62887df5ad51f70e165c7559 Mon Sep 17 00:00:00 2001 From: Itish Date: Tue, 10 Feb 2026 11:25:28 +0530 Subject: [PATCH 3/5] feat: add Playwright-based cookie extraction script Adds scripts/extract-cookies.ts that opens a browser to LeetCode login, waits for the user to authenticate, then automatically saves session cookies to a JSON file with 600 permissions. Supports all login methods (username/password, Google, GitHub, Apple) since it polls the cookie jar rather than listening for specific redirects. Co-Authored-By: Claude Opus 4.6 --- README.md | 13 ++++++-- scripts/extract-cookies.ts | 67 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 scripts/extract-cookies.ts diff --git a/README.md b/README.md index 94253ab..8c56a9f 100644 --- a/README.md +++ b/README.md @@ -141,7 +141,16 @@ To run only a subset of tools, append the module name (`users`, `problems`, or ` By default, the MCP server makes unauthenticated requests to the LeetCode GraphQL API. To access user-specific data (submission history, private problem lists, contest participation, etc.), you can provide session cookies via a JSON file: -1. Create a JSON file with your LeetCode session cookies: +1. Extract your LeetCode session cookies using the included helper script: + + ```bash + npx playwright install chromium # first time only + npx ts-node scripts/extract-cookies.ts ./leetcode-cookies.json + ``` + + This opens a browser window — log in to LeetCode and the script will automatically detect your session and save the cookies. The output file is set to `600` permissions (owner-only read/write). + + Alternatively, create the JSON file manually with cookies from your browser's developer tools: ```json { @@ -169,7 +178,7 @@ By default, the MCP server makes unauthenticated requests to the LeetCode GraphQ When the env var is set, the server injects `Cookie` and `x-csrftoken` headers into all GraphQL requests. When unset, it falls back to unauthenticated mode silently. -> **Tip:** You can extract `LEETCODE_SESSION` and `csrftoken` from your browser's developer tools after logging in to leetcode.com. The session cookie typically expires after ~14 days. +> **Note:** The `LEETCODE_SESSION` cookie typically expires after ~14 days. Re-run the extraction script to refresh it. ### MCP Inspector diff --git a/scripts/extract-cookies.ts b/scripts/extract-cookies.ts new file mode 100644 index 0000000..0933edc --- /dev/null +++ b/scripts/extract-cookies.ts @@ -0,0 +1,67 @@ +/** + * Extracts LeetCode session cookies via browser login. + * + * Usage: + * npx playwright install chromium # first time only + * npx ts-node scripts/extract-cookies.ts [output-path] + * + * Default output: ./leetcode-cookies.json + */ + +import { chromium } from 'playwright'; +import { writeFileSync, chmodSync } from 'fs'; +import { resolve } from 'path'; + +const LOGIN_URL = 'https://leetcode.com/accounts/login/'; +const COOKIE_NAMES = ['LEETCODE_SESSION', 'csrftoken', 'cf_clearance']; +const POLL_INTERVAL_MS = 1000; +const TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes + +async function extractCookies(outputPath: string): Promise { + console.log('Launching browser...'); + const browser = await chromium.launch({ headless: false }); + const context = await browser.newContext(); + const page = await context.newPage(); + + await page.goto(LOGIN_URL); + console.log('\n Log in to LeetCode in the browser window.'); + console.log(' The script will detect your session automatically.\n'); + + const startTime = Date.now(); + + while (Date.now() - startTime < TIMEOUT_MS) { + const cookies = await context.cookies('https://leetcode.com'); + const session = cookies.find((c) => c.name === 'LEETCODE_SESSION'); + + if (session) { + const extracted: Record = {}; + for (const cookie of cookies) { + if (COOKIE_NAMES.includes(cookie.name)) { + extracted[cookie.name] = cookie.value; + } + } + + // Add metadata + const sessionExpiry = new Date(session.expires * 1000).toISOString(); + const payload = { ...extracted, expires_at: sessionExpiry }; + + const absPath = resolve(outputPath); + writeFileSync(absPath, JSON.stringify(payload, null, 2) + '\n'); + chmodSync(absPath, 0o600); + + console.log(`Cookies saved to ${absPath} (permissions: 600)`); + console.log(`Session expires: ${sessionExpiry}`); + await browser.close(); + return; + } + + await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS)); + } + + await browser.close(); + console.error('Timed out waiting for login (5 minutes). No cookies saved.'); + process.exit(1); +} + +const outputPath = process.argv[2] || './leetcode-cookies.json'; +extractCookies(outputPath); From 2297433638aa82885c5e3c32b9f05e37011ca36c Mon Sep 17 00:00:00 2001 From: Itish Date: Tue, 10 Feb 2026 11:26:52 +0530 Subject: [PATCH 4/5] chore: add cookie file to gitignore, playwright to optional deps, npm script - Prevent accidental commit of leetcode-cookies.json (contains session secrets) - Add playwright as optional dependency (heavy, only needed for cookie extraction) - Add npm run extract-cookies convenience script Co-Authored-By: Claude Opus 4.6 --- .gitignore | 3 +++ package.json | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 43a48f1..57e9354 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,9 @@ bun.lockb coverage/ .vitest/ +# Auth cookies (may contain session secrets) +leetcode-cookies.json + CLAUDE.md .claude/ .specify/ diff --git a/package.json b/package.json index c1e1eb7..4c00b03 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,8 @@ "check:fix": "biome check --write . && biome format --write .", "mcp": "ts-node mcp/index.ts", "mcp:dist": "npm run build && node dist/mcp/index.js", - "mcp:inspect": "ts-node mcp/runInspector.ts" + "mcp:inspect": "ts-node mcp/runInspector.ts", + "extract-cookies": "npx playwright install chromium && ts-node scripts/extract-cookies.ts" }, "keywords": [], "author": "alfaarghya", @@ -52,6 +53,9 @@ "vite": "^6.0.5", "vitest": "^4.0.15" }, + "optionalDependencies": { + "playwright": "^1.52.0" + }, "lint-staged": { "*.{ts,js}": [ "biome check --write --no-errors-on-unmatched", From 0b140fdbc6bf58f92ba438d90a7cc847d980ce37 Mon Sep 17 00:00:00 2001 From: Itish Date: Tue, 10 Feb 2026 11:46:56 +0530 Subject: [PATCH 5/5] feat(mcp): add 9 auth-required tools for submissions, notes, streaks, and favorites Add tools that leverage the cookie auth injection to provide authenticated LeetCode API access: user status, daily streak, favorites lists, submission details with source code, problem notes (read/write), favorite management (add/remove), and lightweight problem status checks. Includes requireAuth() guard that gives clear error messages when cookies aren't configured, and 8 new GraphQL query/mutation definitions. Co-Authored-By: Claude Opus 4.6 --- mcp/leetCodeService.ts | 104 +++++++++++++++++++++++++++- mcp/modules/problemTools.ts | 85 +++++++++++++++++++++++ mcp/modules/userTools.ts | 32 +++++++++ mcp/serverUtils.ts | 10 +++ mcp/types.ts | 17 ++++- src/GQLQueries/favoritesLists.ts | 19 +++++ src/GQLQueries/index.ts | 11 +++ src/GQLQueries/problemStatus.ts | 13 ++++ src/GQLQueries/questionNote.ts | 11 +++ src/GQLQueries/streakCounter.ts | 9 +++ src/GQLQueries/submissionDetails.ts | 32 +++++++++ src/GQLQueries/toggleFavorite.ts | 15 ++++ src/GQLQueries/updateNote.ts | 10 +++ src/GQLQueries/userStatus.ts | 18 +++++ 14 files changed, 383 insertions(+), 3 deletions(-) create mode 100644 src/GQLQueries/favoritesLists.ts create mode 100644 src/GQLQueries/problemStatus.ts create mode 100644 src/GQLQueries/questionNote.ts create mode 100644 src/GQLQueries/streakCounter.ts create mode 100644 src/GQLQueries/submissionDetails.ts create mode 100644 src/GQLQueries/toggleFavorite.ts create mode 100644 src/GQLQueries/updateNote.ts create mode 100644 src/GQLQueries/userStatus.ts diff --git a/mcp/leetCodeService.ts b/mcp/leetCodeService.ts index 8ff3d45..4130215 100644 --- a/mcp/leetCodeService.ts +++ b/mcp/leetCodeService.ts @@ -34,6 +34,15 @@ import { userQuestionProgressQuery, userContestRankingInfoQuery, skillStatsQuery, + submissionDetailsQuery, + streakCounterQuery, + userStatusQuery, + questionNoteQuery, + updateNoteMutation, + addToFavoriteMutation, + removeFromFavoriteMutation, + favoritesListsQuery, + problemStatusQuery, } from '../src/GQLQueries'; import type { DailyProblemData, @@ -42,8 +51,17 @@ import type { TrendingDiscussionObject, UserData, } from '../src/types'; -import { executeGraphQL } from './serverUtils'; -import { SubmissionArgs, CalendarArgs, ProblemArgs, DiscussCommentsArgs, Variables } from './types'; +import { executeGraphQL, requireAuth } from './serverUtils'; +import { + SubmissionArgs, + CalendarArgs, + ProblemArgs, + DiscussCommentsArgs, + Variables, + SubmissionDetailArgs, + QuestionNoteArgs, + ToggleFavoriteArgs, +} from './types'; // Builds GraphQL variables by filtering out undefined, null, and NaN values. function buildVariables(input: Record): Variables { @@ -234,3 +252,85 @@ export async function getUserContestRankingInfo(username: string) { export async function getUserProgressRaw(username: string) { return executeGraphQL(userQuestionProgressQuery, { username }); } + +// ── Auth-required tools ───────────────────────────────────────────── + +// Retrieves authenticated user status (username, premium, checkedIn, notifications). +export async function getUserStatus() { + requireAuth(); + const data = (await executeGraphQL(userStatusQuery, {})) as { userStatus: Record }; + return data.userStatus; +} + +// Retrieves the daily streak counter for the authenticated user. +export async function getUserStreak() { + requireAuth(); + const data = (await executeGraphQL(streakCounterQuery, {})) as { streakCounter: Record }; + return data.streakCounter; +} + +// Retrieves the authenticated user's favorite/bookmark lists with problem IDs. +export async function getUserFavorites() { + requireAuth(); + const data = (await executeGraphQL(favoritesListsQuery, {})) as { + favoritesLists: { allFavorites: unknown[] }; + }; + return data.favoritesLists.allFavorites; +} + +// Retrieves full submission details including source code. +export async function getSubmissionDetails(args: SubmissionDetailArgs) { + requireAuth(); + const data = (await executeGraphQL(submissionDetailsQuery, { + submissionId: args.submissionId, + })) as { submissionDetails: Record }; + return data.submissionDetails; +} + +// Retrieves the user's personal note on a problem. +export async function getProblemNote(titleSlug: string) { + requireAuth(); + const data = (await executeGraphQL(questionNoteQuery, { titleSlug })) as { + question: { questionId: string; questionFrontendId: string; title: string; titleSlug: string; note: string | null }; + }; + return data.question; +} + +// Creates or updates a personal note on a problem. +export async function updateProblemNote(args: QuestionNoteArgs) { + requireAuth(); + const data = (await executeGraphQL(updateNoteMutation, { + titleSlug: args.titleSlug, + content: args.note ?? '', + })) as { updateNote: { question: Record } }; + return data.updateNote.question; +} + +// Adds a problem to a favorites list. +export async function addProblemToFavorite(args: ToggleFavoriteArgs) { + requireAuth(); + const data = (await executeGraphQL(addToFavoriteMutation, { + favoriteIdHash: args.favoriteIdHash, + questionId: args.questionId, + })) as { addQuestionToFavorite: { ok: boolean } }; + return { ok: data.addQuestionToFavorite.ok }; +} + +// Removes a problem from a favorites list. +export async function removeProblemFromFavorite(args: ToggleFavoriteArgs) { + requireAuth(); + const data = (await executeGraphQL(removeFromFavoriteMutation, { + favoriteIdHash: args.favoriteIdHash, + questionId: args.questionId, + })) as { removeQuestionFromFavorite: { ok: boolean } }; + return { ok: data.removeQuestionFromFavorite.ok }; +} + +// Retrieves solve status for a specific problem (lighter than full select). +export async function getProblemStatus(titleSlug: string) { + requireAuth(); + const data = (await executeGraphQL(problemStatusQuery, { titleSlug })) as { + question: Record; + }; + return data.question; +} diff --git a/mcp/modules/problemTools.ts b/mcp/modules/problemTools.ts index 1ca883f..2b09e7d 100644 --- a/mcp/modules/problemTools.ts +++ b/mcp/modules/problemTools.ts @@ -1,13 +1,19 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { z } from 'zod'; import { + addProblemToFavorite, getDailyProblem, getDailyProblemLegacy, getDailyProblemRaw, getOfficialSolution, + getProblemNote, getProblemSet, + getProblemStatus, getSelectProblem, getSelectProblemRaw, + getSubmissionDetails, + removeProblemFromFavorite, + updateProblemNote, } from '../leetCodeService'; import { runTool } from '../serverUtils'; import { ToolModule } from '../types'; @@ -92,5 +98,84 @@ export class ProblemToolsModule implements ToolModule { }, async () => runTool(() => getDailyProblemLegacy()), ); + + // ── Auth-required tools ─────────────────────────────────────────── + + server.registerTool( + 'leetcode_submission_details', + { + title: 'Submission Details', + description: '[Auth Required] Full submission: source code, runtime, memory, percentiles, errors', + inputSchema: { + submissionId: z.number().int().positive(), + }, + }, + async ({ submissionId }) => runTool(() => getSubmissionDetails({ submissionId })), + ); + + server.registerTool( + 'leetcode_problem_note', + { + title: 'Problem Note', + description: "[Auth Required] User's personal note on a problem", + inputSchema: { + titleSlug: z.string(), + }, + }, + async ({ titleSlug }) => runTool(() => getProblemNote(titleSlug)), + ); + + server.registerTool( + 'leetcode_problem_note_update', + { + title: 'Update Problem Note', + description: '[Auth Required] Create/update personal note on a problem', + inputSchema: { + titleSlug: z.string(), + note: z.string(), + }, + }, + async ({ titleSlug, note }) => runTool(() => updateProblemNote({ titleSlug, note })), + ); + + server.registerTool( + 'leetcode_problem_favorite_add', + { + title: 'Add to Favorites', + description: '[Auth Required] Add problem to a favorites list', + inputSchema: { + favoriteIdHash: z.string(), + questionId: z.string(), + }, + }, + async ({ favoriteIdHash, questionId }) => + runTool(() => addProblemToFavorite({ favoriteIdHash, questionId })), + ); + + server.registerTool( + 'leetcode_problem_favorite_remove', + { + title: 'Remove from Favorites', + description: '[Auth Required] Remove problem from a favorites list', + inputSchema: { + favoriteIdHash: z.string(), + questionId: z.string(), + }, + }, + async ({ favoriteIdHash, questionId }) => + runTool(() => removeProblemFromFavorite({ favoriteIdHash, questionId })), + ); + + server.registerTool( + 'leetcode_problem_status', + { + title: 'Problem Status', + description: '[Auth Required] Solve status for a specific problem (ac/notac/null) — lighter than full select', + inputSchema: { + titleSlug: z.string(), + }, + }, + async ({ titleSlug }) => runTool(() => getProblemStatus(titleSlug)), + ); } } diff --git a/mcp/modules/userTools.ts b/mcp/modules/userTools.ts index 0ec873b..9666a48 100644 --- a/mcp/modules/userTools.ts +++ b/mcp/modules/userTools.ts @@ -13,12 +13,15 @@ import { getUserContest, getUserContestHistory, getUserContestRankingInfo, + getUserFavorites, getUserProfileAggregate, getUserProfileCalendarRaw, getUserProfileRaw, getUserProfileSummary, getUserProgress, getUserProgressRaw, + getUserStatus, + getUserStreak, } from '../leetCodeService'; import { runTool } from '../serverUtils'; import { ToolModule } from '../types'; @@ -248,5 +251,34 @@ export class UserToolsModule implements ToolModule { }, async ({ username }) => runTool(() => getUserProgressRaw(username)), ); + + // ── Auth-required tools ─────────────────────────────────────────── + + server.registerTool( + 'leetcode_user_status', + { + title: 'Authenticated User Status', + description: '[Auth Required] Authenticated user info: username, isPremium, checkedInToday, notifications', + }, + async () => runTool(() => getUserStatus()), + ); + + server.registerTool( + 'leetcode_user_streak', + { + title: 'Daily Streak', + description: '[Auth Required] Daily streak: currentStreak, daysSkipped, todayCompleted', + }, + async () => runTool(() => getUserStreak()), + ); + + server.registerTool( + 'leetcode_user_favorites', + { + title: 'Favorite Lists', + description: "[Auth Required] User's favorite/bookmark problem lists with problem IDs", + }, + async () => runTool(() => getUserFavorites()), + ); } } diff --git a/mcp/serverUtils.ts b/mcp/serverUtils.ts index 301fd66..642ab79 100644 --- a/mcp/serverUtils.ts +++ b/mcp/serverUtils.ts @@ -6,6 +6,16 @@ import { GraphQLParams, GraphQLClientError, ToolResponse, ToolExecutor, ToolModu const GRAPHQL_ENDPOINT = 'https://leetcode.com/graphql'; export const SERVER_VERSION = '1.0.0'; +// Asserts that auth cookies are configured. Throws a clear error if not. +export function requireAuth(): void { + const auth = loadAuthData(); + if (!auth.cookie) { + throw new Error( + 'This tool requires authentication. Set LEETCODE_COOKIES_FILE env var with a path to your cookies JSON file.', + ); + } +} + // Loads auth cookies from LEETCODE_COOKIES_FILE env var. function loadAuthData(): { cookie: string; csrftoken: string } { const cookieFile = process.env.LEETCODE_COOKIES_FILE; diff --git a/mcp/types.ts b/mcp/types.ts index c322aff..6dbc245 100644 --- a/mcp/types.ts +++ b/mcp/types.ts @@ -76,4 +76,19 @@ export type ToolExecutor = () => Promise; */ export interface ToolModule { register(server: McpServer): void; -} \ No newline at end of file +} + +/** + * Arguments for submission detail lookups (auth required). + */ +export type SubmissionDetailArgs = { submissionId: number }; + +/** + * Arguments for question note operations (auth required). + */ +export type QuestionNoteArgs = { titleSlug: string; note?: string }; + +/** + * Arguments for favorite toggle operations (auth required). + */ +export type ToggleFavoriteArgs = { favoriteIdHash: string; questionId: string }; \ No newline at end of file diff --git a/src/GQLQueries/favoritesLists.ts b/src/GQLQueries/favoritesLists.ts new file mode 100644 index 0000000..6f61e22 --- /dev/null +++ b/src/GQLQueries/favoritesLists.ts @@ -0,0 +1,19 @@ +export const favoritesListsQuery = ` + query favoritesLists { + favoritesLists { + allFavorites { + idHash + name + isPublicFavorite + viewCount + creator + isWatched + questions { + questionId + title + titleSlug + } + } + } + } +`; diff --git a/src/GQLQueries/index.ts b/src/GQLQueries/index.ts index 66da032..74558cc 100644 --- a/src/GQLQueries/index.ts +++ b/src/GQLQueries/index.ts @@ -3,16 +3,27 @@ export { default as contestQuery } from './contest'; export { default as dailyProblemQuery } from './dailyProblem'; export { discussCommentsQuery } from './discussComments'; export { discussTopicQuery } from './discussTopic'; +export { favoritesListsQuery } from './favoritesLists'; export { getUserProfileQuery } from './getUserProfile'; export { default as languageStatsQuery } from './languageStats'; export { officialSolutionQuery } from './officialSolution'; export { default as problemListQuery } from './problemList'; +export { problemStatusQuery } from './problemStatus'; +export { questionNoteQuery } from './questionNote'; export { default as AcSubmissionQuery } from './recentAcSubmit'; export { default as submissionQuery } from './recentSubmit'; export { default as selectProblemQuery } from './selectProblem'; export { skillStatsQuery } from './skillStats'; +export { streakCounterQuery } from './streakCounter'; +export { submissionDetailsQuery } from './submissionDetails'; +export { + addToFavoriteMutation, + removeFromFavoriteMutation, +} from './toggleFavorite'; export { default as trendingDiscussQuery } from './trendingDiscuss'; +export { updateNoteMutation } from './updateNote'; export { userContestRankingInfoQuery } from './userContestRanking'; export { default as userProfileQuery } from './userProfile'; export { userProfileCalendarQuery } from './userProfileCalendar'; export { userQuestionProgressQuery } from './userQuestionProgress'; +export { userStatusQuery } from './userStatus'; diff --git a/src/GQLQueries/problemStatus.ts b/src/GQLQueries/problemStatus.ts new file mode 100644 index 0000000..e6e7746 --- /dev/null +++ b/src/GQLQueries/problemStatus.ts @@ -0,0 +1,13 @@ +export const problemStatusQuery = ` + query questionStatus($titleSlug: String!) { + question(titleSlug: $titleSlug) { + questionId + questionFrontendId + title + titleSlug + status + difficulty + isPaidOnly + } + } +`; diff --git a/src/GQLQueries/questionNote.ts b/src/GQLQueries/questionNote.ts new file mode 100644 index 0000000..5abaeab --- /dev/null +++ b/src/GQLQueries/questionNote.ts @@ -0,0 +1,11 @@ +export const questionNoteQuery = ` + query questionNote($titleSlug: String!) { + question(titleSlug: $titleSlug) { + questionId + questionFrontendId + title + titleSlug + note + } + } +`; diff --git a/src/GQLQueries/streakCounter.ts b/src/GQLQueries/streakCounter.ts new file mode 100644 index 0000000..fea6c6e --- /dev/null +++ b/src/GQLQueries/streakCounter.ts @@ -0,0 +1,9 @@ +export const streakCounterQuery = ` + query streakCounter { + streakCounter { + streakCount + daysSkipped + currentDayCompleted + } + } +`; diff --git a/src/GQLQueries/submissionDetails.ts b/src/GQLQueries/submissionDetails.ts new file mode 100644 index 0000000..d0ef1fe --- /dev/null +++ b/src/GQLQueries/submissionDetails.ts @@ -0,0 +1,32 @@ +export const submissionDetailsQuery = ` + query submissionDetails($submissionId: Int!) { + submissionDetails(submissionId: $submissionId) { + runtime + runtimeDisplay + runtimePercentile + memory + memoryDisplay + memoryPercentile + code + timestamp + lang { + name + verboseName + } + question { + questionId + titleSlug + title + } + notes + topicTags { + tagId + slug + name + } + runtimeError + compileError + lastTestcase + } + } +`; diff --git a/src/GQLQueries/toggleFavorite.ts b/src/GQLQueries/toggleFavorite.ts new file mode 100644 index 0000000..4d53e4f --- /dev/null +++ b/src/GQLQueries/toggleFavorite.ts @@ -0,0 +1,15 @@ +export const addToFavoriteMutation = ` + mutation addQuestionToFavorite($favoriteIdHash: String!, $questionId: String!) { + addQuestionToFavorite(favoriteIdHash: $favoriteIdHash, questionId: $questionId) { + ok + } + } +`; + +export const removeFromFavoriteMutation = ` + mutation removeQuestionFromFavorite($favoriteIdHash: String!, $questionId: String!) { + removeQuestionFromFavorite(favoriteIdHash: $favoriteIdHash, questionId: $questionId) { + ok + } + } +`; diff --git a/src/GQLQueries/updateNote.ts b/src/GQLQueries/updateNote.ts new file mode 100644 index 0000000..389c7e8 --- /dev/null +++ b/src/GQLQueries/updateNote.ts @@ -0,0 +1,10 @@ +export const updateNoteMutation = ` + mutation updateNote($titleSlug: String!, $content: String!) { + updateNote(titleSlug: $titleSlug, content: $content) { + question { + questionId + note + } + } + } +`; diff --git a/src/GQLQueries/userStatus.ts b/src/GQLQueries/userStatus.ts new file mode 100644 index 0000000..16766d4 --- /dev/null +++ b/src/GQLQueries/userStatus.ts @@ -0,0 +1,18 @@ +export const userStatusQuery = ` + query globalData { + userStatus { + userId + isSignedIn + isPremium + isVerified + username + avatar + isAdmin + checkedInToday + notificationStatus { + lastModified + numUnread + } + } + } +`;