mirror of
https://github.com/ahmadk953/poixpixel-discord-bot.git
synced 2025-07-04 19:36:00 +00:00
feat(bot): added achievement system caching
This commit is contained in:
commit
a611990503
3 changed files with 58 additions and 84 deletions
|
@ -14,10 +14,10 @@ import {
|
||||||
import {
|
import {
|
||||||
getAllAchievements,
|
getAllAchievements,
|
||||||
getUserAchievements,
|
getUserAchievements,
|
||||||
awardAchievement,
|
|
||||||
createAchievement,
|
createAchievement,
|
||||||
deleteAchievement,
|
deleteAchievement,
|
||||||
removeUserAchievement,
|
removeUserAchievement,
|
||||||
|
updateAchievementProgress,
|
||||||
} from '@/db/db.js';
|
} from '@/db/db.js';
|
||||||
import { announceAchievement } from '@/util/achievementManager.js';
|
import { announceAchievement } from '@/util/achievementManager.js';
|
||||||
import { createPaginationButtons } from '@/util/helpers.js';
|
import { createPaginationButtons } from '@/util/helpers.js';
|
||||||
|
@ -309,7 +309,11 @@ async function handleAwardAchievement(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const success = await awardAchievement(user.id, achievementId);
|
const success = await updateAchievementProgress(
|
||||||
|
user.id,
|
||||||
|
achievementId,
|
||||||
|
100,
|
||||||
|
);
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
await announceAchievement(interaction.guild!, user.id, achievement);
|
await announceAchievement(interaction.guild!, user.id, achievement);
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
import { and, eq } from 'drizzle-orm';
|
import { and, eq } from 'drizzle-orm';
|
||||||
|
|
||||||
import { db, ensureDbInitialized, handleDbError } from '../db.js';
|
import {
|
||||||
|
db,
|
||||||
|
ensureDbInitialized,
|
||||||
|
handleDbError,
|
||||||
|
invalidateCache,
|
||||||
|
withCache,
|
||||||
|
} from '../db.js';
|
||||||
import * as schema from '../schema.js';
|
import * as schema from '../schema.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -12,16 +18,22 @@ export async function getAllAchievements(): Promise<
|
||||||
> {
|
> {
|
||||||
try {
|
try {
|
||||||
await ensureDbInitialized();
|
await ensureDbInitialized();
|
||||||
|
|
||||||
if (!db) {
|
if (!db) {
|
||||||
console.error('Database not initialized, cannot get achievements');
|
console.error('Database not initialized, cannot get achievements');
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return await db
|
const achievementDefinitions = await withCache(
|
||||||
.select()
|
'achievementDefinitions',
|
||||||
.from(schema.achievementDefinitionsTable)
|
async () => {
|
||||||
.orderBy(schema.achievementDefinitionsTable.threshold);
|
return await db
|
||||||
|
.select()
|
||||||
|
.from(schema.achievementDefinitionsTable)
|
||||||
|
.orderBy(schema.achievementDefinitionsTable.threshold);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return achievementDefinitions;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return handleDbError('Failed to get all achievements', error as Error);
|
return handleDbError('Failed to get all achievements', error as Error);
|
||||||
}
|
}
|
||||||
|
@ -37,84 +49,33 @@ export async function getUserAchievements(
|
||||||
): Promise<schema.userAchievementsTableTypes[]> {
|
): Promise<schema.userAchievementsTableTypes[]> {
|
||||||
try {
|
try {
|
||||||
await ensureDbInitialized();
|
await ensureDbInitialized();
|
||||||
|
|
||||||
if (!db) {
|
if (!db) {
|
||||||
console.error('Database not initialized, cannot get user achievements');
|
console.error('Database not initialized, cannot get user achievements');
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return await db
|
const cachedUserAchievements = await withCache(
|
||||||
.select({
|
`userAchievements:${userId}`,
|
||||||
id: schema.userAchievementsTable.id,
|
async () => {
|
||||||
discordId: schema.userAchievementsTable.discordId,
|
return await db
|
||||||
achievementId: schema.userAchievementsTable.achievementId,
|
.select({
|
||||||
earnedAt: schema.userAchievementsTable.earnedAt,
|
id: schema.userAchievementsTable.id,
|
||||||
progress: schema.userAchievementsTable.progress,
|
discordId: schema.userAchievementsTable.discordId,
|
||||||
})
|
achievementId: schema.userAchievementsTable.achievementId,
|
||||||
.from(schema.userAchievementsTable)
|
earnedAt: schema.userAchievementsTable.earnedAt,
|
||||||
.where(eq(schema.userAchievementsTable.discordId, userId));
|
progress: schema.userAchievementsTable.progress,
|
||||||
|
})
|
||||||
|
.from(schema.userAchievementsTable)
|
||||||
|
.where(eq(schema.userAchievementsTable.discordId, userId));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return cachedUserAchievements;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return handleDbError('Failed to get user achievements', error as Error);
|
return handleDbError('Failed to get user achievements', error as Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Award an achievement to a user
|
|
||||||
* @param userId - Discord ID of the user
|
|
||||||
* @param achievementId - ID of the achievement
|
|
||||||
* @returns Boolean indicating success
|
|
||||||
*/
|
|
||||||
export async function awardAchievement(
|
|
||||||
userId: string,
|
|
||||||
achievementId: number,
|
|
||||||
): Promise<boolean> {
|
|
||||||
try {
|
|
||||||
await ensureDbInitialized();
|
|
||||||
|
|
||||||
if (!db) {
|
|
||||||
console.error('Database not initialized, cannot award achievement');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const existing = await db
|
|
||||||
.select()
|
|
||||||
.from(schema.userAchievementsTable)
|
|
||||||
.where(
|
|
||||||
and(
|
|
||||||
eq(schema.userAchievementsTable.discordId, userId),
|
|
||||||
eq(schema.userAchievementsTable.achievementId, achievementId),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.then((rows) => rows[0]);
|
|
||||||
|
|
||||||
if (existing) {
|
|
||||||
if (existing.earnedAt) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
await db
|
|
||||||
.update(schema.userAchievementsTable)
|
|
||||||
.set({
|
|
||||||
earnedAt: new Date(),
|
|
||||||
progress: 100,
|
|
||||||
})
|
|
||||||
.where(eq(schema.userAchievementsTable.id, existing.id));
|
|
||||||
} else {
|
|
||||||
await db.insert(schema.userAchievementsTable).values({
|
|
||||||
discordId: userId,
|
|
||||||
achievementId: achievementId,
|
|
||||||
earnedAt: new Date(),
|
|
||||||
progress: 100,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
handleDbError('Failed to award achievement', error as Error);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update achievement progress for a user
|
* Update achievement progress for a user
|
||||||
* @param userId - Discord ID of the user
|
* @param userId - Discord ID of the user
|
||||||
|
@ -150,16 +111,19 @@ export async function updateAchievementProgress(
|
||||||
if (existing) {
|
if (existing) {
|
||||||
await db
|
await db
|
||||||
.update(schema.userAchievementsTable)
|
.update(schema.userAchievementsTable)
|
||||||
.set({ progress })
|
.set({ progress, earnedAt: progress === 100 ? new Date() : null })
|
||||||
.where(eq(schema.userAchievementsTable.id, existing.id));
|
.where(eq(schema.userAchievementsTable.id, existing.id));
|
||||||
} else {
|
} else {
|
||||||
await db.insert(schema.userAchievementsTable).values({
|
await db.insert(schema.userAchievementsTable).values({
|
||||||
discordId: userId,
|
discordId: userId,
|
||||||
achievementId,
|
achievementId,
|
||||||
progress,
|
progress,
|
||||||
|
earnedAt: progress === 100 ? new Date() : null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await invalidateCache(`userAchievements:${userId}`);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleDbError('Failed to update achievement progress', error as Error);
|
handleDbError('Failed to update achievement progress', error as Error);
|
||||||
|
@ -184,7 +148,6 @@ export async function createAchievement(achievementData: {
|
||||||
}): Promise<schema.achievementDefinitionsTableTypes | undefined> {
|
}): Promise<schema.achievementDefinitionsTableTypes | undefined> {
|
||||||
try {
|
try {
|
||||||
await ensureDbInitialized();
|
await ensureDbInitialized();
|
||||||
|
|
||||||
if (!db) {
|
if (!db) {
|
||||||
console.error('Database not initialized, cannot create achievement');
|
console.error('Database not initialized, cannot create achievement');
|
||||||
return undefined;
|
return undefined;
|
||||||
|
@ -204,6 +167,8 @@ export async function createAchievement(achievementData: {
|
||||||
})
|
})
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
|
await invalidateCache('achievementDefinitions');
|
||||||
|
|
||||||
return achievement;
|
return achievement;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return handleDbError('Failed to create achievement', error as Error);
|
return handleDbError('Failed to create achievement', error as Error);
|
||||||
|
@ -220,7 +185,6 @@ export async function deleteAchievement(
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
await ensureDbInitialized();
|
await ensureDbInitialized();
|
||||||
|
|
||||||
if (!db) {
|
if (!db) {
|
||||||
console.error('Database not initialized, cannot delete achievement');
|
console.error('Database not initialized, cannot delete achievement');
|
||||||
return false;
|
return false;
|
||||||
|
@ -234,6 +198,8 @@ export async function deleteAchievement(
|
||||||
.delete(schema.achievementDefinitionsTable)
|
.delete(schema.achievementDefinitionsTable)
|
||||||
.where(eq(schema.achievementDefinitionsTable.id, achievementId));
|
.where(eq(schema.achievementDefinitionsTable.id, achievementId));
|
||||||
|
|
||||||
|
await invalidateCache('achievementDefinitions');
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleDbError('Failed to delete achievement', error as Error);
|
handleDbError('Failed to delete achievement', error as Error);
|
||||||
|
@ -253,7 +219,6 @@ export async function removeUserAchievement(
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
await ensureDbInitialized();
|
await ensureDbInitialized();
|
||||||
|
|
||||||
if (!db) {
|
if (!db) {
|
||||||
console.error('Database not initialized, cannot remove user achievement');
|
console.error('Database not initialized, cannot remove user achievement');
|
||||||
return false;
|
return false;
|
||||||
|
@ -267,6 +232,9 @@ export async function removeUserAchievement(
|
||||||
eq(schema.userAchievementsTable.achievementId, achievementId),
|
eq(schema.userAchievementsTable.achievementId, achievementId),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await invalidateCache(`userAchievements:${discordId}`);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleDbError('Failed to remove user achievement', error as Error);
|
handleDbError('Failed to remove user achievement', error as Error);
|
||||||
|
|
|
@ -2,7 +2,6 @@ import { Message, EmbedBuilder, TextChannel, Guild } from 'discord.js';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
addXpToUser,
|
addXpToUser,
|
||||||
awardAchievement,
|
|
||||||
getAllAchievements,
|
getAllAchievements,
|
||||||
getUserAchievements,
|
getUserAchievements,
|
||||||
getUserLevel,
|
getUserLevel,
|
||||||
|
@ -34,11 +33,14 @@ async function handleProgress(
|
||||||
(a) => a.achievementId === achievement.id && a.earnedAt !== null,
|
(a) => a.achievementId === achievement.id && a.earnedAt !== null,
|
||||||
);
|
);
|
||||||
|
|
||||||
await updateAchievementProgress(userId, achievement.id, progress);
|
const updated = await updateAchievementProgress(
|
||||||
|
userId,
|
||||||
|
achievement.id,
|
||||||
|
progress,
|
||||||
|
);
|
||||||
|
|
||||||
if (progress === 100 && !existing && !skipAward) {
|
if (progress === 100 && !existing && !skipAward) {
|
||||||
const awarded = await awardAchievement(userId, achievement.id);
|
if (updated && guild) {
|
||||||
if (awarded && guild) {
|
|
||||||
await announceAchievement(guild, userId, achievement);
|
await announceAchievement(guild, userId, achievement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue