blob: 2c299d014149b26ae8dafdbb2db31ec441e00aa8 [file] [log] [blame]
pineafan63fc5e22022-08-04 22:04:10 +01001import type Discord from "discord.js";
2import { Collection, MongoClient } from "mongodb";
Skyler Grey75ea9172022-08-06 10:22:23 +01003// @ts-expect-error
pineafan63fc5e22022-08-04 22:04:10 +01004import structuredClone from "@ungap/structured-clone";
Skyler Grey75ea9172022-08-06 10:22:23 +01005import config from "../config/main.json" assert { type: "json" };
pineafan4edb7762022-06-26 19:21:04 +01006
7const mongoClient = new MongoClient(config.mongoUrl);
pineafan63fc5e22022-08-04 22:04:10 +01008await mongoClient.connect();
pineafan4edb7762022-06-26 19:21:04 +01009const database = mongoClient.db("Nucleus");
pineafan6fb3e072022-05-20 19:27:23 +010010
pineafan63fc5e22022-08-04 22:04:10 +010011// eslint-disable-next-line @typescript-eslint/no-explicit-any
12export const Entry = (data: any) => {
pineafan6fb3e072022-05-20 19:27:23 +010013 data = data ?? {};
pineafan63fc5e22022-08-04 22:04:10 +010014 // eslint-disable-next-line @typescript-eslint/no-explicit-any
15 data.getKey = (key: any) => data[key];
pineafan6fb3e072022-05-20 19:27:23 +010016 return {
pineafan63fc5e22022-08-04 22:04:10 +010017 // eslint-disable-next-line @typescript-eslint/no-explicit-any
18 get(target: Record<string, any>, prop: string, receiver: any) {
19 let dataToReturn = data[prop];
Skyler Grey11236ba2022-08-08 21:13:33 +010020 if (dataToReturn === null) return Reflect.get(target, prop, receiver);
21 if (typeof dataToReturn === "object" && !Array.isArray(dataToReturn))
22 dataToReturn = new Proxy(Reflect.get(target, prop, receiver), Entry(dataToReturn));
pineafan6fb3e072022-05-20 19:27:23 +010023 return dataToReturn ?? Reflect.get(target, prop, receiver);
24 }
pineafan63fc5e22022-08-04 22:04:10 +010025 };
26};
pineafan6fb3e072022-05-20 19:27:23 +010027
pineafan4edb7762022-06-26 19:21:04 +010028export class Guilds {
pineafan6fb3e072022-05-20 19:27:23 +010029 guilds: Collection<GuildConfig>;
pineafan63fc5e22022-08-04 22:04:10 +010030 defaultData: GuildConfig | null;
31
32 constructor() {
pineafan4edb7762022-06-26 19:21:04 +010033 this.guilds = database.collection<GuildConfig>("guilds");
pineafan63fc5e22022-08-04 22:04:10 +010034 this.defaultData = null;
pineafan6fb3e072022-05-20 19:27:23 +010035 }
36
Skyler Greyad002172022-08-16 18:48:26 +010037 async setup(): Promise<Guilds> {
Skyler Grey11236ba2022-08-08 21:13:33 +010038 this.defaultData = (await import("../config/default.json", { assert: { type: "json" } }))
39 .default as unknown as GuildConfig;
40 return this;
pineafan63fc5e22022-08-04 22:04:10 +010041 }
42
Skyler Greyad002172022-08-16 18:48:26 +010043 async read(guild: string): Promise<GuildConfig> {
pineafan63fc5e22022-08-04 22:04:10 +010044 const entry = await this.guilds.findOne({ id: guild });
Skyler Grey11236ba2022-08-08 21:13:33 +010045 return new Proxy(structuredClone(this.defaultData), Entry(entry)) as unknown as GuildConfig;
pineafan6fb3e072022-05-20 19:27:23 +010046 }
47
Skyler Grey11236ba2022-08-08 21:13:33 +010048 async write(guild: string, set: object | null, unset: string[] | string = []) {
pineafan63fc5e22022-08-04 22:04:10 +010049 // eslint-disable-next-line @typescript-eslint/no-explicit-any
50 const uo: Record<string, any> = {};
51 if (!Array.isArray(unset)) unset = [unset];
52 for (const key of unset) {
pineafan0bc04162022-07-25 17:22:26 +010053 uo[key] = null;
pineafan6702cef2022-06-13 17:52:37 +010054 }
Skyler Grey75ea9172022-08-06 10:22:23 +010055 const out = { $set: {}, $unset: {} };
56 if (set) out.$set = set;
57 if (unset.length) out.$unset = uo;
pineafan0bc04162022-07-25 17:22:26 +010058 await this.guilds.updateOne({ id: guild }, out, { upsert: true });
pineafan6702cef2022-06-13 17:52:37 +010059 }
60
pineafan63fc5e22022-08-04 22:04:10 +010061 // eslint-disable-next-line @typescript-eslint/no-explicit-any
pineafan6702cef2022-06-13 17:52:37 +010062 async append(guild: string, key: string, value: any) {
63 if (Array.isArray(value)) {
Skyler Grey75ea9172022-08-06 10:22:23 +010064 await this.guilds.updateOne(
65 { id: guild },
66 {
67 $addToSet: { [key]: { $each: value } }
68 },
69 { upsert: true }
70 );
pineafan6702cef2022-06-13 17:52:37 +010071 } else {
Skyler Grey75ea9172022-08-06 10:22:23 +010072 await this.guilds.updateOne(
73 { id: guild },
74 {
75 $addToSet: { [key]: value }
76 },
77 { upsert: true }
78 );
pineafan6702cef2022-06-13 17:52:37 +010079 }
80 }
81
Skyler Grey75ea9172022-08-06 10:22:23 +010082 async remove(
83 guild: string,
84 key: string,
Skyler Greyc634e2b2022-08-06 17:50:48 +010085 // eslint-disable-next-line @typescript-eslint/no-explicit-any
Skyler Grey75ea9172022-08-06 10:22:23 +010086 value: any,
87 innerKey?: string | null
88 ) {
pineafan63fc5e22022-08-04 22:04:10 +010089 console.log(Array.isArray(value));
pineafan02ba0232022-07-24 22:16:15 +010090 if (innerKey) {
Skyler Grey75ea9172022-08-06 10:22:23 +010091 await this.guilds.updateOne(
92 { id: guild },
93 {
94 $pull: { [key]: { [innerKey]: { $eq: value } } }
95 },
96 { upsert: true }
97 );
pineafan0bc04162022-07-25 17:22:26 +010098 } else if (Array.isArray(value)) {
Skyler Grey75ea9172022-08-06 10:22:23 +010099 await this.guilds.updateOne(
100 { id: guild },
101 {
102 $pullAll: { [key]: value }
103 },
104 { upsert: true }
105 );
pineafan6702cef2022-06-13 17:52:37 +0100106 } else {
Skyler Grey75ea9172022-08-06 10:22:23 +0100107 await this.guilds.updateOne(
108 { id: guild },
109 {
110 $pullAll: { [key]: [value] }
111 },
112 { upsert: true }
113 );
pineafan6702cef2022-06-13 17:52:37 +0100114 }
pineafan6fb3e072022-05-20 19:27:23 +0100115 }
pineafane23c4ec2022-07-27 21:56:27 +0100116
117 async delete(guild: string) {
118 await this.guilds.deleteOne({ id: guild });
119 }
pineafan6fb3e072022-05-20 19:27:23 +0100120}
121
pineafan4edb7762022-06-26 19:21:04 +0100122export class History {
123 histories: Collection<HistorySchema>;
pineafan4edb7762022-06-26 19:21:04 +0100124
pineafan3a02ea32022-08-11 21:35:04 +0100125 constructor() {
pineafan4edb7762022-06-26 19:21:04 +0100126 this.histories = database.collection<HistorySchema>("history");
pineafan4edb7762022-06-26 19:21:04 +0100127 }
128
Skyler Grey75ea9172022-08-06 10:22:23 +0100129 async create(
130 type: string,
131 guild: string,
132 user: Discord.User,
133 moderator: Discord.User | null,
134 reason: string | null,
pineafan3a02ea32022-08-11 21:35:04 +0100135 before?: string | null,
136 after?: string | null,
137 amount?: string | null
Skyler Grey75ea9172022-08-06 10:22:23 +0100138 ) {
pineafan4edb7762022-06-26 19:21:04 +0100139 await this.histories.insertOne({
140 type: type,
141 guild: guild,
142 user: user.id,
pineafan3a02ea32022-08-11 21:35:04 +0100143 moderator: moderator ? moderator.id : null,
pineafan4edb7762022-06-26 19:21:04 +0100144 reason: reason,
145 occurredAt: new Date(),
pineafan3a02ea32022-08-11 21:35:04 +0100146 before: before ?? null,
147 after: after ?? null,
148 amount: amount ?? null
pineafan4edb7762022-06-26 19:21:04 +0100149 });
150 }
151
152 async read(guild: string, user: string, year: number) {
Skyler Grey75ea9172022-08-06 10:22:23 +0100153 const entry = (await this.histories
154 .find({
155 guild: guild,
156 user: user,
157 occurredAt: {
158 $gte: new Date(year - 1, 11, 31, 23, 59, 59),
159 $lt: new Date(year + 1, 0, 1, 0, 0, 0)
160 }
161 })
162 .toArray()) as HistorySchema[];
pineafan4edb7762022-06-26 19:21:04 +0100163 return entry;
164 }
pineafane23c4ec2022-07-27 21:56:27 +0100165
166 async delete(guild: string) {
167 await this.histories.deleteMany({ guild: guild });
168 }
pineafan4edb7762022-06-26 19:21:04 +0100169}
170
171export class ModNotes {
172 modNotes: Collection<ModNoteSchema>;
pineafan4edb7762022-06-26 19:21:04 +0100173
pineafan3a02ea32022-08-11 21:35:04 +0100174 constructor() {
pineafan4edb7762022-06-26 19:21:04 +0100175 this.modNotes = database.collection<ModNoteSchema>("modNotes");
pineafan4edb7762022-06-26 19:21:04 +0100176 }
177
178 async create(guild: string, user: string, note: string | null) {
Skyler Grey11236ba2022-08-08 21:13:33 +0100179 await this.modNotes.updateOne({ guild: guild, user: user }, { $set: { note: note } }, { upsert: true });
pineafan4edb7762022-06-26 19:21:04 +0100180 }
181
182 async read(guild: string, user: string) {
pineafan63fc5e22022-08-04 22:04:10 +0100183 const entry = await this.modNotes.findOne({ guild: guild, user: user });
pineafan4edb7762022-06-26 19:21:04 +0100184 return entry?.note ?? null;
185 }
186}
187
pineafan73a7c4a2022-07-24 10:38:04 +0100188export class Premium {
189 premium: Collection<PremiumSchema>;
pineafan4edb7762022-06-26 19:21:04 +0100190
pineafan3a02ea32022-08-11 21:35:04 +0100191 constructor() {
pineafan73a7c4a2022-07-24 10:38:04 +0100192 this.premium = database.collection<PremiumSchema>("premium");
pineafan4edb7762022-06-26 19:21:04 +0100193 }
194
pineafan73a7c4a2022-07-24 10:38:04 +0100195 async hasPremium(guild: string) {
Skyler Grey75ea9172022-08-06 10:22:23 +0100196 const entry = await this.premium.findOne({
197 appliesTo: { $in: [guild] }
198 });
pineafane23c4ec2022-07-27 21:56:27 +0100199 return entry !== null;
pineafan4edb7762022-06-26 19:21:04 +0100200 }
201}
202
pineafan6fb3e072022-05-20 19:27:23 +0100203export interface GuildConfig {
Skyler Grey75ea9172022-08-06 10:22:23 +0100204 id: string;
205 version: number;
pineafan6fb3e072022-05-20 19:27:23 +0100206 singleEventNotifications: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100207 statsChannelDeleted: boolean;
208 };
pineafan6fb3e072022-05-20 19:27:23 +0100209 filters: {
210 images: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100211 NSFW: boolean;
212 size: boolean;
213 };
214 malware: boolean;
pineafan6fb3e072022-05-20 19:27:23 +0100215 wordFilter: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100216 enabled: boolean;
pineafan6fb3e072022-05-20 19:27:23 +0100217 words: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100218 strict: string[];
219 loose: string[];
220 };
pineafan6fb3e072022-05-20 19:27:23 +0100221 allowed: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100222 users: string[];
223 roles: string[];
224 channels: string[];
225 };
226 };
pineafan6fb3e072022-05-20 19:27:23 +0100227 invite: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100228 enabled: boolean;
229 channels: string[];
230 };
pineafan6fb3e072022-05-20 19:27:23 +0100231 pings: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100232 mass: number;
233 everyone: boolean;
234 roles: boolean;
pineafan6fb3e072022-05-20 19:27:23 +0100235 allowed: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100236 roles: string[];
237 rolesToMention: string[];
238 users: string[];
239 channels: string[];
240 };
241 };
242 };
pineafan6fb3e072022-05-20 19:27:23 +0100243 welcome: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100244 enabled: boolean;
Skyler Grey75ea9172022-08-06 10:22:23 +0100245 role: string | null;
246 ping: string | null;
247 channel: string | null;
248 message: string | null;
249 };
250 stats: Record<string, { name: string; enabled: boolean }>;
pineafan6fb3e072022-05-20 19:27:23 +0100251 logging: {
252 logs: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100253 enabled: boolean;
254 channel: string | null;
Skyler Greyad002172022-08-16 18:48:26 +0100255 toLog: string;
Skyler Grey75ea9172022-08-06 10:22:23 +0100256 };
pineafan6fb3e072022-05-20 19:27:23 +0100257 staff: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100258 channel: string | null;
259 };
pineafan73a7c4a2022-07-24 10:38:04 +0100260 attachments: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100261 channel: string | null;
262 saved: Record<string, string>;
263 };
264 };
pineafan6fb3e072022-05-20 19:27:23 +0100265 verify: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100266 role: string | null;
267 };
pineafan6fb3e072022-05-20 19:27:23 +0100268 tickets: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100269 enabled: boolean;
270 category: string | null;
Skyler Greyad002172022-08-16 18:48:26 +0100271 types: string;
272 customTypes: string[] | null;
Skyler Grey75ea9172022-08-06 10:22:23 +0100273 useCustom: boolean;
274 supportRole: string | null;
275 maxTickets: number;
276 };
pineafan6fb3e072022-05-20 19:27:23 +0100277 moderation: {
278 mute: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100279 timeout: boolean;
280 role: string | null;
281 text: string | null;
282 link: string | null;
283 };
pineafan6fb3e072022-05-20 19:27:23 +0100284 kick: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100285 text: string | null;
286 link: string | null;
287 };
pineafan6fb3e072022-05-20 19:27:23 +0100288 ban: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100289 text: string | null;
290 link: string | null;
291 };
pineafan6fb3e072022-05-20 19:27:23 +0100292 softban: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100293 text: string | null;
294 link: string | null;
295 };
pineafan6fb3e072022-05-20 19:27:23 +0100296 warn: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100297 text: string | null;
298 link: string | null;
299 };
pineafan6fb3e072022-05-20 19:27:23 +0100300 role: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100301 role: string | null;
302 };
303 };
pineafan6fb3e072022-05-20 19:27:23 +0100304 tracks: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100305 name: string;
306 retainPrevious: boolean;
307 nullable: boolean;
308 track: string[];
309 manageableBy: string[];
310 }[];
pineafan6fb3e072022-05-20 19:27:23 +0100311 roleMenu: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100312 enabled: boolean;
313 allowWebUI: boolean;
pineafan6fb3e072022-05-20 19:27:23 +0100314 options: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100315 name: string;
316 description: string;
317 min: number;
318 max: number;
pineafan6fb3e072022-05-20 19:27:23 +0100319 options: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100320 name: string;
321 description: string | null;
322 role: string;
323 }[];
324 }[];
325 };
326 tags: Record<string, string>;
pineafan63fc5e22022-08-04 22:04:10 +0100327}
pineafan4edb7762022-06-26 19:21:04 +0100328
329export interface HistorySchema {
Skyler Grey75ea9172022-08-06 10:22:23 +0100330 type: string;
331 guild: string;
332 user: string;
333 moderator: string | null;
pineafan3a02ea32022-08-11 21:35:04 +0100334 reason: string | null;
Skyler Grey75ea9172022-08-06 10:22:23 +0100335 occurredAt: Date;
336 before: string | null;
337 after: string | null;
338 amount: string | null;
pineafan4edb7762022-06-26 19:21:04 +0100339}
340
341export interface ModNoteSchema {
Skyler Grey75ea9172022-08-06 10:22:23 +0100342 guild: string;
343 user: string;
pineafan3a02ea32022-08-11 21:35:04 +0100344 note: string | null;
pineafan4edb7762022-06-26 19:21:04 +0100345}
346
pineafan73a7c4a2022-07-24 10:38:04 +0100347export interface PremiumSchema {
Skyler Grey75ea9172022-08-06 10:22:23 +0100348 user: string;
349 level: number;
350 expires: Date;
351 appliesTo: string[];
352}