/[sudobot]/branches/3.x/src/api/controllers/ConfigController.ts
ViewVC logotype

Contents of /branches/3.x/src/api/controllers/ConfigController.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: 11714 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-2022 OSN Inc.
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 { dot, object } from "dot-object";
21 import { body } from "express-validator";
22 import { z as zod } from "zod";
23 import KeyValuePair from "../../types/KeyValuePair";
24 import Controller from "../Controller";
25 import RequireAuth from "../middleware/RequireAuth";
26 import ValidatorError from "../middleware/ValidatorError";
27 import Request from "../Request";
28 import merge from 'ts-deepmerge';
29
30 export default class ConfigController extends Controller {
31 globalMiddleware(): Function[] {
32 return [RequireAuth, ValidatorError];
33 }
34
35 middleware(): KeyValuePair<Function[]> {
36 return {
37 update: [
38 body(["config"]).isObject()
39 ]
40 };
41 }
42
43 private zodSchema(id: string) {
44 const snowflake = zod.string().regex(/\d+/, { message: "The given value is not a Snowflake" });
45 const config = this.client.config.props[id];
46
47 const schema = zod.object({
48 "prefix": zod.string().optional(),
49 "debug": zod.boolean().optional(),
50 "mute_role": snowflake.optional(),
51 "gen_role": snowflake.optional(),
52 "logging_channel": snowflake.optional(),
53 "logging_channel_join_leave": snowflake.optional(),
54 "mod_role": snowflake.optional(),
55 "announcement_channel": snowflake.optional(),
56 "admin": snowflake.optional(),
57 "lockall": zod.array(zod.string()).optional(),
58 "warn_notallowed": zod.boolean().optional(),
59 "role_commands": zod.record(
60 snowflake,
61 zod.array(zod.string().min(1))
62 ).optional().default({}),
63 "autoclear": zod.object({
64 "enabled": zod.boolean().optional(),
65 "channels": zod.array(snowflake).optional().default(config.autoclear.channels)
66 }).optional(),
67 "verification": zod.object({
68 "enabled": zod.boolean().optional(),
69 "role": snowflake.optional()
70 }).optional(),
71 "welcomer": zod.object({
72 "enabled": zod.boolean().optional(),
73 "channel": snowflake.optional(),
74 "message": zod.string().min(1).or(zod.null()).optional(),
75 "randomize": zod.boolean().optional()
76 }).optional(),
77 "cooldown": zod.object({
78 "enabled": zod.boolean().optional(),
79 "global": zod.any().optional(),
80 "cmds": zod.object({}).optional()
81 }).optional(),
82 "starboard": zod.object({
83 "enabled": zod.boolean().optional(),
84 "reactions": zod.number().int().optional(),
85 "channel": snowflake.optional()
86 }).optional(),
87 "autorole": zod.object({
88 "enabled": zod.boolean().optional(),
89 "roles": zod.array(snowflake).optional().default(config.autorole.roles)
90 }).optional(),
91 "spam_filter": zod.object({
92 "enabled": zod.boolean().optional(),
93 "limit": zod.number().int().optional(),
94 "time": zod.number().optional(),
95 "diff": zod.number().optional(),
96 "exclude": zod.array(snowflake).optional().default(config.spam_filter.exclude),
97 "samelimit": zod.number().int().optional(),
98 "unmute_in": zod.number().optional()
99 }).optional(),
100 "raid": zod.object({
101 "enabled": zod.boolean().optional(),
102 "max_joins": zod.number().int().optional(),
103 "time": zod.number().optional(),
104 "channels": zod.array(snowflake).optional().default(config.raid.channels),
105 "exclude": zod.boolean().optional()
106 }).optional(),
107 "global_commands": zod.array(zod.string()).optional().default(config.global_commands),
108 "filters": zod.object({
109 "ignore_staff": zod.boolean().optional(),
110 "chars_repeated": zod.number().int().optional(),
111 "words_repeated": zod.number().int().optional(),
112 "words": zod.array(zod.string()).optional().default(config.filters.words),
113 "tokens": zod.array(zod.string()).optional().default(config.filters.tokens),
114 "invite_message": zod.string().optional(),
115 "words_excluded": zod.array(snowflake).optional().default(config.filters.words_excluded),
116 "domain_excluded": zod.array(snowflake).optional().default(config.filters.domain_excluded),
117 "invite_excluded": zod.array(snowflake).optional().default(config.filters.invite_excluded),
118 "words_enabled": zod.boolean().optional(),
119 "invite_enabled": zod.boolean().optional(),
120 "domain_enabled": zod.boolean().optional(),
121 "regex": zod.boolean().optional(),
122 "file_mimes_excluded": zod.array(zod.string()).optional().default(config.filters.file_mimes_excluded),
123 "file_types_excluded": zod.array(zod.string()).optional().default(config.filters.file_types_excluded),
124 "domains": zod.array(zod.string()).optional().default(config.filters.domains),
125 "regex_patterns": zod.array(zod.string()).optional().default(config.filters.regex_patterns),
126 "rickrolls_enabled": zod.boolean().optional(),
127 "pings": zod.number().int().optional()
128 }).optional()
129 });
130
131 return schema;
132 }
133
134 public async index(request: Request) {
135 const { id } = request.params;
136
137 if (!request.user?.guilds.includes(id)) {
138 return this.response({ error: "You don't have permission to access configuration of this guild." }, 403);
139 }
140
141 if (id === "global" || !this.client.config.props[id]) {
142 return this.response({ error: "No configuration found for the given guild ID" }, 404);
143 }
144
145 return this.client.config.props[id];
146 }
147
148 public async update(request: Request) {
149 const { id } = request.params;
150 const { config: origconfig } = request.body;
151
152 console.log(origconfig);
153
154 try {
155 const currentConfigDotObject = this.client.config.props[id];
156
157 console.log("Current config: ", currentConfigDotObject);
158
159 const result = this.zodSchema(id).safeParse(object(origconfig));
160
161 if (!result?.success) {
162 return this.response({ error: "The data schema does not match.", error_type: 'validation', errors: result.error.errors }, 422);
163 }
164
165 const config = result.data;
166 const newConfig = merge.withOptions({ mergeArrays: false }, currentConfigDotObject, config);
167 const result2 = this.zodSchema(id).safeParse(newConfig);
168
169 if (!result2?.success) {
170 console.log(result2.error.errors);
171 return this.response({ error: 'Internal Server Error (500)', error_type: 'internal' }, 500);
172 }
173
174 console.log('Final', newConfig);
175 this.client.config.props[id] = newConfig;
176 this.client.config.write();
177 return { message: "Configuration updated", previous: dot(currentConfigDotObject), new: dot(this.client.config.props[id]) };
178 }
179 catch (e) {
180 console.log(e);
181 return this.response({ error: 'Internal Server Error', error_type: 'internal' }, 500);
182 }
183 }
184
185 public async update2(request: Request) {
186 const { id } = request.params;
187 const { config: origconfig } = request.body;
188
189 console.log(origconfig);
190
191 try {
192 const currentConfigDotObject = dot(this.client.config.props[id]);
193 let newConfigDotObject = {...currentConfigDotObject};
194
195 console.log("Current config: ", currentConfigDotObject);
196
197 const result = this.zodSchema(id).safeParse(object({...origconfig}));
198
199 if (!result?.success) {
200 return this.response({ error: "The data schema does not match.", error_type: 'validation', errors: result.error.errors }, 422);
201 }
202
203 const config = result.data;
204
205 console.log(config);
206
207 for (const key in config) {
208 const configKey = key as keyof typeof config;
209 const regexMatched = /(.+)\[\d+\]/g.test(configKey);
210
211 if (typeof currentConfigDotObject[configKey] === 'undefined' && !regexMatched) {
212 console.log(configKey, config[configKey]);
213
214 if (currentConfigDotObject[configKey] instanceof Array && config[configKey] instanceof Array) {
215 console.log('Array');
216 }
217 else
218 return this.response({ error: `The key '${configKey}' is not allowed` }, 422);
219 }
220
221 if (!regexMatched && config[configKey] !== null && config[configKey] !== null && typeof config[configKey] !== typeof currentConfigDotObject[configKey]) {
222 console.log(typeof config[configKey], typeof currentConfigDotObject[configKey]);
223
224 if (typeof currentConfigDotObject[configKey] === 'number' && typeof config[configKey] === 'string') {
225 const int = parseInt(config[configKey]!.toString());
226
227 if (int !== NaN) {
228 newConfigDotObject[configKey] = int;
229 console.log("Updating: ", configKey, config[configKey], newConfigDotObject[configKey]);
230 continue;
231 }
232 }
233
234 return this.response({ error: `The key '${configKey}' has incompatible value type '${config[configKey] === null ? 'null' : typeof config[configKey]}'` }, 422);
235 }
236
237 console.log("Updating: ", configKey, config[configKey], newConfigDotObject[configKey]);
238 }
239
240 const newObj = object({...newConfigDotObject});
241 const configObj = object({...config});
242
243 // console.log("Newobj", newObj);
244 // console.log("Configobj", configObj);
245
246 newConfigDotObject = merge.withOptions({ mergeArrays: false }, newObj, configObj);
247 console.log("Output: ", newConfigDotObject);
248
249 this.client.config.props[id] = newConfigDotObject;
250 this.client.config.write();
251
252 return { message: "Configuration updated", previous: currentConfigDotObject, new: dot(this.client.config.props[id]) };
253 }
254 catch (e) {
255 console.log(e);
256 return this.response({ error: 'Internal Server Error', error_type: 'internal' }, 500);
257 }
258 }
259 }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26