refactor: move config loading to shared directory
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import { createCommand } from "@shared/lib/utils";
|
import { createCommand } from "@shared/lib/utils";
|
||||||
import { SlashCommandBuilder, PermissionFlagsBits, ModalBuilder, TextInputBuilder, TextInputStyle, ActionRowBuilder, ModalSubmitInteraction } from "discord.js";
|
import { SlashCommandBuilder, PermissionFlagsBits, ModalBuilder, TextInputBuilder, TextInputStyle, ActionRowBuilder, ModalSubmitInteraction } from "discord.js";
|
||||||
import { config, saveConfig } from "@lib/config";
|
import { config, saveConfig } from "@shared/lib/config";
|
||||||
import type { GameConfigType } from "@lib/config";
|
import type { GameConfigType } from "@shared/lib/config";
|
||||||
import { createSuccessEmbed, createErrorEmbed } from "@lib/embeds";
|
import { createSuccessEmbed, createErrorEmbed } from "@lib/embeds";
|
||||||
|
|
||||||
export const configCommand = createCommand({
|
export const configCommand = createCommand({
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { createCommand } from "@shared/lib/utils";
|
import { createCommand } from "@shared/lib/utils";
|
||||||
import { SlashCommandBuilder, PermissionFlagsBits, AttachmentBuilder } from "discord.js";
|
import { SlashCommandBuilder, PermissionFlagsBits, AttachmentBuilder } from "discord.js";
|
||||||
import { config, saveConfig } from "@/lib/config";
|
import { config, saveConfig } from "@shared/lib/config";
|
||||||
import { DrizzleClient } from "@shared/db/DrizzleClient";
|
import { DrizzleClient } from "@shared/db/DrizzleClient";
|
||||||
import { items } from "@db/schema";
|
import { items } from "@db/schema";
|
||||||
import { createSuccessEmbed, createErrorEmbed } from "@lib/embeds";
|
import { createSuccessEmbed, createErrorEmbed } from "@lib/embeds";
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { createCommand } from "@shared/lib/utils";
|
import { createCommand } from "@shared/lib/utils";
|
||||||
import { SlashCommandBuilder, PermissionFlagsBits, MessageFlags } from "discord.js";
|
import { SlashCommandBuilder, PermissionFlagsBits, MessageFlags } from "discord.js";
|
||||||
import { createBaseEmbed } from "@lib/embeds";
|
import { createBaseEmbed } from "@lib/embeds";
|
||||||
import { configManager } from "@/lib/configManager";
|
import { configManager } from "@shared/lib/configManager";
|
||||||
import { config, reloadConfig } from "@/lib/config";
|
import { config, reloadConfig } from "@shared/lib/config";
|
||||||
import { AuroraClient } from "@/lib/BotClient";
|
import { AuroraClient } from "@/lib/BotClient";
|
||||||
|
|
||||||
export const features = createCommand({
|
export const features = createCommand({
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { createCommand } from "@shared/lib/utils";
|
import { createCommand } from "@shared/lib/utils";
|
||||||
import { SlashCommandBuilder, PermissionFlagsBits, MessageFlags, ComponentType } from "discord.js";
|
import { SlashCommandBuilder, PermissionFlagsBits, MessageFlags, ComponentType } from "discord.js";
|
||||||
import { config } from "@/lib/config";
|
import { config } from "@shared/lib/config";
|
||||||
import { PruneService } from "@shared/modules/moderation/prune.service";
|
import { PruneService } from "@shared/modules/moderation/prune.service";
|
||||||
import {
|
import {
|
||||||
getConfirmationMessage,
|
getConfirmationMessage,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
getModerationErrorEmbed,
|
getModerationErrorEmbed,
|
||||||
getUserWarningEmbed
|
getUserWarningEmbed
|
||||||
} from "@/modules/moderation/moderation.view";
|
} from "@/modules/moderation/moderation.view";
|
||||||
import { config } from "@/lib/config";
|
import { config } from "@shared/lib/config";
|
||||||
|
|
||||||
export const warn = createCommand({
|
export const warn = createCommand({
|
||||||
data: new SlashCommandBuilder()
|
data: new SlashCommandBuilder()
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { UserError } from "@/lib/errors";
|
|||||||
import { userTimers, users } from "@db/schema";
|
import { userTimers, users } from "@db/schema";
|
||||||
import { eq, and, sql } from "drizzle-orm";
|
import { eq, and, sql } from "drizzle-orm";
|
||||||
import { DrizzleClient } from "@shared/db/DrizzleClient";
|
import { DrizzleClient } from "@shared/db/DrizzleClient";
|
||||||
import { config } from "@lib/config";
|
import { config } from "@shared/lib/config";
|
||||||
import { TimerType } from "@shared/lib/constants";
|
import { TimerType } from "@shared/lib/constants";
|
||||||
|
|
||||||
const EXAM_TIMER_TYPE = TimerType.EXAM_SYSTEM;
|
const EXAM_TIMER_TYPE = TimerType.EXAM_SYSTEM;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { createCommand } from "@shared/lib/utils";
|
|||||||
import { SlashCommandBuilder, MessageFlags } from "discord.js";
|
import { SlashCommandBuilder, MessageFlags } from "discord.js";
|
||||||
import { economyService } from "@shared/modules/economy/economy.service";
|
import { economyService } from "@shared/modules/economy/economy.service";
|
||||||
import { userService } from "@shared/modules/user/user.service";
|
import { userService } from "@shared/modules/user/user.service";
|
||||||
import { config } from "@/lib/config";
|
import { config } from "@shared/lib/config";
|
||||||
import { createErrorEmbed, createSuccessEmbed } from "@lib/embeds";
|
import { createErrorEmbed, createSuccessEmbed } from "@lib/embeds";
|
||||||
import { UserError } from "@/lib/errors";
|
import { UserError } from "@/lib/errors";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { createCommand } from "@shared/lib/utils";
|
import { createCommand } from "@shared/lib/utils";
|
||||||
import { SlashCommandBuilder } from "discord.js";
|
import { SlashCommandBuilder } from "discord.js";
|
||||||
import { config } from "@/lib/config";
|
import { config } from "@shared/lib/config";
|
||||||
import { createErrorEmbed } from "@/lib/embeds";
|
import { createErrorEmbed } from "@/lib/embeds";
|
||||||
import { getFeedbackTypeMenu } from "@/modules/feedback/feedback.view";
|
import { getFeedbackTypeMenu } from "@/modules/feedback/feedback.view";
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { createErrorEmbed } from "@lib/embeds";
|
|||||||
import { getItemUseResultEmbed } from "@/modules/inventory/inventory.view";
|
import { getItemUseResultEmbed } from "@/modules/inventory/inventory.view";
|
||||||
import type { ItemUsageData } from "@shared/lib/types";
|
import type { ItemUsageData } from "@shared/lib/types";
|
||||||
import { UserError } from "@/lib/errors";
|
import { UserError } from "@/lib/errors";
|
||||||
import { config } from "@/lib/config";
|
import { config } from "@shared/lib/config";
|
||||||
|
|
||||||
export const use = createCommand({
|
export const use = createCommand({
|
||||||
data: new SlashCommandBuilder()
|
data: new SlashCommandBuilder()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Events } from "discord.js";
|
import { Events } from "discord.js";
|
||||||
import type { Event } from "@shared/lib/types";
|
import type { Event } from "@shared/lib/types";
|
||||||
import { config } from "@lib/config";
|
import { config } from "@shared/lib/config";
|
||||||
import { userService } from "@shared/modules/user/user.service";
|
import { userService } from "@shared/modules/user/user.service";
|
||||||
|
|
||||||
// Visitor role
|
// Visitor role
|
||||||
|
|||||||
@@ -1,204 +0,0 @@
|
|||||||
import { readFileSync, existsSync, writeFileSync } from 'node:fs';
|
|
||||||
import { join } from 'node:path';
|
|
||||||
import { z } from 'zod';
|
|
||||||
|
|
||||||
const configPath = join(import.meta.dir, '..', '..', 'config', 'config.json');
|
|
||||||
|
|
||||||
export interface GameConfigType {
|
|
||||||
leveling: {
|
|
||||||
base: number;
|
|
||||||
exponent: number;
|
|
||||||
chat: {
|
|
||||||
cooldownMs: number;
|
|
||||||
minXp: number;
|
|
||||||
maxXp: number;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
economy: {
|
|
||||||
daily: {
|
|
||||||
amount: bigint;
|
|
||||||
streakBonus: bigint;
|
|
||||||
weeklyBonus: bigint;
|
|
||||||
cooldownMs: number;
|
|
||||||
},
|
|
||||||
transfers: {
|
|
||||||
allowSelfTransfer: boolean;
|
|
||||||
minAmount: bigint;
|
|
||||||
},
|
|
||||||
exam: {
|
|
||||||
multMin: number;
|
|
||||||
multMax: number;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
inventory: {
|
|
||||||
maxStackSize: bigint;
|
|
||||||
maxSlots: number;
|
|
||||||
},
|
|
||||||
commands: Record<string, boolean>;
|
|
||||||
lootdrop: {
|
|
||||||
activityWindowMs: number;
|
|
||||||
minMessages: number;
|
|
||||||
spawnChance: number;
|
|
||||||
cooldownMs: number;
|
|
||||||
reward: {
|
|
||||||
min: number;
|
|
||||||
max: number;
|
|
||||||
currency: string;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
studentRole: string;
|
|
||||||
visitorRole: string;
|
|
||||||
colorRoles: string[];
|
|
||||||
welcomeChannelId?: string;
|
|
||||||
welcomeMessage?: string;
|
|
||||||
feedbackChannelId?: string;
|
|
||||||
terminal?: {
|
|
||||||
channelId: string;
|
|
||||||
messageId: string;
|
|
||||||
};
|
|
||||||
moderation: {
|
|
||||||
prune: {
|
|
||||||
maxAmount: number;
|
|
||||||
confirmThreshold: number;
|
|
||||||
batchSize: number;
|
|
||||||
batchDelayMs: number;
|
|
||||||
};
|
|
||||||
cases: {
|
|
||||||
dmOnWarn: boolean;
|
|
||||||
logChannelId?: string;
|
|
||||||
autoTimeoutThreshold?: number;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
system: Record<string, any>;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initial default config state
|
|
||||||
export const config: GameConfigType = {} as GameConfigType;
|
|
||||||
|
|
||||||
const bigIntSchema = z.union([z.string(), z.number(), z.bigint()])
|
|
||||||
.refine((val) => {
|
|
||||||
try {
|
|
||||||
BigInt(val);
|
|
||||||
return true;
|
|
||||||
} catch {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}, { message: "Must be a valid integer" })
|
|
||||||
.transform((val) => BigInt(val));
|
|
||||||
|
|
||||||
const configSchema = z.object({
|
|
||||||
leveling: z.object({
|
|
||||||
base: z.number(),
|
|
||||||
exponent: z.number(),
|
|
||||||
chat: z.object({
|
|
||||||
cooldownMs: z.number(),
|
|
||||||
minXp: z.number(),
|
|
||||||
maxXp: z.number(),
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
economy: z.object({
|
|
||||||
daily: z.object({
|
|
||||||
amount: bigIntSchema,
|
|
||||||
streakBonus: bigIntSchema,
|
|
||||||
weeklyBonus: bigIntSchema.default(50n),
|
|
||||||
cooldownMs: z.number(),
|
|
||||||
}),
|
|
||||||
transfers: z.object({
|
|
||||||
allowSelfTransfer: z.boolean(),
|
|
||||||
minAmount: bigIntSchema,
|
|
||||||
}),
|
|
||||||
exam: z.object({
|
|
||||||
multMin: z.number(),
|
|
||||||
multMax: z.number(),
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
inventory: z.object({
|
|
||||||
maxStackSize: bigIntSchema,
|
|
||||||
maxSlots: z.number(),
|
|
||||||
}),
|
|
||||||
commands: z.record(z.string(), z.boolean()),
|
|
||||||
lootdrop: z.object({
|
|
||||||
activityWindowMs: z.number(),
|
|
||||||
minMessages: z.number(),
|
|
||||||
spawnChance: z.number(),
|
|
||||||
cooldownMs: z.number(),
|
|
||||||
reward: z.object({
|
|
||||||
min: z.number(),
|
|
||||||
max: z.number(),
|
|
||||||
currency: z.string(),
|
|
||||||
})
|
|
||||||
|
|
||||||
}),
|
|
||||||
studentRole: z.string(),
|
|
||||||
visitorRole: z.string(),
|
|
||||||
colorRoles: z.array(z.string()).default([]),
|
|
||||||
welcomeChannelId: z.string().optional(),
|
|
||||||
welcomeMessage: z.string().optional(),
|
|
||||||
feedbackChannelId: z.string().optional(),
|
|
||||||
terminal: z.object({
|
|
||||||
channelId: z.string(),
|
|
||||||
messageId: z.string()
|
|
||||||
}).optional(),
|
|
||||||
moderation: z.object({
|
|
||||||
prune: z.object({
|
|
||||||
maxAmount: z.number().default(100),
|
|
||||||
confirmThreshold: z.number().default(50),
|
|
||||||
batchSize: z.number().default(100),
|
|
||||||
batchDelayMs: z.number().default(1000)
|
|
||||||
}),
|
|
||||||
cases: z.object({
|
|
||||||
dmOnWarn: z.boolean().default(true),
|
|
||||||
logChannelId: z.string().optional(),
|
|
||||||
autoTimeoutThreshold: z.number().optional()
|
|
||||||
})
|
|
||||||
}).default({
|
|
||||||
prune: {
|
|
||||||
maxAmount: 100,
|
|
||||||
confirmThreshold: 50,
|
|
||||||
batchSize: 100,
|
|
||||||
batchDelayMs: 1000
|
|
||||||
},
|
|
||||||
cases: {
|
|
||||||
dmOnWarn: true
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
system: z.record(z.string(), z.any()).default({}),
|
|
||||||
});
|
|
||||||
|
|
||||||
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
|
|
||||||
// We use Object.assign to keep the reference to the exported 'config' object same
|
|
||||||
const validatedConfig = configSchema.parse(rawConfig);
|
|
||||||
Object.assign(config, validatedConfig);
|
|
||||||
|
|
||||||
console.log("🔄 Config reloaded from disk.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initial load
|
|
||||||
reloadConfig();
|
|
||||||
|
|
||||||
// Backwards compatibility alias
|
|
||||||
export const GameConfig = config;
|
|
||||||
|
|
||||||
export function saveConfig(newConfig: unknown) {
|
|
||||||
// Validate and transform input
|
|
||||||
const validatedConfig = configSchema.parse(newConfig);
|
|
||||||
|
|
||||||
const replacer = (key: string, value: any) => {
|
|
||||||
if (typeof value === 'bigint') {
|
|
||||||
return value.toString();
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
};
|
|
||||||
|
|
||||||
const jsonString = JSON.stringify(validatedConfig, replacer, 4);
|
|
||||||
writeFileSync(configPath, jsonString, 'utf-8');
|
|
||||||
reloadConfig();
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { readdir } from "node:fs/promises";
|
import { readdir } from "node:fs/promises";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import type { Command } from "@shared/lib/types";
|
import type { Command } from "@shared/lib/types";
|
||||||
import { config } from "@lib/config";
|
import { config } from "@shared/lib/config";
|
||||||
import type { LoadResult, LoadError } from "./types";
|
import type { LoadResult, LoadError } from "./types";
|
||||||
import type { Client } from "../BotClient";
|
import type { Client } from "../BotClient";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { Interaction } from "discord.js";
|
import type { Interaction } from "discord.js";
|
||||||
import { TextChannel, MessageFlags } from "discord.js";
|
import { TextChannel, MessageFlags } from "discord.js";
|
||||||
import { config } from "@/lib/config";
|
import { config } from "@shared/lib/config";
|
||||||
import { AuroraClient } from "@/lib/BotClient";
|
import { AuroraClient } from "@/lib/BotClient";
|
||||||
import { buildFeedbackMessage, getFeedbackModal } from "./feedback.view";
|
import { buildFeedbackMessage, getFeedbackModal } from "./feedback.view";
|
||||||
import { FEEDBACK_CUSTOM_IDS, type FeedbackType, type FeedbackData } from "./feedback.types";
|
import { FEEDBACK_CUSTOM_IDS, type FeedbackType, type FeedbackData } from "./feedback.types";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { ButtonInteraction, MessageFlags } from "discord.js";
|
import { ButtonInteraction, MessageFlags } from "discord.js";
|
||||||
import { config } from "@/lib/config";
|
import { config } from "@shared/lib/config";
|
||||||
import { getEnrollmentSuccessMessage } from "./enrollment.view";
|
import { getEnrollmentSuccessMessage } from "./enrollment.view";
|
||||||
import { classService } from "@shared/modules/class/class.service";
|
import { classService } from "@shared/modules/class/class.service";
|
||||||
import { userService } from "@shared/modules/user/user.service";
|
import { userService } from "@shared/modules/user/user.service";
|
||||||
|
|||||||
Reference in New Issue
Block a user