- 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>
135 lines
3.5 KiB
JavaScript
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();
|