diff --git a/.gitignore b/.gitignore index 77f162f..abd374e 100755 --- a/.gitignore +++ b/.gitignore @@ -54,8 +54,8 @@ data/ start.bat tokens.json -config.*json -!config.example.json +seed.json +!seed.example.json .env *.todo \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index e60db88..a80119a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,8 +8,8 @@ services: container_name: discord-bot restart: unless-stopped volumes: - # Configuration files (read-only) - - ./config.json:/app/config.json:ro + # Seed file for initial database population (read-only) + - ./seed.json:/app/seed.json:ro - ./conf:/app/conf:ro # Sound effects directory (read-only) diff --git a/package.json b/package.json index 2e85985..b0f254e 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "restart": "docker compose restart", "logs": "docker compose logs -f", "boom": "pnpm stop && pnpm build && pnpm start", - "reset-db": "pnpm stop && rm -f data/ghbot.db data/ghbot.db-shm data/ghbot.db-wal && echo 'Database reset complete. Run pnpm start to re-seed from config.json'", + "reset-db": "pnpm stop && rm -f data/*.db* && echo 'Database reset complete. Run pnpm start to re-seed from seed.json'", "image:build": "docker build -t ghbot:${VERSION:-latest} .", "image:run": "docker run -d --name ghbot --restart always ghbot:${VERSION:-latest}" }, diff --git a/config.example.json b/seed.example.json similarity index 100% rename from config.example.json rename to seed.example.json diff --git a/src/config/config.js b/src/config/config.js index 02b1813..3767c08 100644 --- a/src/config/config.js +++ b/src/config/config.js @@ -1,41 +1,9 @@ -const fs = require("fs"); -const path = require("path"); - -// Dynamic config that combines file-based config with database +// Database-first configuration manager class ConfigManager { constructor() { - this.fileConfig = this.loadFileConfig(); this.databaseService = null; // Will be injected } - /** - * Load static configuration from file - */ - loadFileConfig() { - const configPath = path.join(__dirname, "..", "..", "config.json"); - - if (!fs.existsSync(configPath)) { - console.warn("config.json not found, using environment variables only"); - return { - discord: { - token: process.env.DISCORD_TOKEN, - adminUserId: process.env.ADMIN_USER_ID, - }, - }; - } - - const config = JSON.parse(fs.readFileSync(configPath, "utf-8")); - - // Validate required fields - if (!config.discord?.token && !process.env.DISCORD_TOKEN) { - throw new Error( - "Discord token is required in config.json or DISCORD_TOKEN environment variable" - ); - } - - return config; - } - /** * Inject database service (to avoid circular dependency) */ @@ -44,89 +12,68 @@ class ConfigManager { } /** - * Get bot configuration (combines file and database) + * Get bot configuration from database (with environment variable fallbacks) */ getBotConfig() { - const fileConfig = this.fileConfig; - const dbConfig = this.databaseService - ? this.databaseService.getBotConfiguration() - : {}; + if (!this.databaseService) { + // Fallback to environment variables if database not available + return { + botName: "GHBot", + debug: false, + discord: { + token: process.env.DISCORD_TOKEN, + adminUserId: process.env.ADMIN_USER_ID, + activities: ["Playing sounds", "Serving facts"], + blacklistedUsers: [], + }, + }; + } + + const dbConfig = this.databaseService.getBotConfiguration(); return { - // Use file config as fallback, database as primary - botName: dbConfig.botName || fileConfig.botName || "GHBot", - debug: - dbConfig.debug !== undefined - ? dbConfig.debug - : fileConfig.debug || false, + botName: dbConfig.botName || "GHBot", + debug: dbConfig.debug || false, discord: { - token: fileConfig.discord?.token || process.env.DISCORD_TOKEN, - adminUserId: - dbConfig.adminUserId || - fileConfig.discord?.adminUserId || - process.env.ADMIN_USER_ID, - activities: dbConfig.activities || - fileConfig.discord?.activities || ["Playing sounds", "Serving facts"], - blacklistedUsers: - dbConfig.blacklistedUsers || - fileConfig.discord?.blacklistedUsers || - [], + token: dbConfig.token || process.env.DISCORD_TOKEN, + adminUserId: dbConfig.adminUserId || process.env.ADMIN_USER_ID, + activities: dbConfig.activities || ["Playing sounds", "Serving facts"], + blacklistedUsers: dbConfig.blacklistedUsers || [], }, }; } /** - * Get guild configuration (from database primarily, file as fallback) + * Get guild configuration (database only) */ getGuildConfig(guildId) { - if (this.databaseService) { - const dbConfig = this.databaseService.getGuildConfig(guildId); - if (dbConfig) { - return dbConfig; - } + if (!this.databaseService) { + // Return minimal default config if database not available + return { + id: guildId, + name: "Unknown Guild", + internalName: "Unknown Guild", + prefix: "!", + enableSfx: true, + sfxVolume: 0.5, + enableFunFacts: true, + enableHamFacts: true, + allowedRolesForRequest: [], + }; } - // Fallback to file config for backward compatibility - if (this.fileConfig.discord?.guilds) { - const guilds = Array.isArray(this.fileConfig.discord.guilds) - ? this.fileConfig.discord.guilds - : Object.values(this.fileConfig.discord.guilds); - - return guilds.find((g) => g.id === guildId); - } - - // Return default config for new guilds - return { - id: guildId, - name: "Unknown Guild", - internalName: "Unknown Guild", - prefix: "!", - enableSfx: true, - sfxVolume: 0.5, - enableFunFacts: true, - enableHamFacts: true, - allowedRolesForRequest: null, - }; + return this.databaseService.getGuildConfig(guildId); } /** - * Get all guild configurations + * Get all guild configurations (database only) */ getAllGuildConfigs() { - if (this.databaseService) { - return this.databaseService.getAllGuildConfigs(); + if (!this.databaseService) { + return []; } - // Fallback to file config - if (this.fileConfig.discord?.guilds) { - const guilds = Array.isArray(this.fileConfig.discord.guilds) - ? this.fileConfig.discord.guilds - : Object.values(this.fileConfig.discord.guilds); - - return guilds; - } - - return []; + return this.databaseService.getAllGuildConfigs(); } } diff --git a/src/services/databaseService.js b/src/services/databaseService.js index a2da4a4..c647cac 100644 --- a/src/services/databaseService.js +++ b/src/services/databaseService.js @@ -67,6 +67,7 @@ class DatabaseService { INSERT OR IGNORE INTO bot_config (key, value) VALUES ('bot_name', 'GHBot'), ('debug', 'false'), + ('token', ''), ('admin_user_id', ''), ('activities', '["Chardee MacDennis", "The Nightman Cometh", "Charlie Work"]'), ('blacklisted_users', '[]') @@ -106,17 +107,17 @@ class DatabaseService { const fs = require("fs"); const path = require("path"); - const configPath = path.join(__dirname, "..", "..", "config.json"); + const seedPath = path.join(__dirname, "..", "..", "seed.json"); - if (!fs.existsSync(configPath)) { - console.log("No config.json file found, skipping seed"); + if (!fs.existsSync(seedPath)) { + console.log("No seed.json file found, skipping seed"); return; } - const config = JSON.parse(fs.readFileSync(configPath, "utf-8")); + const config = JSON.parse(fs.readFileSync(seedPath, "utf-8")); if (!config.discord?.guilds) { - console.log("No guilds found in config.json, skipping seed"); + console.log("No guilds found in seed.json, skipping seed"); return; } @@ -180,7 +181,7 @@ class DatabaseService { } console.log( - `✅ Successfully seeded database with ${seededCount} guild(s) from config.json` + `✅ Successfully seeded database with ${seededCount} guild(s) from seed.json` ); // Update bot configuration in database from file config @@ -190,6 +191,9 @@ class DatabaseService { if (config.debug !== undefined) { this.setBotConfig("debug", config.debug.toString()); } + if (config.discord?.token) { + this.setBotConfig("token", config.discord.token); + } if (config.discord?.adminUserId) { this.setBotConfig("admin_user_id", config.discord.adminUserId); } @@ -212,7 +216,7 @@ class DatabaseService { ); } - console.log("✅ Bot configuration updated from config.json"); + console.log("✅ Bot configuration updated from seed.json"); } catch (error) { console.error("Error seeding database from config file:", error); } @@ -510,6 +514,7 @@ class DatabaseService { getBotConfiguration() { const botName = this.getBotConfig("bot_name") || "GHBot"; const debug = this.getBotConfig("debug") === "true"; + const token = this.getBotConfig("token") || ""; const adminUserId = this.getBotConfig("admin_user_id") || ""; const activities = JSON.parse(this.getBotConfig("activities") || "[]"); const blacklistedUsers = JSON.parse( @@ -519,6 +524,7 @@ class DatabaseService { return { botName, debug, + token, adminUserId, activities, blacklistedUsers,