Files
AuroraBot-discord/src/modules/system/scheduler.ts

87 lines
3.4 KiB
TypeScript

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);
}
}
};