blob: 6eb735e84ba2fa8110dd888871ad2e4e299a0db9 [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 };
pineafan6fb3e072022-05-20 19:27:23 +0100343 welcome: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100344 enabled: boolean;
Skyler Grey75ea9172022-08-06 10:22:23 +0100345 role: string | null;
346 ping: string | null;
347 channel: string | null;
348 message: string | null;
349 };
350 stats: Record<string, { name: string; enabled: boolean }>;
pineafan6fb3e072022-05-20 19:27:23 +0100351 logging: {
352 logs: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100353 enabled: boolean;
354 channel: string | null;
Skyler Greyad002172022-08-16 18:48:26 +0100355 toLog: string;
Skyler Grey75ea9172022-08-06 10:22:23 +0100356 };
pineafan6fb3e072022-05-20 19:27:23 +0100357 staff: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100358 channel: string | null;
359 };
pineafan73a7c4a2022-07-24 10:38:04 +0100360 attachments: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100361 channel: string | null;
362 saved: Record<string, string>;
363 };
364 };
pineafan6fb3e072022-05-20 19:27:23 +0100365 verify: {
PineaFandf4996f2023-01-01 14:20:06 +0000366 enabled: boolean;
Skyler Grey75ea9172022-08-06 10:22:23 +0100367 role: string | null;
368 };
pineafan6fb3e072022-05-20 19:27:23 +0100369 tickets: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100370 enabled: boolean;
371 category: string | null;
Skyler Greyad002172022-08-16 18:48:26 +0100372 types: string;
373 customTypes: string[] | null;
Skyler Grey75ea9172022-08-06 10:22:23 +0100374 useCustom: boolean;
375 supportRole: string | null;
376 maxTickets: number;
377 };
pineafan6fb3e072022-05-20 19:27:23 +0100378 moderation: {
379 mute: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100380 timeout: boolean;
381 role: string | null;
382 text: string | null;
383 link: string | null;
384 };
pineafan6fb3e072022-05-20 19:27:23 +0100385 kick: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100386 text: string | null;
387 link: string | null;
388 };
pineafan6fb3e072022-05-20 19:27:23 +0100389 ban: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100390 text: string | null;
391 link: string | null;
392 };
pineafan6fb3e072022-05-20 19:27:23 +0100393 softban: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100394 text: string | null;
395 link: string | null;
396 };
pineafan6fb3e072022-05-20 19:27:23 +0100397 warn: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100398 text: string | null;
399 link: string | null;
400 };
pineafan6fb3e072022-05-20 19:27:23 +0100401 role: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100402 role: string | null;
TheCodedProfd9636e82023-01-17 22:13:06 -0500403 text: null;
404 link: null;
Skyler Grey75ea9172022-08-06 10:22:23 +0100405 };
PineaFane6ba7882023-01-18 20:41:16 +0000406 nick: {
407 text: string | null;
408 link: string | null;
409 }
Skyler Grey75ea9172022-08-06 10:22:23 +0100410 };
pineafan6fb3e072022-05-20 19:27:23 +0100411 tracks: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100412 name: string;
413 retainPrevious: boolean;
414 nullable: boolean;
415 track: string[];
416 manageableBy: string[];
417 }[];
pineafan6fb3e072022-05-20 19:27:23 +0100418 roleMenu: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100419 enabled: boolean;
420 allowWebUI: boolean;
pineafan6fb3e072022-05-20 19:27:23 +0100421 options: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100422 name: string;
423 description: string;
424 min: number;
425 max: number;
pineafan6fb3e072022-05-20 19:27:23 +0100426 options: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100427 name: string;
428 description: string | null;
429 role: string;
430 }[];
431 }[];
432 };
433 tags: Record<string, string>;
pineafan63fc5e22022-08-04 22:04:10 +0100434}
pineafan4edb7762022-06-26 19:21:04 +0100435
436export interface HistorySchema {
Skyler Grey75ea9172022-08-06 10:22:23 +0100437 type: string;
438 guild: string;
439 user: string;
440 moderator: string | null;
pineafan3a02ea32022-08-11 21:35:04 +0100441 reason: string | null;
Skyler Grey75ea9172022-08-06 10:22:23 +0100442 occurredAt: Date;
443 before: string | null;
444 after: string | null;
445 amount: string | null;
pineafan4edb7762022-06-26 19:21:04 +0100446}
447
448export interface ModNoteSchema {
Skyler Grey75ea9172022-08-06 10:22:23 +0100449 guild: string;
450 user: string;
pineafan3a02ea32022-08-11 21:35:04 +0100451 note: string | null;
pineafan4edb7762022-06-26 19:21:04 +0100452}
453
pineafan73a7c4a2022-07-24 10:38:04 +0100454export interface PremiumSchema {
Skyler Grey75ea9172022-08-06 10:22:23 +0100455 user: string;
456 level: number;
Skyler Grey75ea9172022-08-06 10:22:23 +0100457 appliesTo: string[];
TheCodedProf633866f2023-02-03 17:06:00 -0500458 expiresAt?: number;
Skyler Grey75ea9172022-08-06 10:22:23 +0100459}