Files
ghbot/src/config/config.js
Chris Ham 61a376cfbb Modernize role management system with slash commands and role IDs
- Convert role management from prefix to slash commands (/role add/remove/list)
- Update database schema to store role IDs as JSON arrays instead of regex patterns
- Add /config roles command for administrators to manage allowed roles
- Simplify database schema by reusing allowed_roles_for_request field as JSON
- Add database reset script (pnpm reset-db) for easy testing and migration
- Update config format to only support array format (no backward compatibility)

Role Management Features:
- /role add <role> - Self-assign roles with dropdown selection
- /role remove <role> - Remove roles with dropdown selection
- /role list - Show available self-assignable roles
- /config roles add/remove/list/clear - Administrator role management

Technical Improvements:
- Role ID based matching (more reliable than name-based regex)
- Type-safe role selection with Discord's native role picker
- Permission hierarchy validation
- Rich embed responses with proper error handling
- Ephemeral responses for clean chat experience

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-16 18:07:07 -07:00

135 lines
3.5 KiB
JavaScript

const fs = require("fs");
const path = require("path");
// Dynamic config that combines file-based config with database
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)
*/
setDatabaseService(databaseService) {
this.databaseService = databaseService;
}
/**
* Get bot configuration (combines file and database)
*/
getBotConfig() {
const fileConfig = this.fileConfig;
const dbConfig = this.databaseService
? 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,
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 ||
[],
},
};
}
/**
* Get guild configuration (from database primarily, file as fallback)
*/
getGuildConfig(guildId) {
if (this.databaseService) {
const dbConfig = this.databaseService.getGuildConfig(guildId);
if (dbConfig) {
return dbConfig;
}
}
// 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,
allowedSfxChannels: null,
sfxVolume: 0.5,
enableFunFacts: true,
enableHamFacts: true,
allowedRolesForRequest: null,
};
}
/**
* Get all guild configurations
*/
getAllGuildConfigs() {
if (this.databaseService) {
return this.databaseService.getAllGuildConfigs();
}
// 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 [];
}
}
module.exports = new ConfigManager();