120 lines
4.0 KiB
TypeScript
120 lines
4.0 KiB
TypeScript
import { lootdrops, userTimers, userQuests } from "@/db/schema";
|
|
import { eq, and, lt, isNull, sql } from "drizzle-orm";
|
|
import { DrizzleClient } from "@/lib/DrizzleClient";
|
|
import { AuroraClient } from "@/lib/BotClient";
|
|
import { env } from "@/lib/env";
|
|
import { config } from "@/lib/config";
|
|
import { TimerType } from "@/lib/constants";
|
|
|
|
export const cleanupService = {
|
|
/**
|
|
* Runs all cleanup tasks
|
|
*/
|
|
runAll: async () => {
|
|
console.log("🧹 Starting system cleanup...");
|
|
const stats = {
|
|
lootdrops: 0,
|
|
timers: 0,
|
|
quests: 0
|
|
};
|
|
|
|
try {
|
|
stats.lootdrops = await cleanupService.cleanupLootdrops();
|
|
stats.timers = await cleanupService.cleanupTimers();
|
|
stats.quests = await cleanupService.cleanupQuests();
|
|
|
|
console.log(`✅ Cleanup finished. Stats:
|
|
- Lootdrops: ${stats.lootdrops} removed
|
|
- Timers: ${stats.timers} removed
|
|
- Quests: ${stats.quests} cleaned`);
|
|
} catch (error) {
|
|
console.error("❌ Error during cleanup:", error);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Deletes unclaimed expired lootdrops
|
|
*/
|
|
cleanupLootdrops: async (): Promise<number> => {
|
|
const now = new Date();
|
|
const result = await DrizzleClient.delete(lootdrops)
|
|
.where(and(
|
|
lt(lootdrops.expiresAt, now),
|
|
isNull(lootdrops.claimedBy)
|
|
))
|
|
.returning({ id: lootdrops.messageId });
|
|
|
|
return result.length;
|
|
},
|
|
|
|
/**
|
|
* Cleans up expired user timers and handles side effects (like role removal)
|
|
*/
|
|
cleanupTimers: async (): Promise<number> => {
|
|
const now = new Date();
|
|
let deletedCount = 0;
|
|
|
|
// 1. Specific handling for ACCESS timers (revoking roles etc)
|
|
// This is migrated from scheduler.ts
|
|
const expiredAccess = await DrizzleClient.query.userTimers.findMany({
|
|
where: and(
|
|
eq(userTimers.type, TimerType.ACCESS),
|
|
lt(userTimers.expiresAt, now)
|
|
)
|
|
});
|
|
|
|
for (const timer of expiredAccess) {
|
|
const meta = timer.metadata as any;
|
|
const userIdStr = timer.userId.toString();
|
|
|
|
if (timer.key.startsWith('role_')) {
|
|
try {
|
|
const roleId = meta?.roleId || timer.key.replace('role_', '');
|
|
const guildId = env.DISCORD_GUILD_ID;
|
|
|
|
if (guildId) {
|
|
const guild = await AuroraClient.guilds.fetch(guildId);
|
|
const member = await guild.members.fetch(userIdStr);
|
|
await member.roles.remove(roleId);
|
|
console.log(`👋 Removed temporary role ${roleId} from ${member.user.tag}`);
|
|
}
|
|
} catch (err) {
|
|
console.error(`Failed to remove role for user ${userIdStr}:`, err);
|
|
}
|
|
}
|
|
|
|
// Delete specifically this one
|
|
await DrizzleClient.delete(userTimers)
|
|
.where(and(
|
|
eq(userTimers.userId, timer.userId),
|
|
eq(userTimers.type, timer.type),
|
|
eq(userTimers.key, timer.key)
|
|
));
|
|
deletedCount++;
|
|
}
|
|
|
|
// 2. Bulk delete all other expired timers (COOLDOWN, EFFECT, etc)
|
|
const result = await DrizzleClient.delete(userTimers)
|
|
.where(lt(userTimers.expiresAt, now))
|
|
.returning({ userId: userTimers.userId });
|
|
|
|
deletedCount += result.length;
|
|
return deletedCount;
|
|
},
|
|
|
|
/**
|
|
* Deletes or archives old completed quests
|
|
*/
|
|
cleanupQuests: async (): Promise<number> => {
|
|
const archiveDays = config.system.cleanup.questArchiveDays;
|
|
const threshold = new Date();
|
|
threshold.setDate(threshold.getDate() - archiveDays);
|
|
|
|
const result = await DrizzleClient.delete(userQuests)
|
|
.where(lt(userQuests.completedAt, threshold))
|
|
.returning({ userId: userQuests.userId });
|
|
|
|
return result.length;
|
|
}
|
|
};
|