Skip to content

Commit d906371

Browse files
committed
feat: add user notes feature for LeetCode CN
1 parent fced29b commit d906371

File tree

6 files changed

+407
-0
lines changed

6 files changed

+407
-0
lines changed

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { LeetCodeServiceFactory } from "./leetcode/leetcode-service-factory.js";
1010
import { registerProblemResources } from "./mcp/resources/problem-resources.js";
1111
import { registerSolutionResources } from "./mcp/resources/solution-resources.js";
1212
import { registerContestTools } from "./mcp/tools/contest-tools.js";
13+
import { registerNoteTools } from "./mcp/tools/note-tools.js";
1314
import { registerProblemTools } from "./mcp/tools/problem-tools.js";
1415
import { registerSolutionTools } from "./mcp/tools/solution-tools.js";
1516
import { registerUserTools } from "./mcp/tools/user-tools.js";
@@ -92,6 +93,7 @@ async function main() {
9293
registerUserTools(server, leetcodeService);
9394
registerContestTools(server, leetcodeService);
9495
registerSolutionTools(server, leetcodeService);
96+
registerNoteTools(server, leetcodeService);
9597

9698
registerProblemResources(server, leetcodeService);
9799
registerSolutionResources(server, leetcodeService);
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/**
2+
* GraphQL query for fetching user notes on LeetCode CN
3+
* This query allows retrieving notes with pagination, filtering, and sorting options
4+
*
5+
* @param orderBy - Optional sorting criteria for notes (e.g., "ASCENDING", "DESCENDING")
6+
*/
7+
export const NOTE_AGGREGATE_QUERY = `
8+
query noteAggregateNote(
9+
$aggregateType: AggregateNoteEnum!
10+
$keyword: String
11+
$orderBy: AggregateNoteSortingOrderEnum
12+
$limit: Int = 100
13+
$skip: Int = 0
14+
) {
15+
noteAggregateNote(
16+
aggregateType: $aggregateType
17+
keyword: $keyword
18+
orderBy: $orderBy
19+
limit: $limit
20+
skip: $skip
21+
) {
22+
count
23+
userNotes {
24+
id
25+
summary
26+
content
27+
... on NoteAggregateQuestionNoteNode {
28+
noteQuestion {
29+
linkTemplate
30+
questionId
31+
title
32+
translatedTitle
33+
}
34+
}
35+
}
36+
}
37+
}`;
38+
39+
/**
40+
* GraphQL query for fetching user notes for a specific question ID on LeetCode CN
41+
*/
42+
export const NOTE_BY_QUESTION_ID_QUERY = `
43+
query noteOneTargetCommonNote(
44+
$noteType: NoteCommonTypeEnum!
45+
$questionId: String!
46+
$limit: Int = 20
47+
$skip: Int = 0
48+
) {
49+
noteOneTargetCommonNote(
50+
noteType: $noteType
51+
targetId: $questionId
52+
limit: $limit
53+
skip: $skip
54+
) {
55+
count
56+
userNotes {
57+
id
58+
summary
59+
content
60+
}
61+
}
62+
}`;

src/leetcode/leetcode-base-service.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,4 +170,41 @@ export interface LeetCodeBaseService {
170170
* @returns Promise resolving to the solution article detail data
171171
*/
172172
fetchSolutionArticleDetail(identifier: string): Promise<any>;
173+
174+
/**
175+
* Retrieves user notes from LeetCode with filtering and pagination options.
176+
* Note: This feature is only available on LeetCode CN.
177+
*
178+
* @param options - Query parameters for filtering notes
179+
* @param options.aggregateType - Type of notes to aggregate (e.g., "QUESTION_NOTE")
180+
* @param options.keyword - Optional search term to filter notes
181+
* @param options.orderBy - Optional sorting criteria for notes
182+
* @param options.limit - Maximum number of notes to return
183+
* @param options.skip - Number of notes to skip (for pagination)
184+
* @returns Promise resolving to the filtered notes data
185+
* @throws Error if not implemented or feature not supported
186+
*/
187+
fetchUserNotes(options: {
188+
aggregateType: string;
189+
keyword?: string;
190+
orderBy?: string;
191+
limit?: number;
192+
skip?: number;
193+
}): Promise<any>;
194+
195+
/**
196+
* Retrieves user notes for a specific question ID.
197+
* Note: This feature is only available on LeetCode CN.
198+
*
199+
* @param questionId - The question ID to fetch notes for
200+
* @param limit - Maximum number of notes to return
201+
* @param skip - Number of notes to skip (for pagination)
202+
* @returns Promise resolving to the notes data for the specified question
203+
* @throws Error if not implemented or feature not supported
204+
*/
205+
fetchNotesByQuestionId(
206+
questionId: string,
207+
limit?: number,
208+
skip?: number
209+
): Promise<any>;
173210
}

src/leetcode/leetcode-cn-service.ts

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
import { Credential, LeetCodeCN } from "leetcode-query";
2+
import {
3+
NOTE_AGGREGATE_QUERY,
4+
NOTE_BY_QUESTION_ID_QUERY
5+
} from "./graphql/cn/note-queries.js";
26
import { SEARCH_PROBLEMS_QUERY } from "./graphql/cn/search-problems.js";
37
import { SOLUTION_ARTICLE_DETAIL_QUERY } from "./graphql/cn/solution-article-detail.js";
48
import { SOLUTION_ARTICLES_QUERY } from "./graphql/cn/solution-articles.js";
@@ -358,6 +362,107 @@ export class LeetCodeCNService implements LeetCodeBaseService {
358362
}
359363
}
360364

365+
/**
366+
* Retrieves user notes from LeetCode CN with filtering and pagination options.
367+
* Available only on LeetCode CN platform.
368+
*
369+
* @param options - Query parameters for filtering notes
370+
* @param options.aggregateType - Type of notes to aggregate (e.g., "QUESTION_NOTE")
371+
* @param options.keyword - Optional search term to filter notes
372+
* @param options.orderBy - Optional sorting criteria for notes
373+
* @param options.limit - Maximum number of notes to return
374+
* @param options.skip - Number of notes to skip (for pagination)
375+
* @returns Promise resolving to the filtered notes data
376+
*/
377+
async fetchUserNotes(options: {
378+
aggregateType: string;
379+
keyword?: string;
380+
orderBy?: string;
381+
limit?: number;
382+
skip?: number;
383+
}): Promise<any> {
384+
if (!this.isAuthenticated()) {
385+
throw new Error("Authentication required to fetch user notes");
386+
}
387+
388+
try {
389+
const variables = {
390+
aggregateType: options.aggregateType,
391+
keyword: options.keyword,
392+
orderBy: options.orderBy || "DESCENDING",
393+
limit: options.limit || 20,
394+
skip: options.skip || 0
395+
};
396+
397+
return await this.leetCodeApi
398+
.graphql({
399+
query: NOTE_AGGREGATE_QUERY,
400+
variables
401+
})
402+
.then((response) => {
403+
return (
404+
response.data?.noteAggregateNote || {
405+
count: 0,
406+
userNotes: []
407+
}
408+
);
409+
});
410+
} catch (error) {
411+
console.error(`Error fetching user notes:`, error);
412+
throw error;
413+
}
414+
}
415+
416+
/**
417+
* Retrieves user notes for a specific question ID.
418+
* Available only on LeetCode CN platform.
419+
*
420+
* @param questionId - The question ID to fetch notes for
421+
* @param limit - Maximum number of notes to return (default: 20)
422+
* @param skip - Number of notes to skip (default: 0)
423+
* @returns Promise resolving to the notes data for the specified question
424+
*/
425+
async fetchNotesByQuestionId(
426+
questionId: string,
427+
limit: number = 20,
428+
skip: number = 0
429+
): Promise<any> {
430+
if (!this.isAuthenticated()) {
431+
throw new Error(
432+
"Authentication required to fetch notes by question ID"
433+
);
434+
}
435+
436+
try {
437+
const variables = {
438+
noteType: "COMMON_QUESTION",
439+
questionId: questionId,
440+
limit,
441+
skip
442+
};
443+
444+
return await this.leetCodeApi
445+
.graphql({
446+
query: NOTE_BY_QUESTION_ID_QUERY,
447+
variables
448+
})
449+
.then((response) => {
450+
return (
451+
response.data?.noteOneTargetCommonNote || {
452+
count: 0,
453+
userNotes: []
454+
}
455+
);
456+
});
457+
} catch (error) {
458+
console.error(
459+
`Error fetching notes for question ${questionId}:`,
460+
error
461+
);
462+
throw error;
463+
}
464+
}
465+
361466
isAuthenticated(): boolean {
362467
return (
363468
!!this.credential &&

src/leetcode/leetcode-global-service.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,40 @@ export class LeetCodeGlobalService implements LeetCodeBaseService {
350350
}
351351
}
352352

353+
/**
354+
* Note feature is not supported in LeetCode Global.
355+
* This method is implemented to satisfy the interface but will always throw an error.
356+
*
357+
* @param options - Query parameters (not used)
358+
* @throws Error indicating the feature is not supported on Global platform
359+
*/
360+
async fetchUserNotes(options: {
361+
aggregateType: string;
362+
keyword?: string;
363+
orderBy?: string;
364+
limit?: number;
365+
skip?: number;
366+
}): Promise<any> {
367+
throw new Error("Notes feature is not supported in LeetCode Global");
368+
}
369+
370+
/**
371+
* Note feature is not supported in LeetCode Global.
372+
* This method is implemented to satisfy the interface but will always throw an error.
373+
*
374+
* @param questionId - The question ID (not used)
375+
* @param limit - Maximum number of notes (not used)
376+
* @param skip - Pagination offset (not used)
377+
* @throws Error indicating the feature is not supported on Global platform
378+
*/
379+
async fetchNotesByQuestionId(
380+
questionId: string,
381+
limit?: number,
382+
skip?: number
383+
): Promise<any> {
384+
throw new Error("Notes feature is not supported in LeetCode Global");
385+
}
386+
353387
isAuthenticated(): boolean {
354388
return (
355389
!!this.credential &&

0 commit comments

Comments
 (0)