blob: aa7fd36ba9610559eed92b45f2f3805dfd83c214 [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 +01003import config from "../config/main.json" assert { type: "json" };
TheCodedProf633866f2023-02-03 17:06:00 -05004import client from "../utils/client.js";
pineafan4edb7762022-06-26 19:21:04 +01005
6const mongoClient = new MongoClient(config.mongoUrl);
pineafan63fc5e22022-08-04 22:04:10 +01007await mongoClient.connect();
pineafan4edb7762022-06-26 19:21:04 +01008const database = mongoClient.db("Nucleus");
pineafan6fb3e072022-05-20 19:27:23 +01009
pineafan4edb7762022-06-26 19:21:04 +010010export class Guilds {
pineafan6fb3e072022-05-20 19:27:23 +010011 guilds: Collection<GuildConfig>;
pineafan63fc5e22022-08-04 22:04:10 +010012 defaultData: GuildConfig | null;
13
14 constructor() {
pineafan4edb7762022-06-26 19:21:04 +010015 this.guilds = database.collection<GuildConfig>("guilds");
pineafan63fc5e22022-08-04 22:04:10 +010016 this.defaultData = null;
pineafan6fb3e072022-05-20 19:27:23 +010017 }
18
Skyler Greyad002172022-08-16 18:48:26 +010019 async setup(): Promise<Guilds> {
Skyler Grey11236ba2022-08-08 21:13:33 +010020 this.defaultData = (await import("../config/default.json", { assert: { type: "json" } }))
21 .default as unknown as GuildConfig;
22 return this;
pineafan63fc5e22022-08-04 22:04:10 +010023 }
24
Skyler Greyad002172022-08-16 18:48:26 +010025 async read(guild: string): Promise<GuildConfig> {
pineafan63fc5e22022-08-04 22:04:10 +010026 const entry = await this.guilds.findOne({ id: guild });
PineaFandf4996f2023-01-01 14:20:06 +000027 return Object.assign({}, this.defaultData, entry);
pineafan6fb3e072022-05-20 19:27:23 +010028 }
29
Skyler Grey11236ba2022-08-08 21:13:33 +010030 async write(guild: string, set: object | null, unset: string[] | string = []) {
pineafan63fc5e22022-08-04 22:04:10 +010031 // eslint-disable-next-line @typescript-eslint/no-explicit-any
32 const uo: Record<string, any> = {};
33 if (!Array.isArray(unset)) unset = [unset];
34 for (const key of unset) {
pineafan0bc04162022-07-25 17:22:26 +010035 uo[key] = null;
pineafan6702cef2022-06-13 17:52:37 +010036 }
Skyler Grey75ea9172022-08-06 10:22:23 +010037 const out = { $set: {}, $unset: {} };
38 if (set) out.$set = set;
39 if (unset.length) out.$unset = uo;
pineafan0bc04162022-07-25 17:22:26 +010040 await this.guilds.updateOne({ id: guild }, out, { upsert: true });
pineafan6702cef2022-06-13 17:52:37 +010041 }
42
pineafan63fc5e22022-08-04 22:04:10 +010043 // eslint-disable-next-line @typescript-eslint/no-explicit-any
pineafan6702cef2022-06-13 17:52:37 +010044 async append(guild: string, key: string, value: any) {
45 if (Array.isArray(value)) {
Skyler Grey75ea9172022-08-06 10:22:23 +010046 await this.guilds.updateOne(
47 { id: guild },
48 {
49 $addToSet: { [key]: { $each: value } }
50 },
51 { upsert: true }
52 );
pineafan6702cef2022-06-13 17:52:37 +010053 } else {
Skyler Grey75ea9172022-08-06 10:22:23 +010054 await this.guilds.updateOne(
55 { id: guild },
56 {
57 $addToSet: { [key]: value }
58 },
59 { upsert: true }
60 );
pineafan6702cef2022-06-13 17:52:37 +010061 }
62 }
63
Skyler Grey75ea9172022-08-06 10:22:23 +010064 async remove(
65 guild: string,
66 key: string,
Skyler Greyc634e2b2022-08-06 17:50:48 +010067 // eslint-disable-next-line @typescript-eslint/no-explicit-any
Skyler Grey75ea9172022-08-06 10:22:23 +010068 value: any,
69 innerKey?: string | null
70 ) {
pineafan02ba0232022-07-24 22:16:15 +010071 if (innerKey) {
Skyler Grey75ea9172022-08-06 10:22:23 +010072 await this.guilds.updateOne(
73 { id: guild },
74 {
75 $pull: { [key]: { [innerKey]: { $eq: value } } }
76 },
77 { upsert: true }
78 );
pineafan0bc04162022-07-25 17:22:26 +010079 } else if (Array.isArray(value)) {
Skyler Grey75ea9172022-08-06 10:22:23 +010080 await this.guilds.updateOne(
81 { id: guild },
82 {
83 $pullAll: { [key]: value }
84 },
85 { upsert: true }
86 );
pineafan6702cef2022-06-13 17:52:37 +010087 } else {
Skyler Grey75ea9172022-08-06 10:22:23 +010088 await this.guilds.updateOne(
89 { id: guild },
90 {
91 $pullAll: { [key]: [value] }
92 },
93 { upsert: true }
94 );
pineafan6702cef2022-06-13 17:52:37 +010095 }
pineafan6fb3e072022-05-20 19:27:23 +010096 }
pineafane23c4ec2022-07-27 21:56:27 +010097
98 async delete(guild: string) {
99 await this.guilds.deleteOne({ id: guild });
100 }
pineafan6fb3e072022-05-20 19:27:23 +0100101}
102
pineafan4edb7762022-06-26 19:21:04 +0100103export class History {
104 histories: Collection<HistorySchema>;
pineafan4edb7762022-06-26 19:21:04 +0100105
pineafan3a02ea32022-08-11 21:35:04 +0100106 constructor() {
pineafan4edb7762022-06-26 19:21:04 +0100107 this.histories = database.collection<HistorySchema>("history");
pineafan4edb7762022-06-26 19:21:04 +0100108 }
109
Skyler Grey75ea9172022-08-06 10:22:23 +0100110 async create(
111 type: string,
112 guild: string,
113 user: Discord.User,
114 moderator: Discord.User | null,
115 reason: string | null,
pineafan3a02ea32022-08-11 21:35:04 +0100116 before?: string | null,
117 after?: string | null,
118 amount?: string | null
Skyler Grey75ea9172022-08-06 10:22:23 +0100119 ) {
pineafan4edb7762022-06-26 19:21:04 +0100120 await this.histories.insertOne({
121 type: type,
122 guild: guild,
123 user: user.id,
pineafan3a02ea32022-08-11 21:35:04 +0100124 moderator: moderator ? moderator.id : null,
pineafan4edb7762022-06-26 19:21:04 +0100125 reason: reason,
126 occurredAt: new Date(),
pineafan3a02ea32022-08-11 21:35:04 +0100127 before: before ?? null,
128 after: after ?? null,
129 amount: amount ?? null
pineafan4edb7762022-06-26 19:21:04 +0100130 });
131 }
132
133 async read(guild: string, user: string, year: number) {
Skyler Grey75ea9172022-08-06 10:22:23 +0100134 const entry = (await this.histories
135 .find({
136 guild: guild,
137 user: user,
138 occurredAt: {
139 $gte: new Date(year - 1, 11, 31, 23, 59, 59),
140 $lt: new Date(year + 1, 0, 1, 0, 0, 0)
141 }
142 })
143 .toArray()) as HistorySchema[];
pineafan4edb7762022-06-26 19:21:04 +0100144 return entry;
145 }
pineafane23c4ec2022-07-27 21:56:27 +0100146
147 async delete(guild: string) {
148 await this.histories.deleteMany({ guild: guild });
149 }
pineafan4edb7762022-06-26 19:21:04 +0100150}
151
TheCodedProfb5e9d552023-01-29 15:43:26 -0500152interface ScanCacheSchema {
153 addedAt: Date;
154 hash: string;
155 data: boolean;
156 tags: string[];
157}
158
159export class ScanCache {
160 scanCache: Collection<ScanCacheSchema>;
161
162 constructor() {
163 this.scanCache = database.collection<ScanCacheSchema>("scanCache");
164 }
165
166 async read(hash: string) {
167 return await this.scanCache.findOne({ hash: hash });
168 }
169
170 async write(hash: string, data: boolean, tags?: string[]) {
TheCodedProf1f675042023-02-16 17:01:29 -0500171 await this.scanCache.insertOne({ hash: hash, data: data, tags: tags ?? [], addedAt: new Date() });
TheCodedProfb5e9d552023-01-29 15:43:26 -0500172 }
173
174 async cleanup() {
175 await this.scanCache.deleteMany({ addedAt: { $lt: new Date(Date.now() - (1000 * 60 * 60 * 24 * 31)) }, hash: { $not$text: "http"} });
176 }
177}
178
PineaFan538d3752023-01-12 21:48:23 +0000179export class PerformanceTest {
180 performanceData: Collection<PerformanceDataSchema>;
181
182 constructor() {
183 this.performanceData = database.collection<PerformanceDataSchema>("performance");
184 }
185
186 async record(data: PerformanceDataSchema) {
187 data.timestamp = new Date();
188 await this.performanceData.insertOne(data);
189 }
190 async read() {
191 return await this.performanceData.find({}).toArray();
192 }
193}
194
195export interface PerformanceDataSchema {
196 timestamp?: Date;
197 discord: number;
198 databaseRead: number;
199 resources: {
200 cpu: number;
201 memory: number;
202 temperature: number;
203 }
204}
205
pineafan4edb7762022-06-26 19:21:04 +0100206export class ModNotes {
207 modNotes: Collection<ModNoteSchema>;
pineafan4edb7762022-06-26 19:21:04 +0100208
pineafan3a02ea32022-08-11 21:35:04 +0100209 constructor() {
pineafan4edb7762022-06-26 19:21:04 +0100210 this.modNotes = database.collection<ModNoteSchema>("modNotes");
pineafan4edb7762022-06-26 19:21:04 +0100211 }
212
213 async create(guild: string, user: string, note: string | null) {
Skyler Grey11236ba2022-08-08 21:13:33 +0100214 await this.modNotes.updateOne({ guild: guild, user: user }, { $set: { note: note } }, { upsert: true });
pineafan4edb7762022-06-26 19:21:04 +0100215 }
216
217 async read(guild: string, user: string) {
pineafan63fc5e22022-08-04 22:04:10 +0100218 const entry = await this.modNotes.findOne({ guild: guild, user: user });
pineafan4edb7762022-06-26 19:21:04 +0100219 return entry?.note ?? null;
220 }
TheCodedProf267563a2023-01-21 17:00:57 -0500221
222 async delete(guild: string) {
223 await this.modNotes.deleteMany({ guild: guild });
224 }
pineafan4edb7762022-06-26 19:21:04 +0100225}
226
pineafan73a7c4a2022-07-24 10:38:04 +0100227export class Premium {
228 premium: Collection<PremiumSchema>;
pineafan4edb7762022-06-26 19:21:04 +0100229
pineafan3a02ea32022-08-11 21:35:04 +0100230 constructor() {
pineafan73a7c4a2022-07-24 10:38:04 +0100231 this.premium = database.collection<PremiumSchema>("premium");
pineafan4edb7762022-06-26 19:21:04 +0100232 }
233
TheCodedProf633866f2023-02-03 17:06:00 -0500234 async updateUser(user: string, level: number) {
235 if(!(await this.userExists(user))) await this.createUser(user, level);
236 await this.premium.updateOne({ user: user }, { $set: { level: level } }, { upsert: true });
237 }
238
239 async userExists(user: string): Promise<boolean> {
240 const entry = await this.premium.findOne({ user: user });
241 return entry ? true : false;
242 }
243
244 async createUser(user: string, level: number) {
245 await this.premium.insertOne({ user: user, appliesTo: [], level: level });
246 }
247
pineafan73a7c4a2022-07-24 10:38:04 +0100248 async hasPremium(guild: string) {
Skyler Grey75ea9172022-08-06 10:22:23 +0100249 const entry = await this.premium.findOne({
250 appliesTo: { $in: [guild] }
251 });
TheCodedProf633866f2023-02-03 17:06:00 -0500252 return entry ? true : false;
TheCodedProf267563a2023-01-21 17:00:57 -0500253 }
254
TheCodedProf633866f2023-02-03 17:06:00 -0500255 async fetchUser(user: string): Promise<PremiumSchema | null> {
TheCodedProf267563a2023-01-21 17:00:57 -0500256 const entry = await this.premium.findOne({ user: user });
TheCodedProf633866f2023-02-03 17:06:00 -0500257 if (!entry) return null;
258 return entry;
259 }
260
261 async checkAllPremium() {
262 const entries = await this.premium.find({}).toArray();
263 for(const {user, expiresAt} of entries) {
264 if(expiresAt) expiresAt < new Date().getTime() ? await this.premium.deleteOne({user: user}) : null;
265 const member = await (await client.guilds.fetch("684492926528651336")).members.fetch(user)
266 let level: number = 0;
267 if (member.roles.cache.has("1066468879309750313")) {
268 level = 99;
269 } else if (member.roles.cache.has("1066465491713003520")) {
270 level = 1;
271 } else if (member.roles.cache.has("1066439526496604194")) {
272 level = 2;
273 } else if (member.roles.cache.has("1066464134322978912")) {
274 level = 3;
275 }
276
277 if (level > 0) {
278 await this.updateUser(user, level);
279 } else {
280 await this.premium.updateOne({ user: user }, { expiresAt: (new Date().getTime() + (1000*60*60*24*3)) })
281 }
282 }
TheCodedProf267563a2023-01-21 17:00:57 -0500283 }
284
TheCodedProffc420b72023-01-24 17:14:38 -0500285 addPremium(user: string, guild: string) {
TheCodedProf267563a2023-01-21 17:00:57 -0500286 return this.premium.updateOne({ user: user }, { $addToSet: { appliesTo: guild } }, { upsert: true });
pineafan4edb7762022-06-26 19:21:04 +0100287 }
TheCodedProffc420b72023-01-24 17:14:38 -0500288
289 removePremium(user: string, guild: string) {
290 return this.premium.updateOne({ user: user }, { $pull: { appliesTo: guild } });
291 }
pineafan4edb7762022-06-26 19:21:04 +0100292}
293
pineafan6fb3e072022-05-20 19:27:23 +0100294export interface GuildConfig {
Skyler Grey75ea9172022-08-06 10:22:23 +0100295 id: string;
296 version: number;
PineaFan100df682023-01-02 13:26:08 +0000297 singleEventNotifications: Record<string, boolean>;
pineafan6fb3e072022-05-20 19:27:23 +0100298 filters: {
299 images: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100300 NSFW: boolean;
301 size: boolean;
302 };
303 malware: boolean;
pineafan6fb3e072022-05-20 19:27:23 +0100304 wordFilter: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100305 enabled: boolean;
pineafan6fb3e072022-05-20 19:27:23 +0100306 words: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100307 strict: string[];
308 loose: string[];
309 };
pineafan6fb3e072022-05-20 19:27:23 +0100310 allowed: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100311 users: string[];
312 roles: string[];
313 channels: string[];
314 };
315 };
pineafan6fb3e072022-05-20 19:27:23 +0100316 invite: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100317 enabled: boolean;
PineaFan538d3752023-01-12 21:48:23 +0000318 allowed: {
319 channels: string[];
320 roles: string[];
321 users: string[];
322 };
Skyler Grey75ea9172022-08-06 10:22:23 +0100323 };
pineafan6fb3e072022-05-20 19:27:23 +0100324 pings: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100325 mass: number;
326 everyone: boolean;
327 roles: boolean;
pineafan6fb3e072022-05-20 19:27:23 +0100328 allowed: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100329 roles: string[];
330 rolesToMention: string[];
331 users: string[];
332 channels: string[];
333 };
334 };
TheCodedProfad0b8202023-02-14 14:27:09 -0500335 clean: {
336 channels: string[];
337 allowed: {
338 user: string[];
339 roles: string[];
340 }
341 }
Skyler Grey75ea9172022-08-06 10:22:23 +0100342 };
TheCodedProfbaee2c12023-02-18 16:11:06 -0500343 autoPublish: {
344 enabled: boolean;
345 channels: string[];
346 }
pineafan6fb3e072022-05-20 19:27:23 +0100347 welcome: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100348 enabled: boolean;
Skyler Grey75ea9172022-08-06 10:22:23 +0100349 role: string | null;
350 ping: string | null;
351 channel: string | null;
352 message: string | null;
353 };
354 stats: Record<string, { name: string; enabled: boolean }>;
pineafan6fb3e072022-05-20 19:27:23 +0100355 logging: {
356 logs: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100357 enabled: boolean;
358 channel: string | null;
Skyler Greyad002172022-08-16 18:48:26 +0100359 toLog: string;
Skyler Grey75ea9172022-08-06 10:22:23 +0100360 };
pineafan6fb3e072022-05-20 19:27:23 +0100361 staff: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100362 channel: string | null;
363 };
pineafan73a7c4a2022-07-24 10:38:04 +0100364 attachments: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100365 channel: string | null;
366 saved: Record<string, string>;
367 };
368 };
pineafan6fb3e072022-05-20 19:27:23 +0100369 verify: {
PineaFandf4996f2023-01-01 14:20:06 +0000370 enabled: boolean;
Skyler Grey75ea9172022-08-06 10:22:23 +0100371 role: string | null;
372 };
pineafan6fb3e072022-05-20 19:27:23 +0100373 tickets: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100374 enabled: boolean;
375 category: string | null;
Skyler Greyad002172022-08-16 18:48:26 +0100376 types: string;
377 customTypes: string[] | null;
Skyler Grey75ea9172022-08-06 10:22:23 +0100378 useCustom: boolean;
379 supportRole: string | null;
380 maxTickets: number;
381 };
pineafan6fb3e072022-05-20 19:27:23 +0100382 moderation: {
383 mute: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100384 timeout: boolean;
385 role: string | null;
386 text: string | null;
387 link: string | null;
388 };
pineafan6fb3e072022-05-20 19:27:23 +0100389 kick: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100390 text: string | null;
391 link: string | null;
392 };
pineafan6fb3e072022-05-20 19:27:23 +0100393 ban: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100394 text: string | null;
395 link: string | null;
396 };
pineafan6fb3e072022-05-20 19:27:23 +0100397 softban: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100398 text: string | null;
399 link: string | null;
400 };
pineafan6fb3e072022-05-20 19:27:23 +0100401 warn: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100402 text: string | null;
403 link: string | null;
404 };
pineafan6fb3e072022-05-20 19:27:23 +0100405 role: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100406 role: string | null;
TheCodedProfd9636e82023-01-17 22:13:06 -0500407 text: null;
408 link: null;
Skyler Grey75ea9172022-08-06 10:22:23 +0100409 };
PineaFane6ba7882023-01-18 20:41:16 +0000410 nick: {
411 text: string | null;
412 link: string | null;
413 }
Skyler Grey75ea9172022-08-06 10:22:23 +0100414 };
pineafan6fb3e072022-05-20 19:27:23 +0100415 tracks: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100416 name: string;
417 retainPrevious: boolean;
418 nullable: boolean;
419 track: string[];
420 manageableBy: string[];
421 }[];
pineafan6fb3e072022-05-20 19:27:23 +0100422 roleMenu: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100423 enabled: boolean;
424 allowWebUI: boolean;
pineafan6fb3e072022-05-20 19:27:23 +0100425 options: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100426 name: string;
427 description: string;
428 min: number;
429 max: number;
pineafan6fb3e072022-05-20 19:27:23 +0100430 options: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100431 name: string;
432 description: string | null;
433 role: string;
434 }[];
435 }[];
436 };
437 tags: Record<string, string>;
pineafan63fc5e22022-08-04 22:04:10 +0100438}
pineafan4edb7762022-06-26 19:21:04 +0100439
440export interface HistorySchema {
Skyler Grey75ea9172022-08-06 10:22:23 +0100441 type: string;
442 guild: string;
443 user: string;
444 moderator: string | null;
pineafan3a02ea32022-08-11 21:35:04 +0100445 reason: string | null;
Skyler Grey75ea9172022-08-06 10:22:23 +0100446 occurredAt: Date;
447 before: string | null;
448 after: string | null;
449 amount: string | null;
pineafan4edb7762022-06-26 19:21:04 +0100450}
451
452export interface ModNoteSchema {
Skyler Grey75ea9172022-08-06 10:22:23 +0100453 guild: string;
454 user: string;
pineafan3a02ea32022-08-11 21:35:04 +0100455 note: string | null;
pineafan4edb7762022-06-26 19:21:04 +0100456}
457
pineafan73a7c4a2022-07-24 10:38:04 +0100458export interface PremiumSchema {
Skyler Grey75ea9172022-08-06 10:22:23 +0100459 user: string;
460 level: number;
Skyler Grey75ea9172022-08-06 10:22:23 +0100461 appliesTo: string[];
TheCodedProf633866f2023-02-03 17:06:00 -0500462 expiresAt?: number;
Skyler Grey75ea9172022-08-06 10:22:23 +0100463}