/[sudobot]/branches/8.x/src/services/ConfigManager.ts
ViewVC logotype

Contents of /branches/8.x/src/services/ConfigManager.ts

Parent Directory Parent Directory | Revision Log Revision Log


Revision 577 - (show annotations)
Mon Jul 29 18:52:37 2024 UTC (8 months ago) by rakinar2
File MIME type: application/typescript
File size: 8099 byte(s)
chore: add old version archive branches (2.x to 9.x-dev)
1 /**
2 * This file is part of SudoBot.
3 *
4 * Copyright (C) 2021-2023 OSN Developers.
5 *
6 * SudoBot is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Affero General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * SudoBot is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with SudoBot. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20 import { Snowflake } from "discord.js";
21 import fs, { writeFile } from "fs/promises";
22 import path from "path";
23 import { AnyZodObject, z } from "zod";
24 import { zodToJsonSchema } from "zod-to-json-schema";
25 import type { Extension } from "../core/Extension";
26 import Service from "../core/Service";
27 import { GuildConfig, GuildConfigSchema } from "../types/GuildConfigSchema";
28 import { SystemConfig, SystemConfigSchema } from "../types/SystemConfigSchema";
29 import { log, logDebug, logInfo } from "../utils/Logger";
30 import { sudoPrefix } from "../utils/utils";
31
32 export * from "../types/GuildConfigSchema";
33
34 export const name = "configManager";
35
36 export type GuildConfigContainer = {
37 [key: string]: GuildConfig | undefined;
38 };
39
40 export default class ConfigManager extends Service {
41 public readonly configPath = sudoPrefix("config/config.json");
42 public readonly systemConfigPath = sudoPrefix("config/system.json");
43 public readonly schemaDirectory = sudoPrefix("config/schema", true);
44 public readonly configSchemaPath = path.join(this.schemaDirectory, "config.json");
45 public readonly systemConfigSchemaPath = path.join(this.schemaDirectory, "system.json");
46
47 protected configSchemaInfo =
48 "https://raw.githubusercontent.com/onesoft-sudo/sudobot/main/config/schema/config.json";
49 protected systemConfigSchemaInfo =
50 "https://raw.githubusercontent.com/onesoft-sudo/sudobot/main/config/schema/system.json";
51 protected loaded = false;
52 protected guildConfigSchema = GuildConfigSchema;
53 protected systemConfigSchema = SystemConfigSchema;
54 protected guildConfigContainerSchema = this.guildConfigContainer();
55
56 config: GuildConfigContainer = {} as GuildConfigContainer;
57 systemConfig: SystemConfig = {} as SystemConfig;
58
59 /**
60 * This service is manually booted by the Extension Service.
61 */
62 async manualBoot() {
63 await this.loadOnce();
64 }
65
66 private guildConfigContainer() {
67 return z.record(z.string(), this.guildConfigSchema.optional().or(z.undefined()));
68 }
69
70 loadOnce() {
71 if (this.loaded) {
72 return;
73 }
74
75 this.loaded = true;
76 return this.load();
77 }
78
79 async load() {
80 log(`Loading system configuration from file: ${this.systemConfigPath}`);
81 const systemConfigFileContents = await fs.readFile(this.systemConfigPath, {
82 encoding: "utf-8"
83 });
84
85 log(`Loading guild configuration from file: ${this.configPath}`);
86 const configFileContents = await fs.readFile(this.configPath, { encoding: "utf-8" });
87
88 const configJSON = JSON.parse(configFileContents);
89 const systemConfigJSON = JSON.parse(systemConfigFileContents);
90
91 if ("$schema" in configJSON) {
92 this.configSchemaInfo = configJSON.$schema;
93 delete configJSON.$schema;
94 }
95
96 if ("$schema" in systemConfigJSON) {
97 this.systemConfigSchemaInfo = systemConfigJSON.$schema;
98 delete systemConfigJSON.$schema;
99 }
100
101 this.config = this.guildConfigContainerSchema.parse(configJSON);
102 this.systemConfig = this.systemConfigSchema.parse(systemConfigJSON);
103 logInfo("Successfully loaded the configuration files");
104 }
105
106 onReady() {
107 const guildIds = this.client.guilds.cache.keys();
108
109 if (!process.env.PRIVATE_BOT_MODE) {
110 for (const id of guildIds) {
111 if (id in this.config) {
112 continue;
113 }
114
115 logInfo(`Auto configuring default settings for guild: ${id}`);
116 this.autoConfigure(id);
117 }
118 }
119
120 if (!process.env.NO_GENERATE_CONFIG_SCHEMA) {
121 this.client.logger.info("Generating configuration schema files");
122 this.generateSchema();
123 }
124 }
125
126 autoConfigure(id: Snowflake) {
127 this.config[id] = this.guildConfigSchema.parse({});
128 }
129
130 testConfig() {
131 const guildResult = this.guildConfigContainerSchema.safeParse(this.config);
132
133 if (!guildResult.success) {
134 return { error: guildResult.error, type: "guild" as const };
135 }
136
137 const systemResult = this.systemConfigSchema.safeParse(this.systemConfig);
138
139 if (!systemResult.success) {
140 return { error: systemResult.error, type: "system" as const };
141 }
142
143 return null;
144 }
145
146 async write({ guild = true, system = true } = {}) {
147 if (guild) {
148 log(`Writing guild configuration to file: ${this.configPath}`);
149
150 const json = JSON.stringify(
151 {
152 $schema: this.configSchemaInfo,
153 ...this.config
154 },
155 null,
156 4
157 );
158
159 await writeFile(this.configPath, json, { encoding: "utf-8" });
160 }
161
162 if (system) {
163 log(`Writing system configuration to file: ${this.systemConfigPath}`);
164
165 const json = JSON.stringify(
166 {
167 $schema: this.systemConfigSchemaInfo,
168 ...this.systemConfig
169 },
170 null,
171 4
172 );
173
174 await writeFile(this.systemConfigPath, json, { encoding: "utf-8" });
175 }
176
177 logInfo("Successfully wrote the configuration files");
178 }
179
180 get<T extends GuildConfig = GuildConfig>(guildId: Snowflake): T | undefined {
181 return this.config[guildId] as T | undefined;
182 }
183
184 set(guildId: Snowflake, value: GuildConfig) {
185 this.config[guildId] = value;
186 }
187
188 async registerExtensionConfig(extensions: Extension[]) {
189 if (extensions.length === 0) {
190 return;
191 }
192
193 logDebug("Registering extension configuration schemas");
194
195 let finalGuildConfigSchema: AnyZodObject = this.guildConfigSchema;
196 let finalSystemConfigSchema: AnyZodObject = this.systemConfigSchema;
197
198 for (const extension of extensions) {
199 const guildConfigSchema = await extension.guildConfig();
200 const systemConfigSchema = await extension.systemConfig();
201
202 if (guildConfigSchema) {
203 finalGuildConfigSchema = finalGuildConfigSchema.extend(guildConfigSchema);
204 }
205
206 if (systemConfigSchema) {
207 finalSystemConfigSchema = finalSystemConfigSchema.extend(systemConfigSchema);
208 }
209 }
210
211 this.systemConfigSchema = finalSystemConfigSchema as typeof this.systemConfigSchema;
212 this.guildConfigSchema = finalGuildConfigSchema as typeof this.guildConfigSchema;
213 this.guildConfigContainerSchema = this.guildConfigContainer();
214 }
215
216 async generateSchema() {
217 const configSchema = JSON.stringify(
218 zodToJsonSchema(this.guildConfigContainerSchema),
219 null,
220 4
221 );
222 await writeFile(this.configSchemaPath, configSchema, { encoding: "utf-8" });
223 logInfo("Successfully generated the guild configuration schema file");
224
225 const systemConfigSchema = JSON.stringify(
226 zodToJsonSchema(this.systemConfigSchema),
227 null,
228 4
229 );
230 await writeFile(this.systemConfigSchemaPath, systemConfigSchema, { encoding: "utf-8" });
231 logInfo("Successfully generated the system configuration schema file");
232 }
233 }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26