feat: Introduce dynamic JSON-based configuration for game settings and command toggling via a new admin command.
This commit is contained in:
@@ -3,6 +3,7 @@ import { readdir } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import type { Command, Event } from "@lib/types";
|
||||
import { env } from "@lib/env";
|
||||
import { config } from "@lib/config";
|
||||
|
||||
class Client extends DiscordClient {
|
||||
|
||||
@@ -56,8 +57,23 @@ class Client extends DiscordClient {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Extract category from parent directory name
|
||||
// filePath is like /path/to/commands/admin/features.ts
|
||||
// we want "admin"
|
||||
const pathParts = filePath.split('/');
|
||||
const category = pathParts[pathParts.length - 2];
|
||||
|
||||
for (const command of commands) {
|
||||
if (this.isValidCommand(command)) {
|
||||
command.category = category; // Inject category
|
||||
|
||||
const isEnabled = config.commands[command.data.name] !== false; // Default true if undefined
|
||||
|
||||
if (!isEnabled) {
|
||||
console.log(`🚫 Skipping disabled command: ${command.data.name}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
this.commands.set(command.data.name, command);
|
||||
console.log(`✅ Loaded command: ${command.data.name}`);
|
||||
} else {
|
||||
|
||||
71
src/lib/config.ts
Normal file
71
src/lib/config.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { readFileSync, existsSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
const configPath = join(process.cwd(), 'src', 'config', 'game.json');
|
||||
|
||||
export interface GameConfigType {
|
||||
leveling: {
|
||||
base: number;
|
||||
exponent: number;
|
||||
chat: {
|
||||
cooldownMs: number;
|
||||
minXp: number;
|
||||
maxXp: number;
|
||||
}
|
||||
},
|
||||
economy: {
|
||||
daily: {
|
||||
amount: bigint;
|
||||
streakBonus: bigint;
|
||||
cooldownMs: number;
|
||||
},
|
||||
transfers: {
|
||||
allowSelfTransfer: boolean;
|
||||
minAmount: bigint;
|
||||
}
|
||||
},
|
||||
inventory: {
|
||||
maxStackSize: bigint;
|
||||
maxSlots: number;
|
||||
},
|
||||
commands: Record<string, boolean>;
|
||||
}
|
||||
|
||||
// Initial default config state
|
||||
export const config: GameConfigType = {} as GameConfigType;
|
||||
|
||||
export function reloadConfig() {
|
||||
if (!existsSync(configPath)) {
|
||||
throw new Error(`Config file not found at ${configPath}`);
|
||||
}
|
||||
|
||||
const raw = readFileSync(configPath, 'utf-8');
|
||||
const rawConfig = JSON.parse(raw);
|
||||
|
||||
// Update config object in place
|
||||
config.leveling = rawConfig.leveling;
|
||||
config.economy = {
|
||||
daily: {
|
||||
...rawConfig.economy.daily,
|
||||
amount: BigInt(rawConfig.economy.daily.amount),
|
||||
streakBonus: BigInt(rawConfig.economy.daily.streakBonus),
|
||||
},
|
||||
transfers: {
|
||||
...rawConfig.economy.transfers,
|
||||
minAmount: BigInt(rawConfig.economy.transfers.minAmount),
|
||||
}
|
||||
};
|
||||
config.inventory = {
|
||||
...rawConfig.inventory,
|
||||
maxStackSize: BigInt(rawConfig.inventory.maxStackSize),
|
||||
};
|
||||
config.commands = rawConfig.commands || {};
|
||||
|
||||
console.log("🔄 Config reloaded from disk.");
|
||||
}
|
||||
|
||||
// Initial load
|
||||
reloadConfig();
|
||||
|
||||
// Backwards compatibility alias
|
||||
export const GameConfig = config;
|
||||
20
src/lib/configManager.ts
Normal file
20
src/lib/configManager.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { readFileSync, writeFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { KyokoClient } from '@/lib/KyokoClient';
|
||||
|
||||
const configPath = join(process.cwd(), 'src', 'config', 'game.json');
|
||||
|
||||
export const configManager = {
|
||||
toggleCommand: (commandName: string, enabled: boolean) => {
|
||||
const raw = readFileSync(configPath, 'utf-8');
|
||||
const data = JSON.parse(raw);
|
||||
|
||||
if (!data.commands) {
|
||||
data.commands = {};
|
||||
}
|
||||
|
||||
data.commands[commandName] = enabled;
|
||||
|
||||
writeFileSync(configPath, JSON.stringify(data, null, 4));
|
||||
}
|
||||
};
|
||||
@@ -3,6 +3,7 @@ import type { ChatInputCommandInteraction, ClientEvents, SlashCommandBuilder, Sl
|
||||
export interface Command {
|
||||
data: SlashCommandBuilder | SlashCommandOptionsOnlyBuilder | SlashCommandSubcommandsOnlyBuilder;
|
||||
execute: (interaction: ChatInputCommandInteraction) => Promise<void> | void;
|
||||
category?: string;
|
||||
}
|
||||
|
||||
export interface Event<K extends keyof ClientEvents> {
|
||||
|
||||
Reference in New Issue
Block a user