From 5205a43dc993e3aa1be3210443cd3e4573348348 Mon Sep 17 00:00:00 2001 From: adarshm11 Date: Sat, 13 Sep 2025 20:22:11 -0700 Subject: [PATCH 1/8] add query for leetcode leaderboard --- src/APIFunctions/LeetcodeLeaderboard.js | 27 +++++++++++++++++++ .../member_services/leetcodeLeaderboard.js | 22 +++++++++++++++ src/commands/util/leetcodeLeaderboard.js | 12 +++++++++ 3 files changed, 61 insertions(+) create mode 100644 src/APIFunctions/LeetcodeLeaderboard.js create mode 100644 src/commands/member_services/leetcodeLeaderboard.js create mode 100644 src/commands/util/leetcodeLeaderboard.js diff --git a/src/APIFunctions/LeetcodeLeaderboard.js b/src/APIFunctions/LeetcodeLeaderboard.js new file mode 100644 index 00000000..6141cf66 --- /dev/null +++ b/src/APIFunctions/LeetcodeLeaderboard.js @@ -0,0 +1,27 @@ +const axios = require('axios'); +const { LEETCODE_LEADERBOARD_API_URL } = require('../../config.json'); + +/** + * @summary Query stats from the LeetCode Leaderboard. + * @return {Promise} A promise that contains the leaderboard stats + */ +function queryLeetcodeLeaderboard() { + return new Promise((resolve) => { + axios.get( + LEETCODE_LEADERBOARD_API_URL, + ) + .then((res) => { + const responseData = res.data.map((user, index) => { + const profileUrl = `https://leetcode.com/u/${user.user}/`; + return `${index + 1}. [${user.user}](${profileUrl}): ${user.points > 1 ? user.points + ' points' : user.points + ' point'}`; + }); + const trimmedResponseData = responseData.slice(0, 10).join('\n'); + resolve(trimmedResponseData); + }) + .catch(() => { + resolve('Error querying LeetCode Leaderboard.'); + }); + }); +} + +module.exports = { queryLeetcodeLeaderboard }; diff --git a/src/commands/member_services/leetcodeLeaderboard.js b/src/commands/member_services/leetcodeLeaderboard.js new file mode 100644 index 00000000..9b34435c --- /dev/null +++ b/src/commands/member_services/leetcodeLeaderboard.js @@ -0,0 +1,22 @@ +const Command = require('../Command'); +const { getLeetCodeLeaderboard } = require('../util/leetcodeLeaderboard'); +const { EmbedBuilder } = require('discord.js'); + +module.exports = new Command({ + name: 'leetcode', + description: 'Query the LeetCode Leaderboard', + aliases: [], + example: 's!leetcode', + permissions: 'member', + category: 'member services', + execute: async (message) => { + const leaderboardData = await getLeetCodeLeaderboard(); + + const embed = new EmbedBuilder() + .setColor('#0099ff') + .setTitle('LeetCode Leaderboard') + .setDescription(leaderboardData); + + return message.channel.send({ embeds: [embed] }); + }, +}); diff --git a/src/commands/util/leetcodeLeaderboard.js b/src/commands/util/leetcodeLeaderboard.js new file mode 100644 index 00000000..17fd90e5 --- /dev/null +++ b/src/commands/util/leetcodeLeaderboard.js @@ -0,0 +1,12 @@ +const { + queryLeetcodeLeaderboard +} = require('../../APIFunctions/LeetcodeLeaderboard'); + +/** + * Queries stats from the LeetCode leaderboard. + */ +async function getLeetCodeLeaderboard() { + return await queryLeetcodeLeaderboard(); +} + +module.exports = { getLeetCodeLeaderboard }; From 9448a1b4c21515ce2bcd8feab529df7c44595892 Mon Sep 17 00:00:00 2001 From: adarshm11 Date: Sat, 13 Sep 2025 20:27:27 -0700 Subject: [PATCH 2/8] lint fix --- src/APIFunctions/LeetcodeLeaderboard.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/APIFunctions/LeetcodeLeaderboard.js b/src/APIFunctions/LeetcodeLeaderboard.js index 6141cf66..a4d40071 100644 --- a/src/APIFunctions/LeetcodeLeaderboard.js +++ b/src/APIFunctions/LeetcodeLeaderboard.js @@ -13,7 +13,12 @@ function queryLeetcodeLeaderboard() { .then((res) => { const responseData = res.data.map((user, index) => { const profileUrl = `https://leetcode.com/u/${user.user}/`; - return `${index + 1}. [${user.user}](${profileUrl}): ${user.points > 1 ? user.points + ' points' : user.points + ' point'}`; + return ( + `${index + 1}. [${user.user}](${profileUrl}): ` + + (user.points > 1 + ? user.points + ' points' + : user.points + ' point') + ); }); const trimmedResponseData = responseData.slice(0, 10).join('\n'); resolve(trimmedResponseData); From e9062e8e5bbd6b40b438328f9c221c741a7f2d06 Mon Sep 17 00:00:00 2001 From: adarshm11 Date: Mon, 29 Dec 2025 13:14:35 -0800 Subject: [PATCH 3/8] lc leaderboard embed --- src/APIFunctions/LeetcodeLeaderboard.js | 14 +--- .../member_services/leetcodeLeaderboard.js | 82 ++++++++++++++++++- 2 files changed, 80 insertions(+), 16 deletions(-) diff --git a/src/APIFunctions/LeetcodeLeaderboard.js b/src/APIFunctions/LeetcodeLeaderboard.js index a4d40071..b952593c 100644 --- a/src/APIFunctions/LeetcodeLeaderboard.js +++ b/src/APIFunctions/LeetcodeLeaderboard.js @@ -11,20 +11,10 @@ function queryLeetcodeLeaderboard() { LEETCODE_LEADERBOARD_API_URL, ) .then((res) => { - const responseData = res.data.map((user, index) => { - const profileUrl = `https://leetcode.com/u/${user.user}/`; - return ( - `${index + 1}. [${user.user}](${profileUrl}): ` + - (user.points > 1 - ? user.points + ' points' - : user.points + ' point') - ); - }); - const trimmedResponseData = responseData.slice(0, 10).join('\n'); - resolve(trimmedResponseData); + resolve(res.data); }) .catch(() => { - resolve('Error querying LeetCode Leaderboard.'); + resolve(null); }); }); } diff --git a/src/commands/member_services/leetcodeLeaderboard.js b/src/commands/member_services/leetcodeLeaderboard.js index 9b34435c..f72f58df 100644 --- a/src/commands/member_services/leetcodeLeaderboard.js +++ b/src/commands/member_services/leetcodeLeaderboard.js @@ -2,6 +2,11 @@ const Command = require('../Command'); const { getLeetCodeLeaderboard } = require('../util/leetcodeLeaderboard'); const { EmbedBuilder } = require('discord.js'); +const MONTHS = [ + 'January', 'February', 'March', 'April', 'May', 'June', + 'July', 'August', 'September', 'October', 'November', 'December' +]; + module.exports = new Command({ name: 'leetcode', description: 'Query the LeetCode Leaderboard', @@ -10,12 +15,81 @@ module.exports = new Command({ permissions: 'member', category: 'member services', execute: async (message) => { - const leaderboardData = await getLeetCodeLeaderboard(); + const data = await getLeetCodeLeaderboard(); + + if (!data || !data.leaderboard) { + return message.channel.send('Error querying LeetCode Leaderboard.'); + } + + const { leaderboard, month } = data; + const top10 = leaderboard.slice(0, 10); + + let maxNameLen = 'Username'.length; + let maxPointsLen = 'Points'.length; + + top10.forEach((user, index) => { + let name = user.username; + if (name.length > 10) { + name = name.substring(0, 10); + } + user.displayName = name; + + const rank = index + 1; + const rankStr = rank < 10 ? ` ${rank}. ` : `${rank}. `; + + if (rankStr.length + name.length > maxNameLen) { + maxNameLen = rankStr.length + name.length; + } + + const pointsStr = user.points.toString(); + if (pointsStr.length > maxPointsLen) { + maxPointsLen = pointsStr.length; + } + }); + + const gap = 4; + + let description = '```ansi\n'; + + const title = 'LeetCode Leaderboard'; + const totalWidth = maxNameLen + gap + maxPointsLen; + const titlePadding = Math.floor((totalWidth - title.length) / 2); + description += `\u001b[0;33m${' '.repeat(titlePadding)}${title}\u001b[0m\n`; + + const headerName = 'Username'; + const headerPoints = 'Points'; + const headerNamePadding = ' '.repeat(maxNameLen - headerName.length + gap); + const headerPointsPadding = ' '.repeat(maxPointsLen - headerPoints.length); + + description += `\u001b[0;37m${headerName}${headerNamePadding}${headerPointsPadding}${headerPoints}\u001b[0m\n`; + + top10.forEach((user, index) => { + const rank = index + 1; + const name = user.displayName; + const points = user.points.toString(); + + const rankStr = rank < 10 ? ` ${rank}. ` : `${rank}. `; + const nameCol = `${rankStr}${name}`; + const namePadding = ' '.repeat(maxNameLen - nameCol.length + gap); + const pointsPadding = ' '.repeat(maxPointsLen - points.length); + + let colorCode = '\u001b[0;37m'; + if (rank === 1) colorCode = '\u001b[0;33m'; + else if (rank === 2) colorCode = '\u001b[0;31m'; + else if (rank === 3) colorCode = '\u001b[0;34m'; + + description += `${colorCode}${nameCol}${namePadding}${pointsPadding}${points}\u001b[0m\n`; + }); + + const monthText = `Month: ${MONTHS[month]}`; + const monthPadding = Math.floor((totalWidth - monthText.length) / 2); + description += `\u001b[0;33m${' '.repeat(monthPadding)}${monthText}\u001b[0m\n`; + + description += '```'; const embed = new EmbedBuilder() - .setColor('#0099ff') - .setTitle('LeetCode Leaderboard') - .setDescription(leaderboardData); + .setColor('#000000') + .setDescription(description); return message.channel.send({ embeds: [embed] }); }, From a5535c603b5059a9f0fa26b3bd9634018355a255 Mon Sep 17 00:00:00 2001 From: adarshm11 Date: Mon, 29 Dec 2025 13:18:07 -0800 Subject: [PATCH 4/8] lint fix --- src/commands/member_services/leetcodeLeaderboard.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/commands/member_services/leetcodeLeaderboard.js b/src/commands/member_services/leetcodeLeaderboard.js index f72f58df..03fbb056 100644 --- a/src/commands/member_services/leetcodeLeaderboard.js +++ b/src/commands/member_services/leetcodeLeaderboard.js @@ -61,7 +61,8 @@ module.exports = new Command({ const headerNamePadding = ' '.repeat(maxNameLen - headerName.length + gap); const headerPointsPadding = ' '.repeat(maxPointsLen - headerPoints.length); - description += `\u001b[0;37m${headerName}${headerNamePadding}${headerPointsPadding}${headerPoints}\u001b[0m\n`; + description += `\u001b[0;37m${headerName}${headerNamePadding}` + + `${headerPointsPadding}${headerPoints}\u001b[0m\n`; top10.forEach((user, index) => { const rank = index + 1; @@ -78,12 +79,14 @@ module.exports = new Command({ else if (rank === 2) colorCode = '\u001b[0;31m'; else if (rank === 3) colorCode = '\u001b[0;34m'; - description += `${colorCode}${nameCol}${namePadding}${pointsPadding}${points}\u001b[0m\n`; + description += `${colorCode}${nameCol}${namePadding}` + + `${pointsPadding}${points}\u001b[0m\n`; }); const monthText = `Month: ${MONTHS[month]}`; const monthPadding = Math.floor((totalWidth - monthText.length) / 2); - description += `\u001b[0;33m${' '.repeat(monthPadding)}${monthText}\u001b[0m\n`; + description += `\u001b[0;33m${' '.repeat(monthPadding)}` + + `${monthText}\u001b[0m\n`; description += '```'; From 3479c85636e00606d5e54cb77c429bd53ebe0e32 Mon Sep 17 00:00:00 2001 From: adarshm11 Date: Mon, 29 Dec 2025 13:20:23 -0800 Subject: [PATCH 5/8] remove useless util function --- src/commands/member_services/leetcodeLeaderboard.js | 4 ++-- src/commands/util/leetcodeLeaderboard.js | 12 ------------ 2 files changed, 2 insertions(+), 14 deletions(-) delete mode 100644 src/commands/util/leetcodeLeaderboard.js diff --git a/src/commands/member_services/leetcodeLeaderboard.js b/src/commands/member_services/leetcodeLeaderboard.js index 03fbb056..64bae9cb 100644 --- a/src/commands/member_services/leetcodeLeaderboard.js +++ b/src/commands/member_services/leetcodeLeaderboard.js @@ -1,5 +1,5 @@ const Command = require('../Command'); -const { getLeetCodeLeaderboard } = require('../util/leetcodeLeaderboard'); +const { queryLeetcodeLeaderboard } = require('../../APIFunctions/LeetcodeLeaderboard'); const { EmbedBuilder } = require('discord.js'); const MONTHS = [ @@ -15,7 +15,7 @@ module.exports = new Command({ permissions: 'member', category: 'member services', execute: async (message) => { - const data = await getLeetCodeLeaderboard(); + const data = await queryLeetcodeLeaderboard(); if (!data || !data.leaderboard) { return message.channel.send('Error querying LeetCode Leaderboard.'); diff --git a/src/commands/util/leetcodeLeaderboard.js b/src/commands/util/leetcodeLeaderboard.js deleted file mode 100644 index 17fd90e5..00000000 --- a/src/commands/util/leetcodeLeaderboard.js +++ /dev/null @@ -1,12 +0,0 @@ -const { - queryLeetcodeLeaderboard -} = require('../../APIFunctions/LeetcodeLeaderboard'); - -/** - * Queries stats from the LeetCode leaderboard. - */ -async function getLeetCodeLeaderboard() { - return await queryLeetcodeLeaderboard(); -} - -module.exports = { getLeetCodeLeaderboard }; From fa8fd703a5e92fbb47c40e77e7abe1af0d827cd2 Mon Sep 17 00:00:00 2001 From: adarshm11 Date: Mon, 29 Dec 2025 13:21:20 -0800 Subject: [PATCH 6/8] lint is quite annoying vro --- src/commands/member_services/leetcodeLeaderboard.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/commands/member_services/leetcodeLeaderboard.js b/src/commands/member_services/leetcodeLeaderboard.js index 64bae9cb..0340ed4d 100644 --- a/src/commands/member_services/leetcodeLeaderboard.js +++ b/src/commands/member_services/leetcodeLeaderboard.js @@ -1,5 +1,7 @@ const Command = require('../Command'); -const { queryLeetcodeLeaderboard } = require('../../APIFunctions/LeetcodeLeaderboard'); +const { + queryLeetcodeLeaderboard +} = require('../../APIFunctions/LeetcodeLeaderboard'); const { EmbedBuilder } = require('discord.js'); const MONTHS = [ From aa85ec970c565df431e0a8f8d724022537be3265 Mon Sep 17 00:00:00 2001 From: adarshm11 Date: Fri, 9 Jan 2026 22:31:16 -0800 Subject: [PATCH 7/8] return error stack trace on reject --- src/APIFunctions/LeetcodeLeaderboard.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/APIFunctions/LeetcodeLeaderboard.js b/src/APIFunctions/LeetcodeLeaderboard.js index b952593c..6776e1ed 100644 --- a/src/APIFunctions/LeetcodeLeaderboard.js +++ b/src/APIFunctions/LeetcodeLeaderboard.js @@ -6,15 +6,15 @@ const { LEETCODE_LEADERBOARD_API_URL } = require('../../config.json'); * @return {Promise} A promise that contains the leaderboard stats */ function queryLeetcodeLeaderboard() { - return new Promise((resolve) => { + return new Promise((resolve, reject) => { axios.get( LEETCODE_LEADERBOARD_API_URL, ) .then((res) => { resolve(res.data); }) - .catch(() => { - resolve(null); + .catch((error) => { + reject(error); }); }); } From 27a0602187646c9225549aa9d3d97c4403bd20a0 Mon Sep 17 00:00:00 2001 From: adarshm11 Date: Fri, 9 Jan 2026 23:02:29 -0800 Subject: [PATCH 8/8] lc leaderboard command now general use --- src/commands/{member_services => util}/leetcodeLeaderboard.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename src/commands/{member_services => util}/leetcodeLeaderboard.js (98%) diff --git a/src/commands/member_services/leetcodeLeaderboard.js b/src/commands/util/leetcodeLeaderboard.js similarity index 98% rename from src/commands/member_services/leetcodeLeaderboard.js rename to src/commands/util/leetcodeLeaderboard.js index 0340ed4d..9a2e4786 100644 --- a/src/commands/member_services/leetcodeLeaderboard.js +++ b/src/commands/util/leetcodeLeaderboard.js @@ -14,8 +14,8 @@ module.exports = new Command({ description: 'Query the LeetCode Leaderboard', aliases: [], example: 's!leetcode', - permissions: 'member', - category: 'member services', + permissions: 'general', + category: 'information', execute: async (message) => { const data = await queryLeetcodeLeaderboard();