blob: 48d007711d5833d7364c76a2cfacbc24b0a264d8 [file] [log] [blame]
TheCodedProf94ff6de2023-02-22 17:47:26 -05001import type { GuildMember } from "discord.js";
pineafan63fc5e22022-08-04 22:04:10 +01002import type Discord from "discord.js";
3import { Collection, MongoClient } from "mongodb";
pineafana2e39c72023-02-21 18:37:32 +00004import config from "../config/main.js";
TheCodedProf633866f2023-02-03 17:06:00 -05005import client from "../utils/client.js";
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
pineafan4edb7762022-06-26 19:21:04 +010011export class Guilds {
pineafan6fb3e072022-05-20 19:27:23 +010012 guilds: Collection<GuildConfig>;
pineafan63fc5e22022-08-04 22:04:10 +010013 defaultData: GuildConfig | null;
14
15 constructor() {
pineafan4edb7762022-06-26 19:21:04 +010016 this.guilds = database.collection<GuildConfig>("guilds");
pineafan63fc5e22022-08-04 22:04:10 +010017 this.defaultData = null;
pineafan6fb3e072022-05-20 19:27:23 +010018 }
19
Skyler Greyad002172022-08-16 18:48:26 +010020 async setup(): Promise<Guilds> {
Skyler Grey11236ba2022-08-08 21:13:33 +010021 this.defaultData = (await import("../config/default.json", { assert: { type: "json" } }))
22 .default as unknown as GuildConfig;
23 return this;
pineafan63fc5e22022-08-04 22:04:10 +010024 }
25
Skyler Greyad002172022-08-16 18:48:26 +010026 async read(guild: string): Promise<GuildConfig> {
pineafan63fc5e22022-08-04 22:04:10 +010027 const entry = await this.guilds.findOne({ id: guild });
PineaFandf4996f2023-01-01 14:20:06 +000028 return Object.assign({}, this.defaultData, entry);
pineafan6fb3e072022-05-20 19:27:23 +010029 }
30
Skyler Grey11236ba2022-08-08 21:13:33 +010031 async write(guild: string, set: object | null, unset: string[] | string = []) {
pineafan63fc5e22022-08-04 22:04:10 +010032 // eslint-disable-next-line @typescript-eslint/no-explicit-any
33 const uo: Record<string, any> = {};
34 if (!Array.isArray(unset)) unset = [unset];
35 for (const key of unset) {
pineafan0bc04162022-07-25 17:22:26 +010036 uo[key] = null;
pineafan6702cef2022-06-13 17:52:37 +010037 }
Skyler Grey75ea9172022-08-06 10:22:23 +010038 const out = { $set: {}, $unset: {} };
39 if (set) out.$set = set;
40 if (unset.length) out.$unset = uo;
pineafan0bc04162022-07-25 17:22:26 +010041 await this.guilds.updateOne({ id: guild }, out, { upsert: true });
pineafan6702cef2022-06-13 17:52:37 +010042 }
43
pineafan63fc5e22022-08-04 22:04:10 +010044 // eslint-disable-next-line @typescript-eslint/no-explicit-any
pineafan6702cef2022-06-13 17:52:37 +010045 async append(guild: string, key: string, value: any) {
46 if (Array.isArray(value)) {
Skyler Grey75ea9172022-08-06 10:22:23 +010047 await this.guilds.updateOne(
48 { id: guild },
49 {
50 $addToSet: { [key]: { $each: value } }
51 },
52 { upsert: true }
53 );
pineafan6702cef2022-06-13 17:52:37 +010054 } else {
Skyler Grey75ea9172022-08-06 10:22:23 +010055 await this.guilds.updateOne(
56 { id: guild },
57 {
58 $addToSet: { [key]: value }
59 },
60 { upsert: true }
61 );
pineafan6702cef2022-06-13 17:52:37 +010062 }
63 }
64
Skyler Grey75ea9172022-08-06 10:22:23 +010065 async remove(
66 guild: string,
67 key: string,
Skyler Greyc634e2b2022-08-06 17:50:48 +010068 // eslint-disable-next-line @typescript-eslint/no-explicit-any
Skyler Grey75ea9172022-08-06 10:22:23 +010069 value: any,
70 innerKey?: string | null
71 ) {
pineafan02ba0232022-07-24 22:16:15 +010072 if (innerKey) {
Skyler Grey75ea9172022-08-06 10:22:23 +010073 await this.guilds.updateOne(
74 { id: guild },
75 {
76 $pull: { [key]: { [innerKey]: { $eq: value } } }
77 },
78 { upsert: true }
79 );
pineafan0bc04162022-07-25 17:22:26 +010080 } else if (Array.isArray(value)) {
Skyler Grey75ea9172022-08-06 10:22:23 +010081 await this.guilds.updateOne(
82 { id: guild },
83 {
84 $pullAll: { [key]: value }
85 },
86 { upsert: true }
87 );
pineafan6702cef2022-06-13 17:52:37 +010088 } else {
Skyler Grey75ea9172022-08-06 10:22:23 +010089 await this.guilds.updateOne(
90 { id: guild },
91 {
92 $pullAll: { [key]: [value] }
93 },
94 { upsert: true }
95 );
pineafan6702cef2022-06-13 17:52:37 +010096 }
pineafan6fb3e072022-05-20 19:27:23 +010097 }
pineafane23c4ec2022-07-27 21:56:27 +010098
99 async delete(guild: string) {
100 await this.guilds.deleteOne({ id: guild });
101 }
pineafan6fb3e072022-05-20 19:27:23 +0100102}
103
pineafan4edb7762022-06-26 19:21:04 +0100104export class History {
105 histories: Collection<HistorySchema>;
pineafan4edb7762022-06-26 19:21:04 +0100106
pineafan3a02ea32022-08-11 21:35:04 +0100107 constructor() {
pineafan4edb7762022-06-26 19:21:04 +0100108 this.histories = database.collection<HistorySchema>("history");
pineafan4edb7762022-06-26 19:21:04 +0100109 }
110
Skyler Grey75ea9172022-08-06 10:22:23 +0100111 async create(
112 type: string,
113 guild: string,
114 user: Discord.User,
115 moderator: Discord.User | null,
116 reason: string | null,
pineafan3a02ea32022-08-11 21:35:04 +0100117 before?: string | null,
118 after?: string | null,
119 amount?: string | null
Skyler Grey75ea9172022-08-06 10:22:23 +0100120 ) {
pineafan4edb7762022-06-26 19:21:04 +0100121 await this.histories.insertOne({
122 type: type,
123 guild: guild,
124 user: user.id,
pineafan3a02ea32022-08-11 21:35:04 +0100125 moderator: moderator ? moderator.id : null,
pineafan4edb7762022-06-26 19:21:04 +0100126 reason: reason,
127 occurredAt: new Date(),
pineafan3a02ea32022-08-11 21:35:04 +0100128 before: before ?? null,
129 after: after ?? null,
130 amount: amount ?? null
pineafan4edb7762022-06-26 19:21:04 +0100131 });
132 }
133
134 async read(guild: string, user: string, year: number) {
Skyler Grey75ea9172022-08-06 10:22:23 +0100135 const entry = (await this.histories
136 .find({
137 guild: guild,
138 user: user,
139 occurredAt: {
140 $gte: new Date(year - 1, 11, 31, 23, 59, 59),
141 $lt: new Date(year + 1, 0, 1, 0, 0, 0)
142 }
143 })
144 .toArray()) as HistorySchema[];
pineafan4edb7762022-06-26 19:21:04 +0100145 return entry;
146 }
pineafane23c4ec2022-07-27 21:56:27 +0100147
148 async delete(guild: string) {
149 await this.histories.deleteMany({ guild: guild });
150 }
pineafan4edb7762022-06-26 19:21:04 +0100151}
152
TheCodedProfb5e9d552023-01-29 15:43:26 -0500153interface ScanCacheSchema {
154 addedAt: Date;
155 hash: string;
156 data: boolean;
157 tags: string[];
158}
159
160export class ScanCache {
161 scanCache: Collection<ScanCacheSchema>;
162
163 constructor() {
164 this.scanCache = database.collection<ScanCacheSchema>("scanCache");
165 }
166
167 async read(hash: string) {
168 return await this.scanCache.findOne({ hash: hash });
169 }
170
171 async write(hash: string, data: boolean, tags?: string[]) {
TheCodedProf1f675042023-02-16 17:01:29 -0500172 await this.scanCache.insertOne({ hash: hash, data: data, tags: tags ?? [], addedAt: new Date() });
TheCodedProfb5e9d552023-01-29 15:43:26 -0500173 }
174
175 async cleanup() {
176 await this.scanCache.deleteMany({ addedAt: { $lt: new Date(Date.now() - (1000 * 60 * 60 * 24 * 31)) }, hash: { $not$text: "http"} });
177 }
178}
179
PineaFan538d3752023-01-12 21:48:23 +0000180export class PerformanceTest {
181 performanceData: Collection<PerformanceDataSchema>;
182
183 constructor() {
184 this.performanceData = database.collection<PerformanceDataSchema>("performance");
185 }
186
187 async record(data: PerformanceDataSchema) {
188 data.timestamp = new Date();
189 await this.performanceData.insertOne(data);
190 }
191 async read() {
192 return await this.performanceData.find({}).toArray();
193 }
194}
195
196export interface PerformanceDataSchema {
197 timestamp?: Date;
198 discord: number;
199 databaseRead: number;
200 resources: {
201 cpu: number;
202 memory: number;
203 temperature: number;
204 }
205}
206
pineafan4edb7762022-06-26 19:21:04 +0100207export class ModNotes {
208 modNotes: Collection<ModNoteSchema>;
pineafan4edb7762022-06-26 19:21:04 +0100209
pineafan3a02ea32022-08-11 21:35:04 +0100210 constructor() {
pineafan4edb7762022-06-26 19:21:04 +0100211 this.modNotes = database.collection<ModNoteSchema>("modNotes");
pineafan4edb7762022-06-26 19:21:04 +0100212 }
213
214 async create(guild: string, user: string, note: string | null) {
Skyler Grey11236ba2022-08-08 21:13:33 +0100215 await this.modNotes.updateOne({ guild: guild, user: user }, { $set: { note: note } }, { upsert: true });
pineafan4edb7762022-06-26 19:21:04 +0100216 }
217
218 async read(guild: string, user: string) {
pineafan63fc5e22022-08-04 22:04:10 +0100219 const entry = await this.modNotes.findOne({ guild: guild, user: user });
pineafan4edb7762022-06-26 19:21:04 +0100220 return entry?.note ?? null;
221 }
TheCodedProf267563a2023-01-21 17:00:57 -0500222
223 async delete(guild: string) {
224 await this.modNotes.deleteMany({ guild: guild });
225 }
pineafan4edb7762022-06-26 19:21:04 +0100226}
227
pineafan73a7c4a2022-07-24 10:38:04 +0100228export class Premium {
229 premium: Collection<PremiumSchema>;
pineafan4edb7762022-06-26 19:21:04 +0100230
pineafan3a02ea32022-08-11 21:35:04 +0100231 constructor() {
pineafan73a7c4a2022-07-24 10:38:04 +0100232 this.premium = database.collection<PremiumSchema>("premium");
pineafan4edb7762022-06-26 19:21:04 +0100233 }
234
TheCodedProf633866f2023-02-03 17:06:00 -0500235 async updateUser(user: string, level: number) {
236 if(!(await this.userExists(user))) await this.createUser(user, level);
237 await this.premium.updateOne({ user: user }, { $set: { level: level } }, { upsert: true });
238 }
239
240 async userExists(user: string): Promise<boolean> {
241 const entry = await this.premium.findOne({ user: user });
242 return entry ? true : false;
243 }
244
245 async createUser(user: string, level: number) {
246 await this.premium.insertOne({ user: user, appliesTo: [], level: level });
247 }
248
TheCodedProfaa3fe992023-02-25 21:53:09 -0500249 async hasPremium(guild: string): Promise<[boolean, string, number, boolean] | null> {
TheCodedProf94ff6de2023-02-22 17:47:26 -0500250 const entries = await this.premium.find({}).toArray();
251 const members = await (await client.guilds.fetch(guild)).members.fetch()
252 for(const {user} of entries) {
253 const member = members.get(user);
TheCodedProfaa3fe992023-02-25 21:53:09 -0500254 if(member) { //TODO: Notify user if they've given premium to a server that has since gotten premium via a mod.
TheCodedProf94ff6de2023-02-22 17:47:26 -0500255 const modPerms = //TODO: Create list in config for perms
256 member.permissions.has("Administrator") ||
257 member.permissions.has("ManageChannels") ||
258 member.permissions.has("ManageRoles") ||
259 member.permissions.has("ManageEmojisAndStickers") ||
260 member.permissions.has("ManageWebhooks") ||
261 member.permissions.has("ManageGuild") ||
262 member.permissions.has("KickMembers") ||
263 member.permissions.has("BanMembers") ||
264 member.permissions.has("ManageEvents") ||
265 member.permissions.has("ManageMessages") ||
266 member.permissions.has("ManageThreads")
267 const entry = entries.find(e => e.user === member.id);
TheCodedProfaa3fe992023-02-25 21:53:09 -0500268 if(entry && (entry.level === 3) && modPerms) return [true, member.id, entry.level, true];
TheCodedProf94ff6de2023-02-22 17:47:26 -0500269 }
270 }
Skyler Grey75ea9172022-08-06 10:22:23 +0100271 const entry = await this.premium.findOne({
TheCodedProf94ff6de2023-02-22 17:47:26 -0500272 appliesTo: {
273 $elemMatch: {
274 $eq: guild
275 }
276 }
Skyler Grey75ea9172022-08-06 10:22:23 +0100277 });
TheCodedProfaa3fe992023-02-25 21:53:09 -0500278 return entry ? [true, entry.user, entry.level, false] : null;
TheCodedProf267563a2023-01-21 17:00:57 -0500279 }
280
TheCodedProf633866f2023-02-03 17:06:00 -0500281 async fetchUser(user: string): Promise<PremiumSchema | null> {
TheCodedProf267563a2023-01-21 17:00:57 -0500282 const entry = await this.premium.findOne({ user: user });
TheCodedProf633866f2023-02-03 17:06:00 -0500283 if (!entry) return null;
284 return entry;
285 }
286
TheCodedProf94ff6de2023-02-22 17:47:26 -0500287 async checkAllPremium(member?: GuildMember) {
TheCodedProf633866f2023-02-03 17:06:00 -0500288 const entries = await this.premium.find({}).toArray();
TheCodedProf94ff6de2023-02-22 17:47:26 -0500289 if(member) {
290 const entry = entries.find(e => e.user === member.id);
291 if(entry) {
292 const expiresAt = entry.expiresAt;
293 if(expiresAt) expiresAt < Date.now() ? await this.premium.deleteOne({user: member.id}) : null;
294 }
295 const roles = member.roles;
296 let level = 0;
297 if (roles.cache.has("1066468879309750313")) {
TheCodedProf633866f2023-02-03 17:06:00 -0500298 level = 99;
TheCodedProf94ff6de2023-02-22 17:47:26 -0500299 } else if (roles.cache.has("1066465491713003520")) {
TheCodedProf633866f2023-02-03 17:06:00 -0500300 level = 1;
TheCodedProf94ff6de2023-02-22 17:47:26 -0500301 } else if (roles.cache.has("1066439526496604194")) {
TheCodedProf633866f2023-02-03 17:06:00 -0500302 level = 2;
TheCodedProf94ff6de2023-02-22 17:47:26 -0500303 } else if (roles.cache.has("1066464134322978912")) {
TheCodedProf633866f2023-02-03 17:06:00 -0500304 level = 3;
305 }
TheCodedProf94ff6de2023-02-22 17:47:26 -0500306 await this.updateUser(member.id, level);
TheCodedProf633866f2023-02-03 17:06:00 -0500307 if (level > 0) {
TheCodedProf94ff6de2023-02-22 17:47:26 -0500308 await this.premium.updateOne({ user: member.id }, {$unset: { expiresAt: ""}})
TheCodedProf633866f2023-02-03 17:06:00 -0500309 } else {
TheCodedProf94ff6de2023-02-22 17:47:26 -0500310 await this.premium.updateOne({ user: member.id }, {$set: { expiresAt: (Date.now() + (1000*60*60*24*3)) }})
311 }
312 } else {
313 const members = await (await client.guilds.fetch('684492926528651336')).members.fetch();
314 for(const {roles, id} of members.values()) {
315 const entry = entries.find(e => e.user === id);
316 if(entry) {
317 const expiresAt = entry.expiresAt;
318 if(expiresAt) expiresAt < Date.now() ? await this.premium.deleteOne({user: id}) : null;
319 }
320 let level: number = 0;
321 if (roles.cache.has("1066468879309750313")) {
322 level = 99;
323 } else if (roles.cache.has("1066465491713003520")) {
324 level = 1;
325 } else if (roles.cache.has("1066439526496604194")) {
326 level = 2;
327 } else if (roles.cache.has("1066464134322978912")) {
328 level = 3;
329 }
330 await this.updateUser(id, level);
331 if (level > 0) {
332 await this.premium.updateOne({ user: id }, {$unset: { expiresAt: ""}})
333 } else {
334 await this.premium.updateOne({ user: id }, {$set: { expiresAt: (Date.now() + (1000*60*60*24*3)) }})
335 }
TheCodedProf633866f2023-02-03 17:06:00 -0500336 }
337 }
TheCodedProf267563a2023-01-21 17:00:57 -0500338 }
339
TheCodedProffc420b72023-01-24 17:14:38 -0500340 addPremium(user: string, guild: string) {
TheCodedProf267563a2023-01-21 17:00:57 -0500341 return this.premium.updateOne({ user: user }, { $addToSet: { appliesTo: guild } }, { upsert: true });
pineafan4edb7762022-06-26 19:21:04 +0100342 }
TheCodedProffc420b72023-01-24 17:14:38 -0500343
344 removePremium(user: string, guild: string) {
345 return this.premium.updateOne({ user: user }, { $pull: { appliesTo: guild } });
346 }
pineafan4edb7762022-06-26 19:21:04 +0100347}
348
pineafan6fb3e072022-05-20 19:27:23 +0100349export interface GuildConfig {
Skyler Grey75ea9172022-08-06 10:22:23 +0100350 id: string;
351 version: number;
PineaFan100df682023-01-02 13:26:08 +0000352 singleEventNotifications: Record<string, boolean>;
pineafan6fb3e072022-05-20 19:27:23 +0100353 filters: {
354 images: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100355 NSFW: boolean;
356 size: boolean;
357 };
358 malware: boolean;
pineafan6fb3e072022-05-20 19:27:23 +0100359 wordFilter: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100360 enabled: boolean;
pineafan6fb3e072022-05-20 19:27:23 +0100361 words: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100362 strict: string[];
363 loose: string[];
364 };
pineafan6fb3e072022-05-20 19:27:23 +0100365 allowed: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100366 users: string[];
367 roles: string[];
368 channels: string[];
369 };
370 };
pineafan6fb3e072022-05-20 19:27:23 +0100371 invite: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100372 enabled: boolean;
PineaFan538d3752023-01-12 21:48:23 +0000373 allowed: {
374 channels: string[];
375 roles: string[];
376 users: string[];
377 };
Skyler Grey75ea9172022-08-06 10:22:23 +0100378 };
pineafan6fb3e072022-05-20 19:27:23 +0100379 pings: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100380 mass: number;
381 everyone: boolean;
382 roles: boolean;
pineafan6fb3e072022-05-20 19:27:23 +0100383 allowed: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100384 roles: string[];
385 rolesToMention: string[];
386 users: string[];
387 channels: string[];
388 };
389 };
TheCodedProfad0b8202023-02-14 14:27:09 -0500390 clean: {
391 channels: string[];
392 allowed: {
393 user: string[];
394 roles: string[];
395 }
396 }
Skyler Grey75ea9172022-08-06 10:22:23 +0100397 };
TheCodedProfbaee2c12023-02-18 16:11:06 -0500398 autoPublish: {
399 enabled: boolean;
400 channels: string[];
401 }
pineafan6fb3e072022-05-20 19:27:23 +0100402 welcome: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100403 enabled: boolean;
Skyler Grey75ea9172022-08-06 10:22:23 +0100404 role: string | null;
405 ping: string | null;
406 channel: string | null;
407 message: string | null;
408 };
409 stats: Record<string, { name: string; enabled: boolean }>;
pineafan6fb3e072022-05-20 19:27:23 +0100410 logging: {
411 logs: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100412 enabled: boolean;
413 channel: string | null;
Skyler Greyad002172022-08-16 18:48:26 +0100414 toLog: string;
Skyler Grey75ea9172022-08-06 10:22:23 +0100415 };
pineafan6fb3e072022-05-20 19:27:23 +0100416 staff: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100417 channel: string | null;
418 };
pineafan73a7c4a2022-07-24 10:38:04 +0100419 attachments: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100420 channel: string | null;
421 saved: Record<string, string>;
422 };
423 };
pineafan6fb3e072022-05-20 19:27:23 +0100424 verify: {
PineaFandf4996f2023-01-01 14:20:06 +0000425 enabled: boolean;
Skyler Grey75ea9172022-08-06 10:22:23 +0100426 role: string | null;
427 };
pineafan6fb3e072022-05-20 19:27:23 +0100428 tickets: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100429 enabled: boolean;
430 category: string | null;
Skyler Greyad002172022-08-16 18:48:26 +0100431 types: string;
432 customTypes: string[] | null;
Skyler Grey75ea9172022-08-06 10:22:23 +0100433 useCustom: boolean;
434 supportRole: string | null;
435 maxTickets: number;
436 };
pineafan6fb3e072022-05-20 19:27:23 +0100437 moderation: {
438 mute: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100439 timeout: boolean;
440 role: string | null;
441 text: string | null;
442 link: string | null;
443 };
pineafan6fb3e072022-05-20 19:27:23 +0100444 kick: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100445 text: string | null;
446 link: string | null;
447 };
pineafan6fb3e072022-05-20 19:27:23 +0100448 ban: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100449 text: string | null;
450 link: string | null;
451 };
pineafan6fb3e072022-05-20 19:27:23 +0100452 softban: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100453 text: string | null;
454 link: string | null;
455 };
pineafan6fb3e072022-05-20 19:27:23 +0100456 warn: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100457 text: string | null;
458 link: string | null;
459 };
pineafan6fb3e072022-05-20 19:27:23 +0100460 role: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100461 role: string | null;
TheCodedProfd9636e82023-01-17 22:13:06 -0500462 text: null;
463 link: null;
Skyler Grey75ea9172022-08-06 10:22:23 +0100464 };
PineaFane6ba7882023-01-18 20:41:16 +0000465 nick: {
466 text: string | null;
467 link: string | null;
468 }
Skyler Grey75ea9172022-08-06 10:22:23 +0100469 };
pineafan6fb3e072022-05-20 19:27:23 +0100470 tracks: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100471 name: string;
472 retainPrevious: boolean;
473 nullable: boolean;
474 track: string[];
475 manageableBy: string[];
476 }[];
pineafan6fb3e072022-05-20 19:27:23 +0100477 roleMenu: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100478 enabled: boolean;
479 allowWebUI: boolean;
pineafan6fb3e072022-05-20 19:27:23 +0100480 options: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100481 name: string;
482 description: string;
483 min: number;
484 max: number;
pineafan6fb3e072022-05-20 19:27:23 +0100485 options: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100486 name: string;
487 description: string | null;
488 role: string;
489 }[];
490 }[];
491 };
492 tags: Record<string, string>;
pineafan63fc5e22022-08-04 22:04:10 +0100493}
pineafan4edb7762022-06-26 19:21:04 +0100494
495export interface HistorySchema {
Skyler Grey75ea9172022-08-06 10:22:23 +0100496 type: string;
497 guild: string;
498 user: string;
499 moderator: string | null;
pineafan3a02ea32022-08-11 21:35:04 +0100500 reason: string | null;
Skyler Grey75ea9172022-08-06 10:22:23 +0100501 occurredAt: Date;
502 before: string | null;
503 after: string | null;
504 amount: string | null;
pineafan4edb7762022-06-26 19:21:04 +0100505}
506
507export interface ModNoteSchema {
Skyler Grey75ea9172022-08-06 10:22:23 +0100508 guild: string;
509 user: string;
pineafan3a02ea32022-08-11 21:35:04 +0100510 note: string | null;
pineafan4edb7762022-06-26 19:21:04 +0100511}
512
pineafan73a7c4a2022-07-24 10:38:04 +0100513export interface PremiumSchema {
Skyler Grey75ea9172022-08-06 10:22:23 +0100514 user: string;
515 level: number;
Skyler Grey75ea9172022-08-06 10:22:23 +0100516 appliesTo: string[];
TheCodedProf633866f2023-02-03 17:06:00 -0500517 expiresAt?: number;
Skyler Grey75ea9172022-08-06 10:22:23 +0100518}