blob: c7b1777d417be81025dfe9c491eb4c6ef653411d [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" };
pineafan4edb7762022-06-26 19:21:04 +01004
5const mongoClient = new MongoClient(config.mongoUrl);
pineafan63fc5e22022-08-04 22:04:10 +01006await mongoClient.connect();
pineafan4edb7762022-06-26 19:21:04 +01007const database = mongoClient.db("Nucleus");
pineafan6fb3e072022-05-20 19:27:23 +01008
pineafan4edb7762022-06-26 19:21:04 +01009export class Guilds {
pineafan6fb3e072022-05-20 19:27:23 +010010 guilds: Collection<GuildConfig>;
pineafan63fc5e22022-08-04 22:04:10 +010011 defaultData: GuildConfig | null;
12
13 constructor() {
pineafan4edb7762022-06-26 19:21:04 +010014 this.guilds = database.collection<GuildConfig>("guilds");
pineafan63fc5e22022-08-04 22:04:10 +010015 this.defaultData = null;
pineafan6fb3e072022-05-20 19:27:23 +010016 }
17
Skyler Greyad002172022-08-16 18:48:26 +010018 async setup(): Promise<Guilds> {
Skyler Grey11236ba2022-08-08 21:13:33 +010019 this.defaultData = (await import("../config/default.json", { assert: { type: "json" } }))
20 .default as unknown as GuildConfig;
21 return this;
pineafan63fc5e22022-08-04 22:04:10 +010022 }
23
Skyler Greyad002172022-08-16 18:48:26 +010024 async read(guild: string): Promise<GuildConfig> {
pineafan63fc5e22022-08-04 22:04:10 +010025 const entry = await this.guilds.findOne({ id: guild });
PineaFandf4996f2023-01-01 14:20:06 +000026 return Object.assign({}, this.defaultData, entry);
pineafan6fb3e072022-05-20 19:27:23 +010027 }
28
Skyler Grey11236ba2022-08-08 21:13:33 +010029 async write(guild: string, set: object | null, unset: string[] | string = []) {
pineafan63fc5e22022-08-04 22:04:10 +010030 // eslint-disable-next-line @typescript-eslint/no-explicit-any
31 const uo: Record<string, any> = {};
32 if (!Array.isArray(unset)) unset = [unset];
33 for (const key of unset) {
pineafan0bc04162022-07-25 17:22:26 +010034 uo[key] = null;
pineafan6702cef2022-06-13 17:52:37 +010035 }
Skyler Grey75ea9172022-08-06 10:22:23 +010036 const out = { $set: {}, $unset: {} };
37 if (set) out.$set = set;
38 if (unset.length) out.$unset = uo;
pineafan0bc04162022-07-25 17:22:26 +010039 await this.guilds.updateOne({ id: guild }, out, { upsert: true });
pineafan6702cef2022-06-13 17:52:37 +010040 }
41
pineafan63fc5e22022-08-04 22:04:10 +010042 // eslint-disable-next-line @typescript-eslint/no-explicit-any
pineafan6702cef2022-06-13 17:52:37 +010043 async append(guild: string, key: string, value: any) {
44 if (Array.isArray(value)) {
Skyler Grey75ea9172022-08-06 10:22:23 +010045 await this.guilds.updateOne(
46 { id: guild },
47 {
48 $addToSet: { [key]: { $each: value } }
49 },
50 { upsert: true }
51 );
pineafan6702cef2022-06-13 17:52:37 +010052 } else {
Skyler Grey75ea9172022-08-06 10:22:23 +010053 await this.guilds.updateOne(
54 { id: guild },
55 {
56 $addToSet: { [key]: value }
57 },
58 { upsert: true }
59 );
pineafan6702cef2022-06-13 17:52:37 +010060 }
61 }
62
Skyler Grey75ea9172022-08-06 10:22:23 +010063 async remove(
64 guild: string,
65 key: string,
Skyler Greyc634e2b2022-08-06 17:50:48 +010066 // eslint-disable-next-line @typescript-eslint/no-explicit-any
Skyler Grey75ea9172022-08-06 10:22:23 +010067 value: any,
68 innerKey?: string | null
69 ) {
pineafan02ba0232022-07-24 22:16:15 +010070 if (innerKey) {
Skyler Grey75ea9172022-08-06 10:22:23 +010071 await this.guilds.updateOne(
72 { id: guild },
73 {
74 $pull: { [key]: { [innerKey]: { $eq: value } } }
75 },
76 { upsert: true }
77 );
pineafan0bc04162022-07-25 17:22:26 +010078 } else if (Array.isArray(value)) {
Skyler Grey75ea9172022-08-06 10:22:23 +010079 await this.guilds.updateOne(
80 { id: guild },
81 {
82 $pullAll: { [key]: value }
83 },
84 { upsert: true }
85 );
pineafan6702cef2022-06-13 17:52:37 +010086 } else {
Skyler Grey75ea9172022-08-06 10:22:23 +010087 await this.guilds.updateOne(
88 { id: guild },
89 {
90 $pullAll: { [key]: [value] }
91 },
92 { upsert: true }
93 );
pineafan6702cef2022-06-13 17:52:37 +010094 }
pineafan6fb3e072022-05-20 19:27:23 +010095 }
pineafane23c4ec2022-07-27 21:56:27 +010096
97 async delete(guild: string) {
98 await this.guilds.deleteOne({ id: guild });
99 }
pineafan6fb3e072022-05-20 19:27:23 +0100100}
101
pineafan4edb7762022-06-26 19:21:04 +0100102export class History {
103 histories: Collection<HistorySchema>;
pineafan4edb7762022-06-26 19:21:04 +0100104
pineafan3a02ea32022-08-11 21:35:04 +0100105 constructor() {
pineafan4edb7762022-06-26 19:21:04 +0100106 this.histories = database.collection<HistorySchema>("history");
pineafan4edb7762022-06-26 19:21:04 +0100107 }
108
Skyler Grey75ea9172022-08-06 10:22:23 +0100109 async create(
110 type: string,
111 guild: string,
112 user: Discord.User,
113 moderator: Discord.User | null,
114 reason: string | null,
pineafan3a02ea32022-08-11 21:35:04 +0100115 before?: string | null,
116 after?: string | null,
117 amount?: string | null
Skyler Grey75ea9172022-08-06 10:22:23 +0100118 ) {
pineafan4edb7762022-06-26 19:21:04 +0100119 await this.histories.insertOne({
120 type: type,
121 guild: guild,
122 user: user.id,
pineafan3a02ea32022-08-11 21:35:04 +0100123 moderator: moderator ? moderator.id : null,
pineafan4edb7762022-06-26 19:21:04 +0100124 reason: reason,
125 occurredAt: new Date(),
pineafan3a02ea32022-08-11 21:35:04 +0100126 before: before ?? null,
127 after: after ?? null,
128 amount: amount ?? null
pineafan4edb7762022-06-26 19:21:04 +0100129 });
130 }
131
132 async read(guild: string, user: string, year: number) {
Skyler Grey75ea9172022-08-06 10:22:23 +0100133 const entry = (await this.histories
134 .find({
135 guild: guild,
136 user: user,
137 occurredAt: {
138 $gte: new Date(year - 1, 11, 31, 23, 59, 59),
139 $lt: new Date(year + 1, 0, 1, 0, 0, 0)
140 }
141 })
142 .toArray()) as HistorySchema[];
pineafan4edb7762022-06-26 19:21:04 +0100143 return entry;
144 }
pineafane23c4ec2022-07-27 21:56:27 +0100145
146 async delete(guild: string) {
147 await this.histories.deleteMany({ guild: guild });
148 }
pineafan4edb7762022-06-26 19:21:04 +0100149}
150
TheCodedProfb5e9d552023-01-29 15:43:26 -0500151interface ScanCacheSchema {
152 addedAt: Date;
153 hash: string;
154 data: boolean;
155 tags: string[];
156}
157
158export class ScanCache {
159 scanCache: Collection<ScanCacheSchema>;
160
161 constructor() {
162 this.scanCache = database.collection<ScanCacheSchema>("scanCache");
163 }
164
165 async read(hash: string) {
166 return await this.scanCache.findOne({ hash: hash });
167 }
168
169 async write(hash: string, data: boolean, tags?: string[]) {
170 await this.scanCache.insertOne({ hash: hash, data: data, tags: tags ?? [], addedAt: new Date() }); // TODO: cleanup function maybe
171 }
172
173 async cleanup() {
174 await this.scanCache.deleteMany({ addedAt: { $lt: new Date(Date.now() - (1000 * 60 * 60 * 24 * 31)) }, hash: { $not$text: "http"} });
175 }
176}
177
PineaFan538d3752023-01-12 21:48:23 +0000178export class PerformanceTest {
179 performanceData: Collection<PerformanceDataSchema>;
180
181 constructor() {
182 this.performanceData = database.collection<PerformanceDataSchema>("performance");
183 }
184
185 async record(data: PerformanceDataSchema) {
186 data.timestamp = new Date();
187 await this.performanceData.insertOne(data);
188 }
189 async read() {
190 return await this.performanceData.find({}).toArray();
191 }
192}
193
194export interface PerformanceDataSchema {
195 timestamp?: Date;
196 discord: number;
197 databaseRead: number;
198 resources: {
199 cpu: number;
200 memory: number;
201 temperature: number;
202 }
203}
204
pineafan4edb7762022-06-26 19:21:04 +0100205export class ModNotes {
206 modNotes: Collection<ModNoteSchema>;
pineafan4edb7762022-06-26 19:21:04 +0100207
pineafan3a02ea32022-08-11 21:35:04 +0100208 constructor() {
pineafan4edb7762022-06-26 19:21:04 +0100209 this.modNotes = database.collection<ModNoteSchema>("modNotes");
pineafan4edb7762022-06-26 19:21:04 +0100210 }
211
212 async create(guild: string, user: string, note: string | null) {
Skyler Grey11236ba2022-08-08 21:13:33 +0100213 await this.modNotes.updateOne({ guild: guild, user: user }, { $set: { note: note } }, { upsert: true });
pineafan4edb7762022-06-26 19:21:04 +0100214 }
215
216 async read(guild: string, user: string) {
pineafan63fc5e22022-08-04 22:04:10 +0100217 const entry = await this.modNotes.findOne({ guild: guild, user: user });
pineafan4edb7762022-06-26 19:21:04 +0100218 return entry?.note ?? null;
219 }
TheCodedProf267563a2023-01-21 17:00:57 -0500220
221 async delete(guild: string) {
222 await this.modNotes.deleteMany({ guild: guild });
223 }
pineafan4edb7762022-06-26 19:21:04 +0100224}
225
pineafan73a7c4a2022-07-24 10:38:04 +0100226export class Premium {
227 premium: Collection<PremiumSchema>;
pineafan4edb7762022-06-26 19:21:04 +0100228
pineafan3a02ea32022-08-11 21:35:04 +0100229 constructor() {
pineafan73a7c4a2022-07-24 10:38:04 +0100230 this.premium = database.collection<PremiumSchema>("premium");
pineafan4edb7762022-06-26 19:21:04 +0100231 }
232
pineafan73a7c4a2022-07-24 10:38:04 +0100233 async hasPremium(guild: string) {
Skyler Grey75ea9172022-08-06 10:22:23 +0100234 const entry = await this.premium.findOne({
235 appliesTo: { $in: [guild] }
236 });
TheCodedProf267563a2023-01-21 17:00:57 -0500237 if (!entry) return false;
238 return entry.expires.valueOf() > Date.now();
239 }
240
241 async fetchTotal(user: string): Promise<number> {
242 const entry = await this.premium.findOne({ user: user });
243 if (!entry) return 0;
244 return entry.appliesTo.length;
245 }
246
TheCodedProffc420b72023-01-24 17:14:38 -0500247 addPremium(user: string, guild: string) {
TheCodedProf267563a2023-01-21 17:00:57 -0500248 return this.premium.updateOne({ user: user }, { $addToSet: { appliesTo: guild } }, { upsert: true });
pineafan4edb7762022-06-26 19:21:04 +0100249 }
TheCodedProffc420b72023-01-24 17:14:38 -0500250
251 removePremium(user: string, guild: string) {
252 return this.premium.updateOne({ user: user }, { $pull: { appliesTo: guild } });
253 }
pineafan4edb7762022-06-26 19:21:04 +0100254}
255
pineafan6fb3e072022-05-20 19:27:23 +0100256export interface GuildConfig {
Skyler Grey75ea9172022-08-06 10:22:23 +0100257 id: string;
258 version: number;
PineaFan100df682023-01-02 13:26:08 +0000259 singleEventNotifications: Record<string, boolean>;
pineafan6fb3e072022-05-20 19:27:23 +0100260 filters: {
261 images: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100262 NSFW: boolean;
263 size: boolean;
264 };
265 malware: boolean;
pineafan6fb3e072022-05-20 19:27:23 +0100266 wordFilter: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100267 enabled: boolean;
pineafan6fb3e072022-05-20 19:27:23 +0100268 words: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100269 strict: string[];
270 loose: string[];
271 };
pineafan6fb3e072022-05-20 19:27:23 +0100272 allowed: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100273 users: string[];
274 roles: string[];
275 channels: string[];
276 };
277 };
pineafan6fb3e072022-05-20 19:27:23 +0100278 invite: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100279 enabled: boolean;
PineaFan538d3752023-01-12 21:48:23 +0000280 allowed: {
281 channels: string[];
282 roles: string[];
283 users: string[];
284 };
Skyler Grey75ea9172022-08-06 10:22:23 +0100285 };
pineafan6fb3e072022-05-20 19:27:23 +0100286 pings: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100287 mass: number;
288 everyone: boolean;
289 roles: boolean;
pineafan6fb3e072022-05-20 19:27:23 +0100290 allowed: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100291 roles: string[];
292 rolesToMention: string[];
293 users: string[];
294 channels: string[];
295 };
296 };
297 };
pineafan6fb3e072022-05-20 19:27:23 +0100298 welcome: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100299 enabled: boolean;
Skyler Grey75ea9172022-08-06 10:22:23 +0100300 role: string | null;
301 ping: string | null;
302 channel: string | null;
303 message: string | null;
304 };
305 stats: Record<string, { name: string; enabled: boolean }>;
pineafan6fb3e072022-05-20 19:27:23 +0100306 logging: {
307 logs: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100308 enabled: boolean;
309 channel: string | null;
Skyler Greyad002172022-08-16 18:48:26 +0100310 toLog: string;
Skyler Grey75ea9172022-08-06 10:22:23 +0100311 };
pineafan6fb3e072022-05-20 19:27:23 +0100312 staff: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100313 channel: string | null;
314 };
pineafan73a7c4a2022-07-24 10:38:04 +0100315 attachments: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100316 channel: string | null;
317 saved: Record<string, string>;
318 };
319 };
pineafan6fb3e072022-05-20 19:27:23 +0100320 verify: {
PineaFandf4996f2023-01-01 14:20:06 +0000321 enabled: boolean;
Skyler Grey75ea9172022-08-06 10:22:23 +0100322 role: string | null;
323 };
pineafan6fb3e072022-05-20 19:27:23 +0100324 tickets: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100325 enabled: boolean;
326 category: string | null;
Skyler Greyad002172022-08-16 18:48:26 +0100327 types: string;
328 customTypes: string[] | null;
Skyler Grey75ea9172022-08-06 10:22:23 +0100329 useCustom: boolean;
330 supportRole: string | null;
331 maxTickets: number;
332 };
pineafan6fb3e072022-05-20 19:27:23 +0100333 moderation: {
334 mute: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100335 timeout: boolean;
336 role: string | null;
337 text: string | null;
338 link: string | null;
339 };
pineafan6fb3e072022-05-20 19:27:23 +0100340 kick: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100341 text: string | null;
342 link: string | null;
343 };
pineafan6fb3e072022-05-20 19:27:23 +0100344 ban: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100345 text: string | null;
346 link: string | null;
347 };
pineafan6fb3e072022-05-20 19:27:23 +0100348 softban: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100349 text: string | null;
350 link: string | null;
351 };
pineafan6fb3e072022-05-20 19:27:23 +0100352 warn: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100353 text: string | null;
354 link: string | null;
355 };
pineafan6fb3e072022-05-20 19:27:23 +0100356 role: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100357 role: string | null;
TheCodedProfd9636e82023-01-17 22:13:06 -0500358 text: null;
359 link: null;
Skyler Grey75ea9172022-08-06 10:22:23 +0100360 };
PineaFane6ba7882023-01-18 20:41:16 +0000361 nick: {
362 text: string | null;
363 link: string | null;
364 }
Skyler Grey75ea9172022-08-06 10:22:23 +0100365 };
pineafan6fb3e072022-05-20 19:27:23 +0100366 tracks: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100367 name: string;
368 retainPrevious: boolean;
369 nullable: boolean;
370 track: string[];
371 manageableBy: string[];
372 }[];
pineafan6fb3e072022-05-20 19:27:23 +0100373 roleMenu: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100374 enabled: boolean;
375 allowWebUI: boolean;
pineafan6fb3e072022-05-20 19:27:23 +0100376 options: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100377 name: string;
378 description: string;
379 min: number;
380 max: number;
pineafan6fb3e072022-05-20 19:27:23 +0100381 options: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100382 name: string;
383 description: string | null;
384 role: string;
385 }[];
386 }[];
387 };
388 tags: Record<string, string>;
pineafan63fc5e22022-08-04 22:04:10 +0100389}
pineafan4edb7762022-06-26 19:21:04 +0100390
391export interface HistorySchema {
Skyler Grey75ea9172022-08-06 10:22:23 +0100392 type: string;
393 guild: string;
394 user: string;
395 moderator: string | null;
pineafan3a02ea32022-08-11 21:35:04 +0100396 reason: string | null;
Skyler Grey75ea9172022-08-06 10:22:23 +0100397 occurredAt: Date;
398 before: string | null;
399 after: string | null;
400 amount: string | null;
pineafan4edb7762022-06-26 19:21:04 +0100401}
402
403export interface ModNoteSchema {
Skyler Grey75ea9172022-08-06 10:22:23 +0100404 guild: string;
405 user: string;
pineafan3a02ea32022-08-11 21:35:04 +0100406 note: string | null;
pineafan4edb7762022-06-26 19:21:04 +0100407}
408
pineafan73a7c4a2022-07-24 10:38:04 +0100409export interface PremiumSchema {
Skyler Grey75ea9172022-08-06 10:22:23 +0100410 user: string;
411 level: number;
412 expires: Date;
413 appliesTo: string[];
414}