fix(bot): fixed duplicate achievement sending

This commit is contained in:
Ahmad 2025-06-19 23:28:16 -04:00
parent 1eb068a634
commit 86e0a59188
No known key found for this signature in database
GPG key ID: 8FD8A93530D182BF
2 changed files with 64 additions and 154 deletions

1
.github/FUNDING.yml vendored
View file

@ -1,3 +1,2 @@
github: ahmadk953
patreon: poixpixel
thanks_dev: u/gh/ahmadk953

View file

@ -1,11 +1,4 @@
import {
Message,
Client,
EmbedBuilder,
GuildMember,
TextChannel,
Guild,
} from 'discord.js';
import { Message, EmbedBuilder, TextChannel, Guild } from 'discord.js';
import {
addXpToUser,
@ -21,99 +14,65 @@ import { loadConfig } from './configLoader.js';
import { generateAchievementCard } from './achievementCardGenerator.js';
/**
* Check and process achievements for a user based on a message
* @param message - The message that triggered the check
* Handle achievement progress updates
* @param userId - ID of the user
* @param guild - Guild instance (can be null if not applicable)
* @param achievement - Achievement definition
* @param progress - Progress percentage (0-100)
* @param options - Additional options
*/
async function handleProgress(
userId: string,
guild: Guild | null,
achievement: schema.achievementDefinitionsTableTypes,
progress: number,
options: { skipAward?: boolean } = {},
): Promise<void> {
const { skipAward = false } = options;
const userAchievements = await getUserAchievements(userId);
const existing = userAchievements.find(
(a) => a.achievementId === achievement.id && a.earnedAt !== null,
);
if (progress >= 100) {
if (!existing && !skipAward) {
const awarded = await awardAchievement(userId, achievement.id);
if (awarded && guild) {
await announceAchievement(guild, userId, achievement);
}
}
} else {
await updateAchievementProgress(userId, achievement.id, progress);
}
}
/**
* Process message achievements based on user activity
* @param message - The message object from Discord
*/
export async function processMessageAchievements(
message: Message,
): Promise<void> {
if (message.author.bot) return;
const userData = await getUserLevel(message.author.id);
const allAchievements = await getAllAchievements();
const messageAchievements = allAchievements.filter(
for (const ach of allAchievements.filter(
(a) => a.requirementType === 'message_count',
);
for (const achievement of messageAchievements) {
)) {
const progress = Math.min(
100,
(userData.messagesSent / achievement.threshold) * 100,
(userData.messagesSent / ach.threshold) * 100,
);
if (progress >= 100) {
const userAchievements = await getUserAchievements(message.author.id);
const existingAchievement = userAchievements.find(
(a) => a.achievementId === achievement.id && a.earnedAt !== null,
);
if (!existingAchievement) {
const awarded = await awardAchievement(
message.author.id,
achievement.id,
);
if (awarded) {
await announceAchievement(
message.guild!,
message.author.id,
achievement,
);
}
}
} else {
await updateAchievementProgress(
message.author.id,
achievement.id,
progress,
);
}
}
const levelAchievements = allAchievements.filter(
(a) => a.requirementType === 'level',
);
for (const achievement of levelAchievements) {
const progress = Math.min(
100,
(userData.level / achievement.threshold) * 100,
);
if (progress >= 100) {
const userAchievements = await getUserAchievements(message.author.id);
const existingAchievement = userAchievements.find(
(a) => a.achievementId === achievement.id && a.earnedAt !== null,
);
if (!existingAchievement) {
const awarded = await awardAchievement(
message.author.id,
achievement.id,
);
if (awarded) {
await announceAchievement(
message.guild!,
message.author.id,
achievement,
);
}
}
} else {
await updateAchievementProgress(
message.author.id,
achievement.id,
progress,
);
}
await handleProgress(message.author.id, message.guild!, ach, progress);
}
}
/**
* Check achievements for level-ups
* @param memberId - Member ID who leveled up
* @param newLevel - New level value
* @guild - Guild instance
* Process level-up achievements when a user levels up
* @param memberId - ID of the member who leveled up
* @param newLevel - The new level the member has reached
* @param guild - Guild instance where the member belongs
*/
export async function processLevelUpAchievements(
memberId: string,
@ -121,37 +80,19 @@ export async function processLevelUpAchievements(
guild: Guild,
): Promise<void> {
const allAchievements = await getAllAchievements();
const levelAchievements = allAchievements.filter(
for (const ach of allAchievements.filter(
(a) => a.requirementType === 'level',
);
for (const achievement of levelAchievements) {
const progress = Math.min(100, (newLevel / achievement.threshold) * 100);
if (progress >= 100) {
const userAchievements = await getUserAchievements(memberId);
const existingAchievement = userAchievements.find(
(a) => a.achievementId === achievement.id && a.earnedAt !== null,
);
if (!existingAchievement) {
const awarded = await awardAchievement(memberId, achievement.id);
if (awarded) {
await announceAchievement(guild, memberId, achievement);
}
}
} else {
await updateAchievementProgress(memberId, achievement.id, progress);
}
)) {
const progress = Math.min(100, (newLevel / ach.threshold) * 100);
await handleProgress(memberId, guild, ach, progress);
}
}
/**
* Process achievements for command usage
* @param userId - User ID who used the command
* @param commandName - Name of the command
* @param client - Guild instance
* Process command usage achievements when a command is invoked
* @param userId - ID of the user who invoked the command
* @param commandName - Name of the command invoked
* @param guild - Guild instance where the command was invoked
*/
export async function processCommandAchievements(
userId: string,
@ -159,7 +100,6 @@ export async function processCommandAchievements(
guild: Guild,
): Promise<void> {
const allAchievements = await getAllAchievements();
const commandAchievements = allAchievements.filter(
(a) =>
a.requirementType === 'command_usage' &&
@ -167,26 +107,16 @@ export async function processCommandAchievements(
(a.requirement as any).command === commandName,
);
for (const achievement of commandAchievements) {
const userAchievements = await getUserAchievements(userId);
const existingAchievement = userAchievements.find(
(a) => a.achievementId === achievement.id && a.earnedAt !== null,
);
if (!existingAchievement) {
const awarded = await awardAchievement(userId, achievement.id);
if (awarded) {
await announceAchievement(guild, userId, achievement);
}
}
for (const ach of commandAchievements) {
await handleProgress(userId, guild, ach, 100);
}
}
/**
* Process achievements for reaction events (add or remove)
* @param userId - User ID who added/removed the reaction
* @param guild - Guild instance
* @param isRemoval - Whether this is a reaction removal (true) or addition (false)
* Process reaction achievements when a user reacts to a message
* @param userId - ID of the user who reacted
* @param guild - Guild instance where the reaction occurred
* @param isRemoval - Whether the reaction was removed (default: false)
*/
export async function processReactionAchievements(
userId: string,
@ -198,40 +128,21 @@ export async function processReactionAchievements(
if (member.user.bot) return;
const allAchievements = await getAllAchievements();
const reactionAchievements = allAchievements.filter(
(a) => a.requirementType === 'reactions',
);
if (reactionAchievements.length === 0) return;
if (!reactionAchievements.length) return;
const reactionCount = await getUserReactionCount(userId);
for (const achievement of reactionAchievements) {
for (const ach of reactionAchievements) {
const progress = Math.max(
0,
Math.min(100, (reactionCount / achievement.threshold) * 100),
Math.min(100, (reactionCount / ach.threshold) * 100),
);
if (progress >= 100 && !isRemoval) {
const userAchievements = await getUserAchievements(userId);
const existingAchievement = userAchievements.find(
(a) =>
a.achievementId === achievement.id &&
a.earnedAt !== null &&
a.earnedAt !== undefined &&
new Date(a.earnedAt).getTime() > 0,
);
if (!existingAchievement) {
const awarded = await awardAchievement(userId, achievement.id);
if (awarded) {
await announceAchievement(guild, userId, achievement);
}
}
}
await updateAchievementProgress(userId, achievement.id, progress);
await handleProgress(userId, guild, ach, progress, {
skipAward: isRemoval,
});
}
} catch (error) {
console.error('Error processing reaction achievements:', error);