Begin work on overhauling config

This commit is contained in:
MSWS
2023-04-15 16:40:05 -07:00
parent 5466d37364
commit a48c908aff
7 changed files with 944 additions and 200 deletions

900
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -16,6 +16,8 @@
"@edgmrs/maul": "^1.1.1",
"discord.js": "^14.9.0",
"install": "^0.13.0",
"npm": "^9.6.4"
"npm": "^9.6.4",
"sqlite": "^4.1.2",
"sqlite3": "^5.1.6"
}
}

View File

@@ -3,7 +3,6 @@ import { Client, MessageCreateOptions, MessagePayload, TextBasedChannel } from "
import { DSBot } from "./api/DSBot";
import { AbstractModuleManager } from "./api/ModuleManager";
import { BotConfig } from "./api/data/BotConfig";
import { FileConfig } from "./data/FileConfig";
import { LogSettings, LogsConfig, Severity } from "./data/configs/Logs";
import { MaulConfig, MaulEntry } from "./data/configs/MAUL";
import { TokenEntry } from "./data/configs/Token";
@@ -14,6 +13,8 @@ import { PermissionsManager } from "./modules/PermissionsManager";
import { ReactRoles } from "./modules/ReactRoles";
import { PingCommand } from "./modules/commands/PingCommand";
import { VoiceCommand } from "./modules/commands/VoiceCommand";
import { FileConfigStorage } from "./data/FileConfigStorage";
import { ConfigManager } from "./api/data/ConfigManager";
export class Bot implements DSBot {
maul: MaulAPI | undefined;
@@ -25,40 +26,43 @@ export class Bot implements DSBot {
constructor() {
this.modules = new ModuleManager();
this.config = new FileConfig("configs/global.json");
this.data = new FileConfig("data/data.json");
const tokenEntry = this.config.registerEntry(new TokenEntry());
const maulEntry = this.config.registerEntry(new MaulEntry());
const logEntry = this.config.registerEntry(new LogsConfig()) as LogsConfig;
// this.config = new FileConfig("configs/global.json");
// this.data = new FileConfig("data/data.json");
this.config = new ConfigManager(new FileConfigStorage("configs/global.json"));
this.client = new Client({ intents: ["Guilds", "GuildMessageReactions", "GuildVoiceStates", "DirectMessages"] });
this.config.load().then(() => {
this.config.getValue(tokenEntry).then(token => {
this.client.login(token as string);
async () => {
const tokenEntry = await this.config.registerEntry(new TokenEntry());
const maulEntry = await this.config.registerEntry(new MaulEntry());
const logEntry = await this.config.registerEntry(new LogsConfig()) as LogsConfig;
this.config.load().then(() => {
this.config.getValue(tokenEntry).then(token => {
this.client.login(token as string);
});
this.config.getValue(maulEntry).then((result) => {
const conf = result as MaulConfig;
this.maul = new MAUL(conf.URL, conf.TOKEN, conf.IP);
});
this.logger = logEntry.value;
});
this.config.getValue(maulEntry).then((result) => {
const conf = result as MaulConfig;
this.maul = new MAUL(conf.URL, conf.TOKEN, conf.IP);
this.client.once("ready", () => {
this.log(Severity.DEBUG, `Successfully logged in as ${this.client.user?.tag}.`);
this.initModules();
});
this.logger = logEntry.value;
});
this.client.on("error", (error) => {
this.log(Severity.ERROR, `I ran into an API error: ${error.name}\n${error.message}\n${error.stack}`);
});
this.client.once("ready", () => {
this.log(Severity.DEBUG, `Successfully logged in as ${this.client.user?.tag}.`);
this.initModules();
});
this.client.on("error", (error) => {
this.log(Severity.ERROR, `I ran into an API error: ${error.name}\n${error.message}\n${error.stack}`);
});
process.on("unhandledRejection", (error) => {
console.error(error);
this.log(Severity.ERROR, `I ran into a process error: ${(error as any).stack}`);
});
process.on("unhandledRejection", (error) => {
console.error(error);
this.log(Severity.ERROR, `I ran into a process error: ${(error as any).stack}`);
});
};
}
log(severity: Severity, message: string | MessagePayload | MessageCreateOptions): void {

View File

@@ -1,5 +1,4 @@
import { Config } from "./Config";
import { GuildConfig } from "./GuildConfig";
import { Config, GuildConfig } from "./Config";
export interface BotConfig extends Config {
getGuildConfig(guildId: string): Promise<GuildConfig>;

View File

@@ -8,10 +8,10 @@ export interface GuildedConfigEntry<T = unknown> extends ConfigEntry<T> {
}
export interface Config {
registerEntry(entry: ConfigEntry): ConfigEntry;
registerEntry(entry: ConfigEntry): Promise<ConfigEntry>;
removeEntry(entry: ConfigEntry): void;
getConfigEntry(name: string): ConfigEntry | undefined;
getConfigEntry(name: string): Promise<ConfigEntry | undefined>;
getEntries(): Promise<ConfigEntry[]>;
getValue(id: string | ConfigEntry): Promise<unknown>;
@@ -19,7 +19,19 @@ export interface Config {
save(): Promise<void>;
save<T extends ConfigEntry>(entry: T): Promise<T>;
load(): Promise<void>;
load<T extends ConfigEntry>(entry: T): Promise<T>;
}
// export interface GuildConfig extends Config {
// guildId: string;
// }
export interface ConfigStorage {
getEntry(id: string): Promise<ConfigEntry | undefined>;
getEntries(): Promise<ConfigEntry[]>;
saveEntry(entry: ConfigEntry): Promise<void>;
removeEntry(entry: ConfigEntry): Promise<void>;
load(): Promise<void>;
}

View File

@@ -1,5 +1,22 @@
import { Config } from "./Config";
import { ConfigManager } from "./ConfigManager";
import { ConfigStorage } from "./Config";
import { GuildedConfigEntry } from "./Config";
export interface GuildConfig extends Config {
guildId: string;
export class GuildConfig extends ConfigManager {
constructor(private guildId: string, storage: ConfigStorage) {
super(storage);
}
async registerGuildEntry(entry: GuildedConfigEntry): Promise<GuildedConfigEntry> {
entry.guildId = this.guildId;
return await this.registerEntry(entry) as GuildedConfigEntry;
}
async getGuildConfigEntry(name: string): Promise<GuildedConfigEntry | undefined> {
const entry = this.getConfigEntry(name);
if (entry && "guildId" in entry)
return await entry as GuildedConfigEntry;
return undefined;
}
}

View File

@@ -1,134 +0,0 @@
import * as fs from "fs";
import { BotConfig } from "../api/data/BotConfig";
import { ConfigEntry } from "../api/data/Config";
import { GuildConfig } from "../api/data/GuildConfig";
export class FileConfig implements BotConfig {
entries: ConfigEntry[] = [];
configs: GuildConfig[] = [];
file: string;
parent: string;
constructor(file: string) {
this.file = file;
this.parent = file.split("/").slice(0, -1).join("/");
}
getGuildConfigs(): Promise<GuildConfig[]> {
return Promise.resolve(this.configs);
}
addGuildConfig(config: GuildConfig): void {
this.configs.push(config);
}
getGuildConfig(guildId: string): Promise<GuildConfig> {
let config = this.configs.find((c) => c.guildId === guildId);
if (!config) {
console.log(`Creating new guild config: ${guildId}`);
config = new FileGuildConfig(this.parent + `/guilds/${guildId}.json`, guildId);
this.configs.push(config);
}
return Promise.resolve(config);
}
async getEntries(): Promise<ConfigEntry[]> {
return this.entries;
}
async getValue(id: string | ConfigEntry): Promise<unknown> {
if (typeof id === "string")
return this.getConfigEntry(id)?.value;
for (const entry of this.entries) {
if (entry.name === id.name)
return entry.value;
}
return undefined;
}
async setValue(id: string | ConfigEntry<unknown>, value: unknown): Promise<void> {
const entry = this.getConfigEntry((typeof id === "string") ? id : id.name);
if (!entry)
return Promise.reject(`Config entry not found: ${id}`);
entry.value = value;
return Promise.resolve();
}
async load(): Promise<void> {
const promise = new Promise<void>((resolve, reject) => {
if (!fs.existsSync(this.file)) {
console.log(`Creating new config file: ${this.file}`);
fs.writeFileSync(this.file, "{}");
}
fs.open(this.file, "r", (err, fd) => {
if (err) {
console.error(err);
reject(err);
return;
}
// Parse the file as JSON
const data = JSON.parse(fs.readFileSync(fd, "utf8"));
// For each key in the JSON object, create a new ConfigEntry
for (const key in data) {
const entry = this.getConfigEntry(key);
if (!entry) {
console.error(`Config entry not found: ${key}`);
continue;
}
entry.value = data[key];
}
resolve();
});
});
if (this.configs.length === 0 && !this.file.includes("guilds")) {
const files = fs.readdirSync(this.parent + "/guilds");
for (const file of files) {
console.log(`Loading guild config: ${file}`);
const config = new FileGuildConfig(this.parent + `/guilds/${file}`, file.split(".")[0]);
this.configs.push(config);
}
}
return promise;
}
registerEntry(entry: ConfigEntry): ConfigEntry {
this.entries.push(entry);
return entry;
}
removeEntry(entry: ConfigEntry): void {
this.entries.splice(this.entries.indexOf(entry), 1);
}
async save() {
// JSON stringify entries and write to file
const data: any = {};
this.entries.forEach(entry => {
data[entry.name] = entry.value;
});
fs.writeFileSync(this.file, JSON.stringify(data));
}
getConfigEntry(name: string): ConfigEntry | undefined {
return this.entries.find(entry => entry.name === name);
}
}
export class FileGuildConfig extends FileConfig implements GuildConfig {
guildId: string;
constructor(file: string, guildId: string) {
super(file);
this.guildId = guildId;
}
}