Skip to content

[Feat]: Resolved issue #35 and improved bot.js #36

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
337 changes: 178 additions & 159 deletions bot.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Client, GatewayIntentBits, Partials,EmbedBuilder } from 'discord.js';
import { Client, GatewayIntentBits, Partials, EmbedBuilder } from 'discord.js';
import { LeetCode } from 'leetcode-query';
import cron from 'node-cron';
import keepAlive from './keep_alive.js';
Expand All @@ -18,6 +18,17 @@ const client = new Client({
]
});

let leetcodeProblems = [];

async function fetchLeetCodeProblems() {
try {
const response = await axios.get('https://leetcode.com/api/problems/all/');
leetcodeProblems = response.data.stat_status_pairs.filter(problem => !problem.paid_only);
} catch (error) {
console.error('Error fetching LeetCode problems:', error);
}
}

function calculateStreak(submissionCalendar) {
submissionCalendar = JSON.parse(submissionCalendar);
const datesWithSubmissions = Object.entries(submissionCalendar)
Expand All @@ -31,12 +42,10 @@ function calculateStreak(submissionCalendar) {
let hasSolvedToday = false;
const currentDate = new Date();
const lastSubmissionDate = datesWithSubmissions[datesWithSubmissions.length - 1];
// Check if the last submission was today
if (lastSubmissionDate.setHours(0, 0, 0, 0) === currentDate.setHours(0, 0, 0, 0)) {
hasSolvedToday = true;
currentDate.setDate(currentDate.getDate() + 1);
}
// Calculate streak
for (let i = datesWithSubmissions.length - 1; i >= 0; i--) {
currentDate.setDate(currentDate.getDate() - 1);
if (datesWithSubmissions[i].setHours(0, 0, 0, 0) === currentDate.setHours(0, 0, 0, 0)) {
Expand All @@ -48,18 +57,154 @@ function calculateStreak(submissionCalendar) {
return {currentStreak, hasSolvedToday};
}

async function fetchLeetCodeProblems() {
async function handlePotdCommand(message) {
try {
const response = await axios.get('https://leetcode.com/api/problems/all/');
leetcodeProblems = response.data.stat_status_pairs.filter(problem => !problem.paid_only);
const daily = await lc.daily();
const questionLink = `https://leetcode.com${daily.link}`;

const embed = new EmbedBuilder()
.setTitle(`LeetCode Daily Challenge - ${daily.date}`)
.setURL(questionLink)
.setDescription(`**${daily.question.title}**`)
.setColor(0xD1006C)
.addFields(
{ name: 'Difficulty', value: daily.question.difficulty, inline: true },
{ name: 'Link', value: `[Click here](${questionLink})`, inline: true }
)
.setFooter({ text: 'Good luck solving today\'s problem!' });

message.channel.send({ embeds: [embed] });
} catch (error) {
console.error('Error fetching LeetCode problems:', error);
console.error('Error fetching LeetCode daily challenge:', error);
message.channel.send('Sorry, I could not fetch the LeetCode daily challenge question.');
}
}

async function handleRandomCommand(message, args) {
try {
let difficulty = args[1] ? args[1].toLowerCase() : null;
const difficultyLevel = { 'easy': 1, 'medium': 2, 'hard': 3 };
const difficultyColors = { 'easy': 0x00FF00, 'medium': 0xFFFF00, 'hard': 0xFF0000 };

if (leetcodeProblems.length === 0) {
await fetchLeetCodeProblems();
}

let filteredProblems = leetcodeProblems;

if (difficulty && difficultyLevel[difficulty]) {
filteredProblems = leetcodeProblems.filter(problem => problem.difficulty.level === difficultyLevel[difficulty]);
}

if (filteredProblems.length === 0) {
message.channel.send(`Sorry, I couldn't find any LeetCode problems with the specified difficulty level: ${difficulty}`);
return;
}

const randomIndex = Math.floor(Math.random() * filteredProblems.length);
const problem = filteredProblems[randomIndex].stat;
const questionLink = `https://leetcode.com/problems/${problem.question__title_slug}/`;

const lc = new LeetCode();
let leetcodeProblems = []
const embedColor = difficulty ? difficultyColors[difficulty] : 0x7289DA;

const embed = new EmbedBuilder()
.setTitle(`${problem.question__title}`)
.setURL(questionLink)
.setColor(embedColor)
.addFields(
{ name: 'Difficulty', value: difficulty ? difficulty.charAt(0).toUpperCase() + difficulty.slice(1) : 'N/A', inline: true },
{ name: 'Link', value: `[Solve Problem](${questionLink})`, inline: true },
{ name: 'Acceptance Rate', value: `${problem.total_acs} / ${problem.total_submitted} (${(problem.total_acs / problem.total_submitted * 100).toFixed(2)}%)`, inline: true }
)
.setFooter({ text: 'Good luck!' });

message.channel.send({ embeds: [embed] });
} catch (error) {
console.error('Error fetching random LeetCode question:', error);
message.channel.send('Sorry, I could not fetch a random LeetCode question.');
}
}

async function handleUserCommand(message, args) {
const username = args[1];
try {
const [userInfo, contestInfo] = await Promise.all([
lc.user(username),
lc.user_contest_info(username)
]);

if (!userInfo.matchedUser) {
message.channel.send(`User "${username}" not found.`);
return;
}

const user = userInfo.matchedUser;
const profile = user.profile;
const submitStats = user.submitStats;

const embed = new EmbedBuilder()
.setColor('#FFD700')
.setTitle(`LeetCode Profile: **${username}**`)
.setThumbnail(profile.userAvatar)
.addFields(
{ name: '👤 Real Name', value: profile.realName || '*Not provided*', inline: true },
{ name: '🏆 Ranking', value: profile.ranking ? profile.ranking.toString() : '*Not ranked*', inline: true },
{ name: '🌍 Country', value: profile.countryName || '*Not provided*', inline: true },
{ name: '🏢 Company', value: profile.company || '*Not provided*', inline: true },
{ name: '🎓 School', value: profile.school || '*Not provided*', inline: true },
{ name: '\u200B', value: '⬇️ **Problem Solving Stats**', inline: false },
{ name: '🟢 Easy', value: `Solved: ${submitStats.acSubmissionNum[1].count} / ${submitStats.totalSubmissionNum[1].count}`, inline: true },
{ name: '🟠 Medium', value: `Solved: ${submitStats.acSubmissionNum[2].count} / ${submitStats.totalSubmissionNum[2].count}`, inline: true },
{ name: '🔴 Hard', value: `Solved: ${submitStats.acSubmissionNum[3].count} / ${submitStats.totalSubmissionNum[3].count}`, inline: true },
{ name: '📊 Total', value: `Solved: ${submitStats.acSubmissionNum[0].count} / ${submitStats.totalSubmissionNum[0].count}`, inline: true }
);

if (contestInfo.userContestRanking) {
embed.addFields(
{ name: '🚩 **Contest Info**', value: `\`\`\`Rating: ${Math.round(contestInfo.userContestRanking.rating)}\nRanking: ${contestInfo.userContestRanking.globalRanking}\nTop: ${contestInfo.userContestRanking.topPercentage.toFixed(2)}%\nAttended: ${contestInfo.userContestRanking.attendedContestsCount}\`\`\`` }
);
}

if (user.badges && user.badges.length > 0) {
const badgeNames = user.badges.map(badge => badge.displayName).join('\n•');
embed.addFields({ name: '🏅 Badges', value: "•"+badgeNames, inline: false });
}

message.channel.send({ embeds: [embed] });

} catch (error) {
console.error('Error fetching user info:', error);
message.channel.send('Sorry, I could not fetch the user info.');
}
}

async function handleStreakCommand(message, args) {
const username = args[1];
try {
const user = await lc.user(username);
let streakInfo = 0;
let hasSolvedToday = false;
if(user.matchedUser) {
({ currentStreak: streakInfo, hasSolvedToday } = calculateStreak(user.matchedUser.submissionCalendar));
}

let streakMessage;
if (streakInfo > 0) {
if (hasSolvedToday) {
streakMessage = `🎉 **${username}** has solved a problem for ${streakInfo} consecutive days! Great work, keep it up! 💪`;
} else {
streakMessage = `⚠️ **${username}** has solved a problem for ${streakInfo} consecutive days! Solve today's problem to maintain your streak and prevent it from resetting! 🔄`;
}
} else {
streakMessage = `❌ **${username}** does not have a streak yet. Start solving problems today to build your streak! 🚀`;
}

message.channel.send(streakMessage);
} catch (error) {
console.error('Error fetching streak info:', error);
message.channel.send('Sorry, I could not fetch the streak info.');
}
}

client.once('ready', () => {
console.log(`Logged in as ${client.user.tag}!`);
Expand Down Expand Up @@ -90,158 +235,32 @@ client.on('messageCreate', async (message) => {
const args = message.content.split(' ');
const command = args[0].toLowerCase();

if (command === ';potd') {
try {
const daily = await lc.daily();
const questionLink = `https://leetcode.com${daily.link}`;

const embed = new EmbedBuilder()
.setTitle(`LeetCode Daily Challenge - ${daily.date}`)
.setURL(questionLink)
.setDescription(`**${daily.question.title}**`)
.setColor(0xD1006C)
.addFields(
{ name: 'Difficulty', value: daily.question.difficulty, inline: true },
{ name: 'Link', value: `[Click here](${questionLink})`, inline: true }
)
.setFooter({ text: 'Good luck solving today\'s problem!' });

message.channel.send({ embeds: [embed] });
} catch (error) {
console.error('Error fetching LeetCode daily challenge:', error);
message.channel.send('Sorry, I could not fetch the LeetCode daily challenge question.');
}
} else if (command === ';random') {
try {
let difficulty = args[1] ? args[1].toLowerCase() : null;
const difficultyLevel = { 'easy': 1, 'medium': 2, 'hard': 3 };
const difficultyColors = { 'easy': 0x00FF00, 'medium': 0xFFFF00, 'hard': 0xFF0000 }; // Green, Yellow, Red

if (leetcodeProblems.length === 0) {
await fetchLeetCodeProblems();
}

let filteredProblems = leetcodeProblems;

if (difficulty && difficultyLevel[difficulty]) {
filteredProblems = leetcodeProblems.filter(problem => problem.difficulty.level === difficultyLevel[difficulty]);
}

if (filteredProblems.length === 0) {
message.channel.send(`Sorry, I couldn't find any LeetCode problems with the specified difficulty level: ${difficulty}`);
return;
}

const randomIndex = Math.floor(Math.random() * filteredProblems.length);
const problem = filteredProblems[randomIndex].stat;
const questionLink = `https://leetcode.com/problems/${problem.question__title_slug}/`;

const embedColor = difficulty ? difficultyColors[difficulty] : 0x7289DA; // Default is Discord's blue

const embed = new EmbedBuilder()
.setTitle(`${problem.question__title}`)
.setURL(questionLink)
.setColor(embedColor)
.addFields(
{ name: 'Difficulty', value: difficulty ? difficulty.charAt(0).toUpperCase() + difficulty.slice(1) : 'N/A', inline: true },
{ name: 'Link', value: `[Solve Problem](${questionLink})`, inline: true },
{ name: 'Acceptance Rate', value: `${problem.total_acs} / ${problem.total_submitted} (${(problem.total_acs / problem.total_submitted * 100).toFixed(2)}%)`, inline: true }
)
.setFooter({ text: 'Good luck!' });

message.channel.send({ embeds: [embed] });
} catch (error) {
console.error('Error fetching random LeetCode question:', error);
message.channel.send('Sorry, I could not fetch a random LeetCode question.');
}
} else if (command === ';user' && args.length === 2) {
const username = args[1];
try {
const [userInfo, contestInfo] = await Promise.all([
lc.user(username),
lc.user_contest_info(username)
]);

if (!userInfo.matchedUser) {
message.channel.send(`User "${username}" not found.`);
return;
}

const user = userInfo.matchedUser;
const profile = user.profile;
const submitStats = user.submitStats;

const embed = new EmbedBuilder()
.setColor('#FFD700') // Gold color for the embed
.setTitle(`LeetCode Profile: **${username}**`)
.setThumbnail(profile.userAvatar)
.addFields(
{ name: '👤 Real Name', value: profile.realName || '*Not provided*', inline: true },
{ name: '🏆 Ranking', value: profile.ranking ? profile.ranking.toString() : '*Not ranked*', inline: true },
{ name: '🌍 Country', value: profile.countryName || '*Not provided*', inline: true },
{ name: '🏢 Company', value: profile.company || '*Not provided*', inline: true },
{ name: '🎓 School', value: profile.school || '*Not provided*', inline: true },
{ name: '\u200B', value: '⬇️ **Problem Solving Stats**', inline: false },
{ name: '🟢 Easy', value: `Solved: ${submitStats.acSubmissionNum[1].count} / ${submitStats.totalSubmissionNum[1].count}`, inline: true },
{ name: '🟠 Medium', value: `Solved: ${submitStats.acSubmissionNum[2].count} / ${submitStats.totalSubmissionNum[2].count}`, inline: true },
{ name: '🔴 Hard', value: `Solved: ${submitStats.acSubmissionNum[3].count} / ${submitStats.totalSubmissionNum[3].count}`, inline: true },
{ name: '📊 Total', value: `Solved: ${submitStats.acSubmissionNum[0].count} / ${submitStats.totalSubmissionNum[0].count}`, inline: true }
);

// Add contest info
if (contestInfo.userContestRanking) {
embed.addFields(
{ name: '🚩 **Contest Info**', value: `\`\`\`Rating: ${Math.round(contestInfo.userContestRanking.rating)}\nRanking: ${contestInfo.userContestRanking.globalRanking}\nTop: ${contestInfo.userContestRanking.topPercentage.toFixed(2)}%\nAttended: ${contestInfo.userContestRanking.attendedContestsCount}\`\`\`` }
);
}

// Add badges if any
if (user.badges && user.badges.length > 0) {
const badgeNames = user.badges.map(badge => badge.displayName).join('\n•');
embed.addFields({ name: '🏅 Badges', value: "•"+badgeNames, inline: false });
switch (command) {
case ';potd':
await handlePotdCommand(message);
break;
case ';random':
await handleRandomCommand(message, args);
break;
case ';user':
if (args.length === 2) {
await handleUserCommand(message, args);
}

message.channel.send({ embeds: [embed] });

} catch (error) {
console.error('Error fetching user info:', error);
message.channel.send('Sorry, I could not fetch the user info.');
}
} else if (command === ';streak' && args.length === 2) {
const username = args[1];
try {
const user = await lc.user(username);
let streakInfo = 0;
let hasSolvedToday = false;
if(user.matchedUser) {
({ currentStreak: streakInfo, hasSolvedToday } = calculateStreak(user.matchedUser.submissionCalendar));
break;
case ';streak':
if (args.length === 2) {
await handleStreakCommand(message, args);
}

let streakMessage;
if (streakInfo > 0) {
if (hasSolvedToday) {
streakMessage = `🎉 **${username}** has solved a problem for ${streakInfo} consecutive days! Great work, keep it up! 💪`;
} else {
streakMessage = `⚠️ **${username}** has solved a problem for ${streakInfo} consecutive days! Solve today's problem to maintain your streak and prevent it from resetting! 🔄`;
}
} else {
streakMessage = `❌ **${username}** does not have a streak yet. Start solving problems today to build your streak! 🚀`;
}

message.channel.send(streakMessage);
} catch (error) {
console.error('Error fetching streak info:', error);
message.channel.send('Sorry, I could not fetch the streak info.');
}
}
else if (command === ';help') {
const helpMessage = `**Available Commands:**\n
\`;potd\` - Shows the LeetCode Daily Challenge\n
\`;random\` - Shows a random LeetCode problem\n
\`;user <username>\` - Shows user Info\n
\`;streak <username>\` - Shows user Streak Info
\`;help\` - Shows help message`;
message.channel.send(helpMessage);
break;
case ';help':
const helpMessage = `**Available Commands:**\n
\`;potd\` - Shows the LeetCode Daily Challenge\n
\`;random\` - Shows a random LeetCode problem\n
\`;user <username>\` - Shows user Info\n
\`;streak <username>\` - Shows user Streak Info
\`;help\` - Shows help message`;
message.channel.send(helpMessage);
break;
}
});

Expand Down