forked from syntaxbullet/AuroraBot-discord
added react app
This commit is contained in:
@@ -4,7 +4,6 @@ import type { Command } from "@lib/types";
|
||||
import { env } from "@lib/env";
|
||||
import { CommandLoader } from "@lib/loaders/CommandLoader";
|
||||
import { EventLoader } from "@lib/loaders/EventLoader";
|
||||
import { logger } from "@lib/logger";
|
||||
|
||||
export class Client extends DiscordClient {
|
||||
|
||||
@@ -23,25 +22,25 @@ export class Client extends DiscordClient {
|
||||
async loadCommands(reload: boolean = false) {
|
||||
if (reload) {
|
||||
this.commands.clear();
|
||||
logger.info("♻️ Reloading commands...");
|
||||
console.log("♻️ Reloading commands...");
|
||||
}
|
||||
|
||||
const commandsPath = join(import.meta.dir, '../commands');
|
||||
const result = await this.commandLoader.loadFromDirectory(commandsPath, reload);
|
||||
|
||||
logger.info(`📦 Command loading complete: ${result.loaded} loaded, ${result.skipped} skipped, ${result.errors.length} errors`);
|
||||
console.log(`📦 Command loading complete: ${result.loaded} loaded, ${result.skipped} skipped, ${result.errors.length} errors`);
|
||||
}
|
||||
|
||||
async loadEvents(reload: boolean = false) {
|
||||
if (reload) {
|
||||
this.removeAllListeners();
|
||||
logger.info("♻️ Reloading events...");
|
||||
console.log("♻️ Reloading events...");
|
||||
}
|
||||
|
||||
const eventsPath = join(import.meta.dir, '../events');
|
||||
const result = await this.eventLoader.loadFromDirectory(eventsPath, reload);
|
||||
|
||||
logger.info(`📦 Event loading complete: ${result.loaded} loaded, ${result.skipped} skipped, ${result.errors.length} errors`);
|
||||
console.log(`📦 Event loading complete: ${result.loaded} loaded, ${result.skipped} skipped, ${result.errors.length} errors`);
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +49,7 @@ export class Client extends DiscordClient {
|
||||
// We use env.DISCORD_BOT_TOKEN directly so this can run without client.login()
|
||||
const token = env.DISCORD_BOT_TOKEN;
|
||||
if (!token) {
|
||||
logger.error("DISCORD_BOT_TOKEN is not set.");
|
||||
console.error("DISCORD_BOT_TOKEN is not set.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -60,16 +59,16 @@ export class Client extends DiscordClient {
|
||||
const clientId = env.DISCORD_CLIENT_ID;
|
||||
|
||||
if (!clientId) {
|
||||
logger.error("DISCORD_CLIENT_ID is not set.");
|
||||
console.error("DISCORD_CLIENT_ID is not set.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
logger.info(`Started refreshing ${commandsData.length} application (/) commands.`);
|
||||
console.log(`Started refreshing ${commandsData.length} application (/) commands.`);
|
||||
|
||||
let data;
|
||||
if (guildId) {
|
||||
logger.info(`Registering commands to guild: ${guildId}`);
|
||||
console.log(`Registering commands to guild: ${guildId}`);
|
||||
data = await rest.put(
|
||||
Routes.applicationGuildCommands(clientId, guildId),
|
||||
{ body: commandsData },
|
||||
@@ -77,20 +76,20 @@ export class Client extends DiscordClient {
|
||||
// Clear global commands to avoid duplicates
|
||||
await rest.put(Routes.applicationCommands(clientId), { body: [] });
|
||||
} else {
|
||||
logger.info('Registering commands globally');
|
||||
console.log('Registering commands globally');
|
||||
data = await rest.put(
|
||||
Routes.applicationCommands(clientId),
|
||||
{ body: commandsData },
|
||||
);
|
||||
}
|
||||
|
||||
logger.success(`Successfully reloaded ${(data as any).length} application (/) commands.`);
|
||||
console.log(`Successfully reloaded ${(data as any).length} application (/) commands.`);
|
||||
} catch (error: any) {
|
||||
if (error.code === 50001) {
|
||||
logger.warn("Missing Access: The bot is not in the guild or lacks 'applications.commands' scope.");
|
||||
logger.warn(" If you are testing locally, make sure you invited the bot with scope 'bot applications.commands'.");
|
||||
console.warn("Missing Access: The bot is not in the guild or lacks 'applications.commands' scope.");
|
||||
console.warn(" If you are testing locally, make sure you invited the bot with scope 'bot applications.commands'.");
|
||||
} else {
|
||||
logger.error(error);
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -99,22 +98,22 @@ export class Client extends DiscordClient {
|
||||
const { setShuttingDown, waitForTransactions } = await import("./shutdown");
|
||||
const { closeDatabase } = await import("./DrizzleClient");
|
||||
|
||||
logger.info("🛑 Shutdown signal received. Starting graceful shutdown...");
|
||||
console.log("🛑 Shutdown signal received. Starting graceful shutdown...");
|
||||
setShuttingDown(true);
|
||||
|
||||
// Wait for transactions to complete
|
||||
logger.info("⏳ Waiting for active transactions to complete...");
|
||||
console.log("⏳ Waiting for active transactions to complete...");
|
||||
await waitForTransactions(10000);
|
||||
|
||||
// Destroy Discord client
|
||||
logger.info("🔌 Disconnecting from Discord...");
|
||||
console.log("🔌 Disconnecting from Discord...");
|
||||
this.destroy();
|
||||
|
||||
// Close database
|
||||
logger.info("🗄️ Closing database connection...");
|
||||
console.log("🗄️ Closing database connection...");
|
||||
await closeDatabase();
|
||||
|
||||
logger.success("👋 Graceful shutdown complete. Exiting.");
|
||||
console.log("👋 Graceful shutdown complete. Exiting.");
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { AutocompleteInteraction } from "discord.js";
|
||||
import { AuroraClient } from "@/lib/BotClient";
|
||||
import { logger } from "@lib/logger";
|
||||
|
||||
|
||||
/**
|
||||
* Handles autocomplete interactions for slash commands
|
||||
@@ -16,7 +16,7 @@ export class AutocompleteHandler {
|
||||
try {
|
||||
await command.autocomplete(interaction);
|
||||
} catch (error) {
|
||||
logger.error(`Error handling autocomplete for ${interaction.commandName}:`, error);
|
||||
console.error(`Error handling autocomplete for ${interaction.commandName}:`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { ChatInputCommandInteraction, MessageFlags } from "discord.js";
|
||||
import { AuroraClient } from "@/lib/BotClient";
|
||||
import { userService } from "@/modules/user/user.service";
|
||||
import { createErrorEmbed } from "@lib/embeds";
|
||||
import { logger } from "@lib/logger";
|
||||
|
||||
|
||||
/**
|
||||
* Handles slash command execution
|
||||
@@ -13,7 +13,7 @@ export class CommandHandler {
|
||||
const command = AuroraClient.commands.get(interaction.commandName);
|
||||
|
||||
if (!command) {
|
||||
logger.error(`No command matching ${interaction.commandName} was found.`);
|
||||
console.error(`No command matching ${interaction.commandName} was found.`);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -21,14 +21,14 @@ export class CommandHandler {
|
||||
try {
|
||||
await userService.getOrCreateUser(interaction.user.id, interaction.user.username);
|
||||
} catch (error) {
|
||||
logger.error("Failed to ensure user exists:", error);
|
||||
console.error("Failed to ensure user exists:", error);
|
||||
}
|
||||
|
||||
try {
|
||||
await command.execute(interaction);
|
||||
AuroraClient.lastCommandTimestamp = Date.now();
|
||||
} catch (error) {
|
||||
logger.error(String(error));
|
||||
console.error(String(error));
|
||||
const errorEmbed = createErrorEmbed('There was an error while executing this command!');
|
||||
|
||||
if (interaction.replied || interaction.deferred) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ButtonInteraction, StringSelectMenuInteraction, ModalSubmitInteraction, MessageFlags } from "discord.js";
|
||||
import { logger } from "@lib/logger";
|
||||
|
||||
import { UserError } from "@lib/errors";
|
||||
import { createErrorEmbed } from "@lib/embeds";
|
||||
|
||||
@@ -28,7 +28,7 @@ export class ComponentInteractionHandler {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
logger.error(`Handler method ${route.method} not found in module`);
|
||||
console.error(`Handler method ${route.method} not found in module`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -52,7 +52,7 @@ export class ComponentInteractionHandler {
|
||||
|
||||
// Log system errors (non-user errors) for debugging
|
||||
if (!isUserError) {
|
||||
logger.error(`Error in ${handlerName}:`, error);
|
||||
console.error(`Error in ${handlerName}:`, error);
|
||||
}
|
||||
|
||||
const errorEmbed = createErrorEmbed(errorMessage);
|
||||
@@ -72,7 +72,7 @@ export class ComponentInteractionHandler {
|
||||
}
|
||||
} catch (replyError) {
|
||||
// If we can't send a reply, log it
|
||||
logger.error(`Failed to send error response in ${handlerName}:`, replyError);
|
||||
console.error(`Failed to send error response in ${handlerName}:`, replyError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { Command } from "@lib/types";
|
||||
import { config } from "@lib/config";
|
||||
import type { LoadResult, LoadError } from "./types";
|
||||
import type { Client } from "../BotClient";
|
||||
import { logger } from "@lib/logger";
|
||||
|
||||
|
||||
/**
|
||||
* Handles loading commands from the file system
|
||||
@@ -45,7 +45,7 @@ export class CommandLoader {
|
||||
await this.loadCommandFile(filePath, reload, result);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Error reading directory ${dir}:`, error);
|
||||
console.error(`Error reading directory ${dir}:`, error);
|
||||
result.errors.push({ file: dir, error });
|
||||
}
|
||||
}
|
||||
@@ -60,7 +60,7 @@ export class CommandLoader {
|
||||
const commands = Object.values(commandModule);
|
||||
|
||||
if (commands.length === 0) {
|
||||
logger.warn(`No commands found in ${filePath}`);
|
||||
console.warn(`No commands found in ${filePath}`);
|
||||
result.skipped++;
|
||||
return;
|
||||
}
|
||||
@@ -74,21 +74,21 @@ export class CommandLoader {
|
||||
const isEnabled = config.commands[command.data.name] !== false;
|
||||
|
||||
if (!isEnabled) {
|
||||
logger.info(`🚫 Skipping disabled command: ${command.data.name}`);
|
||||
console.log(`🚫 Skipping disabled command: ${command.data.name}`);
|
||||
result.skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
this.client.commands.set(command.data.name, command);
|
||||
logger.success(`Loaded command: ${command.data.name}`);
|
||||
console.log(`Loaded command: ${command.data.name}`);
|
||||
result.loaded++;
|
||||
} else {
|
||||
logger.warn(`Skipping invalid command in ${filePath}`);
|
||||
console.warn(`Skipping invalid command in ${filePath}`);
|
||||
result.skipped++;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Failed to load command from ${filePath}:`, error);
|
||||
console.error(`Failed to load command from ${filePath}:`, error);
|
||||
result.errors.push({ file: filePath, error });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { join } from "node:path";
|
||||
import type { Event } from "@lib/types";
|
||||
import type { LoadResult } from "./types";
|
||||
import type { Client } from "../BotClient";
|
||||
import { logger } from "@lib/logger";
|
||||
|
||||
|
||||
/**
|
||||
* Handles loading events from the file system
|
||||
@@ -44,7 +44,7 @@ export class EventLoader {
|
||||
await this.loadEventFile(filePath, reload, result);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Error reading directory ${dir}:`, error);
|
||||
console.error(`Error reading directory ${dir}:`, error);
|
||||
result.errors.push({ file: dir, error });
|
||||
}
|
||||
}
|
||||
@@ -64,14 +64,14 @@ export class EventLoader {
|
||||
} else {
|
||||
this.client.on(event.name, (...args) => event.execute(...args));
|
||||
}
|
||||
logger.success(`Loaded event: ${event.name}`);
|
||||
console.log(`Loaded event: ${event.name}`);
|
||||
result.loaded++;
|
||||
} else {
|
||||
logger.warn(`Skipping invalid event in ${filePath}`);
|
||||
console.warn(`Skipping invalid event in ${filePath}`);
|
||||
result.skipped++;
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Failed to load event from ${filePath}:`, error);
|
||||
console.error(`Failed to load event from ${filePath}:`, error);
|
||||
result.errors.push({ file: filePath, error });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
|
||||
import { describe, it, expect, beforeEach } from "bun:test";
|
||||
import { logger, getRecentLogs } from "./logger";
|
||||
|
||||
describe("Logger Buffer", () => {
|
||||
// Note: Since the buffer is a module-level variable, it persists across tests.
|
||||
// In a real scenario we might want a reset function, but for now we'll just check relative additions.
|
||||
|
||||
it("should add logs to the buffer", () => {
|
||||
const initialLength = getRecentLogs().length;
|
||||
logger.info("Test Info Log");
|
||||
const newLogs = getRecentLogs();
|
||||
|
||||
expect(newLogs.length).toBe(initialLength + 1);
|
||||
expect(newLogs[0]?.message).toBe("Test Info Log");
|
||||
expect(newLogs[0]?.type).toBe("info");
|
||||
});
|
||||
|
||||
it("should cap the buffer size at 50", () => {
|
||||
// Fill the buffer
|
||||
for (let i = 0; i < 60; i++) {
|
||||
logger.debug(`Log overflow test ${i}`);
|
||||
}
|
||||
|
||||
const logs = getRecentLogs();
|
||||
expect(logs.length).toBeLessThanOrEqual(50);
|
||||
expect(logs[0]?.message).toBe("Log overflow test 59");
|
||||
});
|
||||
|
||||
it("should handle different log levels", () => {
|
||||
logger.error("Critical Error");
|
||||
logger.success("Operation Successful");
|
||||
|
||||
const logs = getRecentLogs();
|
||||
expect(logs[0]?.type).toBe("success");
|
||||
expect(logs[1]?.type).toBe("error");
|
||||
});
|
||||
});
|
||||
@@ -1,67 +0,0 @@
|
||||
import { WebServer } from "@/web/server";
|
||||
|
||||
/**
|
||||
* Centralized logging utility with consistent formatting
|
||||
*/
|
||||
|
||||
const LOG_BUFFER_SIZE = 50;
|
||||
const logBuffer: Array<{ time: string; type: string; message: string }> = [];
|
||||
|
||||
function addToBuffer(type: string, message: string) {
|
||||
const time = new Date().toLocaleTimeString();
|
||||
logBuffer.unshift({ time, type, message });
|
||||
if (logBuffer.length > LOG_BUFFER_SIZE) {
|
||||
logBuffer.pop();
|
||||
}
|
||||
}
|
||||
|
||||
export function getRecentLogs() {
|
||||
return logBuffer;
|
||||
}
|
||||
|
||||
export const logger = {
|
||||
/**
|
||||
* General information message
|
||||
*/
|
||||
info: (message: string, ...args: any[]) => {
|
||||
console.log(`ℹ️ ${message}`, ...args);
|
||||
addToBuffer("info", message);
|
||||
try { WebServer.broadcastLog("info", message); } catch { }
|
||||
},
|
||||
|
||||
/**
|
||||
* Success message
|
||||
*/
|
||||
success: (message: string, ...args: any[]) => {
|
||||
console.log(`✅ ${message}`, ...args);
|
||||
addToBuffer("success", message);
|
||||
try { WebServer.broadcastLog("success", message); } catch { }
|
||||
},
|
||||
|
||||
/**
|
||||
* Warning message
|
||||
*/
|
||||
warn: (message: string, ...args: any[]) => {
|
||||
console.warn(`⚠️ ${message}`, ...args);
|
||||
addToBuffer("warning", message);
|
||||
try { WebServer.broadcastLog("warning", message); } catch { }
|
||||
},
|
||||
|
||||
/**
|
||||
* Error message
|
||||
*/
|
||||
error: (message: string, ...args: any[]) => {
|
||||
console.error(`❌ ${message}`, ...args);
|
||||
addToBuffer("error", message);
|
||||
try { WebServer.broadcastLog("error", message); } catch { }
|
||||
},
|
||||
|
||||
/**
|
||||
* Debug message
|
||||
*/
|
||||
debug: (message: string, ...args: any[]) => {
|
||||
console.log(`🔍 ${message}`, ...args);
|
||||
addToBuffer("debug", message);
|
||||
try { WebServer.broadcastLog("debug", message); } catch { }
|
||||
},
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
import { logger } from "@lib/logger";
|
||||
|
||||
|
||||
let shuttingDown = false;
|
||||
let activeTransactions = 0;
|
||||
@@ -22,7 +22,7 @@ export const waitForTransactions = async (timeoutMs: number = 10000) => {
|
||||
const start = Date.now();
|
||||
while (activeTransactions > 0) {
|
||||
if (Date.now() - start > timeoutMs) {
|
||||
logger.warn(`Shutdown timed out waiting for ${activeTransactions} transactions after ${timeoutMs}ms`);
|
||||
console.warn(`Shutdown timed out waiting for ${activeTransactions} transactions after ${timeoutMs}ms`);
|
||||
break;
|
||||
}
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
|
||||
Reference in New Issue
Block a user