import { userTimers } from "@/db/schema"; import { eq, and, lt } from "drizzle-orm"; import { DrizzleClient } from "@/lib/DrizzleClient"; import { AuroraClient } from "@/lib/BotClient"; import { env } from "@/lib/env"; /** * The Janitor responsible for cleaning up expired ACCESS timers * and revoking privileges. */ export const schedulerService = { start: () => { console.log("๐Ÿ•’ Scheduler started: Janitor loop running every 60s"); // Run immediately on start schedulerService.runJanitor(); // Loop every 60 seconds setInterval(() => { schedulerService.runJanitor(); }, 60 * 1000); // Terminal Update Loop (every 60s) const { terminalService } = require("@/modules/terminal/terminal.service"); setInterval(() => { terminalService.update(); }, 60 * 1000); }, runJanitor: async () => { try { // Find all expired ACCESS timers // We do this in a transaction to ensure we read and delete atomically if possible, // though for this specific case, fetching then deleting is fine as long as we handle race conditions gracefully. const now = new Date(); const expiredAccess = await DrizzleClient.query.userTimers.findMany({ where: and( eq(userTimers.type, 'ACCESS'), lt(userTimers.expiresAt, now) ) }); if (expiredAccess.length === 0) return; console.log(`๐Ÿงน Janitor: Found ${expiredAccess.length} expired access timers.`); for (const timer of expiredAccess) { const meta = timer.metadata as any; const userIdStr = timer.userId.toString(); // Specific Handling for Roles if (timer.key.startsWith('role_')) { try { const roleId = meta?.roleId || timer.key.replace('role_', ''); const guildId = env.DISCORD_GUILD_ID; if (guildId) { // We try to fetch, if bot is not in guild or lacks perms, it will catch 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); // We still delete the timer so we don't loop forever on a left user } } else { console.log(`๐Ÿšซ Revoking access for User ${timer.userId}: Key=${timer.key} (Channel: ${meta?.channelId || 'N/A'})`); // TODO: Generic channel permission removal if needed } // Delete the timer row await DrizzleClient.delete(userTimers) .where(and( eq(userTimers.userId, timer.userId), eq(userTimers.type, timer.type), eq(userTimers.key, timer.key) )); } } catch (error) { console.error("Janitor Error:", error); } } };