blob: b7971c3b3a312aca7fca07f264d5571627943f17 [file] [log] [blame]
TheCodedProf9c51a7e2023-02-27 17:11:13 -05001import { ButtonStyle, CommandInteraction, ComponentType, GuildMember, Message, MessageComponentInteraction } 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
TheCodedProfcfe8e9a2023-02-26 17:28:09 -0500104interface TranscriptEmbed {
105 title?: string;
106 description?: string;
107 fields?: {
108 name: string;
109 value: string;
110 inline: boolean;
111 }[];
112 footer?: {
113 text: string;
114 iconURL?: string;
115 };
116}
117
118interface TranscriptComponent {
119 type: number;
120 style?: ButtonStyle;
121 label?: string;
122 description?: string;
123 placeholder?: string;
124 emojiURL?: string;
125}
126
127interface TranscriptAuthor {
128 username: string;
129 discriminator: number;
130 nickname?: string;
131 id: string;
132 iconURL?: string;
133 topRole: {
134 color: number;
135 badgeURL?: string;
136 }
137}
138
139interface TranscriptAttachment {
140 url: string;
141 filename: string;
142 size: number;
143 log?: string;
144}
145
146interface TranscriptMessage {
147 id: string;
148 author: TranscriptAuthor;
149 content?: string;
150 embeds?: TranscriptEmbed[];
151 components?: TranscriptComponent[][];
152 editedTimestamp?: number;
153 createdTimestamp: number;
154 flags?: string[];
155 attachments?: TranscriptAttachment[];
156 stickerURLs?: string[];
TheCodedProf9c51a7e2023-02-27 17:11:13 -0500157 referencedMessage?: string | [string, string, string]; // the message id, the channel id, the guild id
TheCodedProfcfe8e9a2023-02-26 17:28:09 -0500158}
159
160interface TranscriptSchema {
161 code: string;
162 for: TranscriptAuthor;
163 type: "ticket" | "purge"
164 guild: string;
165 channel: string;
166 messages: TranscriptMessage[];
167 createdTimestamp: number;
168 createdBy: TranscriptAuthor;
169}
170
171export class Transcript {
172 transcripts: Collection<TranscriptSchema>;
173
174 constructor() {
175 this.transcripts = database.collection<TranscriptSchema>("transcripts");
176 }
177
178 async create(transcript: Omit<TranscriptSchema, "code">) {
179 let code;
180 do {
181 code = Math.random().toString(36).substring(2, 16) + Math.random().toString(36).substring(2, 16);
182 } while (await this.transcripts.findOne({ code: code }));
183
184 const doc = await this.transcripts.insertOne(Object.assign(transcript, { code: code }));
185 if(doc.acknowledged) return code;
186 else return null;
187 }
188
189 async read(code: string) {
190 return await this.transcripts.findOne({ code: code });
191 }
TheCodedProf9c51a7e2023-02-27 17:11:13 -0500192
193 async createTranscript(messages: Message[], interaction: MessageComponentInteraction | CommandInteraction, member: GuildMember) {
194 const interactionMember = await interaction.guild?.members.fetch(interaction.user.id)
195 const newOut: Omit<TranscriptSchema, "code"> = {
196 type: "ticket",
197 for: {
198 username: member!.user.username,
199 discriminator: parseInt(member!.user.discriminator),
200 id: member!.user.id,
201 topRole: {
202 color: member!.roles.highest.color
203 }
204 },
205 guild: interaction.guild!.id,
206 channel: interaction.channel!.id,
207 messages: [],
208 createdTimestamp: Date.now(),
209 createdBy: {
210 username: interaction.user.username,
211 discriminator: parseInt(interaction.user.discriminator),
212 id: interaction.user.id,
213 topRole: {
214 color: interactionMember?.roles.highest.color ?? 0x000000
215 }
216 }
217 }
218 if(interactionMember?.roles.icon) newOut.createdBy.topRole.badgeURL = interactionMember.roles.icon.iconURL()!;
219 messages.reverse().forEach((message) => {
220 const msg: TranscriptMessage = {
221 id: message.id,
222 author: {
223 username: message.author.username,
224 discriminator: parseInt(message.author.discriminator),
225 id: message.author.id,
226 topRole: {
227 color: message.member!.roles.highest.color
228 }
229 },
230 createdTimestamp: message.createdTimestamp
231 };
232 if (message.member!.roles.icon) msg.author.topRole.badgeURL = message.member!.roles.icon.iconURL()!;
233 if (message.content) msg.content = message.content;
234 if (message.embeds.length > 0) msg.embeds = message.embeds.map(embed => {
235 const obj: TranscriptEmbed = {};
236 if (embed.title) obj.title = embed.title;
237 if (embed.description) obj.description = embed.description;
238 if (embed.fields.length > 0) obj.fields = embed.fields.map(field => {
239 return {
240 name: field.name,
241 value: field.value,
242 inline: field.inline ?? false
243 }
244 });
245 if (embed.footer) obj.footer = {
246 text: embed.footer.text,
247 };
248 if (embed.footer?.iconURL) obj.footer!.iconURL = embed.footer.iconURL;
249 return obj;
250 });
251 if (message.components.length > 0) msg.components = message.components.map(component => component.components.map(child => {
252 const obj: TranscriptComponent = {
253 type: child.type
254 }
255 if (child.type === ComponentType.Button) {
256 obj.style = child.style;
257 obj.label = child.label ?? "";
258 } else if (child.type > 2) {
259 obj.placeholder = child.placeholder ?? "";
260 }
261 return obj
262 }));
263 if (message.editedTimestamp) msg.editedTimestamp = message.editedTimestamp;
264 msg.flags = message.flags.toArray();
265
266 if (message.stickers.size > 0) msg.stickerURLs = message.stickers.map(sticker => sticker.url);
267 if (message.reference) msg.referencedMessage = [message.reference.guildId ?? "", message.reference.channelId, message.reference.messageId ?? ""];
268 newOut.messages.push(msg);
269 });
270 return newOut;
271 }
272
273 toHumanReadable(transcript: Omit<TranscriptSchema, "code">): string {
274 let out = "";
275 for (const message of transcript.messages) {
276 if (message.referencedMessage) {
277 if (Array.isArray(message.referencedMessage)) {
278 out += `> [Crosspost From] ${message.referencedMessage[0]} in ${message.referencedMessage[1]} in ${message.referencedMessage[2]}\n`;
279 }
280 else out += `> [Reply To] ${message.referencedMessage}\n`;
281 }
282 out += `${message.author.nickname ?? message.author.username}#${message.author.discriminator} (${message.author.id}) (${message.id}) `;
283 out += `[${new Date(message.createdTimestamp).toISOString()}] `;
284 if (message.editedTimestamp) out += `[Edited: ${new Date(message.editedTimestamp).toISOString()}] `;
285 out += "\n";
286 if (message.content) out += `[Content]\n${message.content}\n\n`;
287 if (message.embeds) {
288 for (const embed of message.embeds) {
289 out += `[Embed]\n`;
290 if (embed.title) out += `| Title: ${embed.title}\n`;
291 if (embed.description) out += `| Description: ${embed.description}\n`;
292 if (embed.fields) {
293 for (const field of embed.fields) {
294 out += `| Field: ${field.name} - ${field.value}\n`;
295 }
296 }
297 if (embed.footer) {
298 out += `|Footer: ${embed.footer.text}\n`;
299 }
300 out += "\n";
301 }
302 }
303 if (message.components) {
304 for (const component of message.components) {
305 out += `[Component]\n`;
306 for (const button of component) {
307 out += `| Button: ${button.label ?? button.description}\n`;
308 }
309 out += "\n";
310 }
311 }
312 if (message.attachments) {
313 for (const attachment of message.attachments) {
314 out += `[Attachment] ${attachment.filename} (${attachment.size} bytes) ${attachment.url}\n`;
315 }
316 }
317 }
318 return out
319 }
TheCodedProfcfe8e9a2023-02-26 17:28:09 -0500320}
321
pineafan4edb7762022-06-26 19:21:04 +0100322export class History {
323 histories: Collection<HistorySchema>;
pineafan4edb7762022-06-26 19:21:04 +0100324
pineafan3a02ea32022-08-11 21:35:04 +0100325 constructor() {
pineafan4edb7762022-06-26 19:21:04 +0100326 this.histories = database.collection<HistorySchema>("history");
pineafan4edb7762022-06-26 19:21:04 +0100327 }
328
Skyler Grey75ea9172022-08-06 10:22:23 +0100329 async create(
330 type: string,
331 guild: string,
332 user: Discord.User,
333 moderator: Discord.User | null,
334 reason: string | null,
pineafan3a02ea32022-08-11 21:35:04 +0100335 before?: string | null,
336 after?: string | null,
337 amount?: string | null
Skyler Grey75ea9172022-08-06 10:22:23 +0100338 ) {
pineafan4edb7762022-06-26 19:21:04 +0100339 await this.histories.insertOne({
340 type: type,
341 guild: guild,
342 user: user.id,
pineafan3a02ea32022-08-11 21:35:04 +0100343 moderator: moderator ? moderator.id : null,
pineafan4edb7762022-06-26 19:21:04 +0100344 reason: reason,
345 occurredAt: new Date(),
pineafan3a02ea32022-08-11 21:35:04 +0100346 before: before ?? null,
347 after: after ?? null,
348 amount: amount ?? null
pineafan4edb7762022-06-26 19:21:04 +0100349 });
350 }
351
352 async read(guild: string, user: string, year: number) {
Skyler Grey75ea9172022-08-06 10:22:23 +0100353 const entry = (await this.histories
354 .find({
355 guild: guild,
356 user: user,
357 occurredAt: {
358 $gte: new Date(year - 1, 11, 31, 23, 59, 59),
359 $lt: new Date(year + 1, 0, 1, 0, 0, 0)
360 }
361 })
362 .toArray()) as HistorySchema[];
pineafan4edb7762022-06-26 19:21:04 +0100363 return entry;
364 }
pineafane23c4ec2022-07-27 21:56:27 +0100365
366 async delete(guild: string) {
367 await this.histories.deleteMany({ guild: guild });
368 }
pineafan4edb7762022-06-26 19:21:04 +0100369}
370
TheCodedProfb5e9d552023-01-29 15:43:26 -0500371interface ScanCacheSchema {
372 addedAt: Date;
373 hash: string;
374 data: boolean;
375 tags: string[];
376}
377
378export class ScanCache {
379 scanCache: Collection<ScanCacheSchema>;
380
381 constructor() {
382 this.scanCache = database.collection<ScanCacheSchema>("scanCache");
383 }
384
385 async read(hash: string) {
386 return await this.scanCache.findOne({ hash: hash });
387 }
388
389 async write(hash: string, data: boolean, tags?: string[]) {
TheCodedProf1f675042023-02-16 17:01:29 -0500390 await this.scanCache.insertOne({ hash: hash, data: data, tags: tags ?? [], addedAt: new Date() });
TheCodedProfb5e9d552023-01-29 15:43:26 -0500391 }
392
393 async cleanup() {
394 await this.scanCache.deleteMany({ addedAt: { $lt: new Date(Date.now() - (1000 * 60 * 60 * 24 * 31)) }, hash: { $not$text: "http"} });
395 }
396}
397
PineaFan538d3752023-01-12 21:48:23 +0000398export class PerformanceTest {
399 performanceData: Collection<PerformanceDataSchema>;
400
401 constructor() {
402 this.performanceData = database.collection<PerformanceDataSchema>("performance");
403 }
404
405 async record(data: PerformanceDataSchema) {
406 data.timestamp = new Date();
407 await this.performanceData.insertOne(data);
408 }
409 async read() {
410 return await this.performanceData.find({}).toArray();
411 }
412}
413
414export interface PerformanceDataSchema {
415 timestamp?: Date;
416 discord: number;
417 databaseRead: number;
418 resources: {
419 cpu: number;
420 memory: number;
421 temperature: number;
422 }
423}
424
pineafan4edb7762022-06-26 19:21:04 +0100425export class ModNotes {
426 modNotes: Collection<ModNoteSchema>;
pineafan4edb7762022-06-26 19:21:04 +0100427
pineafan3a02ea32022-08-11 21:35:04 +0100428 constructor() {
pineafan4edb7762022-06-26 19:21:04 +0100429 this.modNotes = database.collection<ModNoteSchema>("modNotes");
pineafan4edb7762022-06-26 19:21:04 +0100430 }
431
432 async create(guild: string, user: string, note: string | null) {
Skyler Grey11236ba2022-08-08 21:13:33 +0100433 await this.modNotes.updateOne({ guild: guild, user: user }, { $set: { note: note } }, { upsert: true });
pineafan4edb7762022-06-26 19:21:04 +0100434 }
435
436 async read(guild: string, user: string) {
pineafan63fc5e22022-08-04 22:04:10 +0100437 const entry = await this.modNotes.findOne({ guild: guild, user: user });
pineafan4edb7762022-06-26 19:21:04 +0100438 return entry?.note ?? null;
439 }
TheCodedProf267563a2023-01-21 17:00:57 -0500440
441 async delete(guild: string) {
442 await this.modNotes.deleteMany({ guild: guild });
443 }
pineafan4edb7762022-06-26 19:21:04 +0100444}
445
pineafan73a7c4a2022-07-24 10:38:04 +0100446export class Premium {
447 premium: Collection<PremiumSchema>;
TheCodedProf9c51a7e2023-02-27 17:11:13 -0500448 cache: Map<string, [boolean, string, number, boolean, Date]>; // Date indicates the time one hour after it was created
449 cacheTimeout = 1000 * 60 * 60; // 1 hour
pineafan4edb7762022-06-26 19:21:04 +0100450
pineafan3a02ea32022-08-11 21:35:04 +0100451 constructor() {
pineafan73a7c4a2022-07-24 10:38:04 +0100452 this.premium = database.collection<PremiumSchema>("premium");
TheCodedProf9c51a7e2023-02-27 17:11:13 -0500453 this.cache = new Map<string, [boolean, string, number, boolean, Date]>();
pineafan4edb7762022-06-26 19:21:04 +0100454 }
455
TheCodedProf633866f2023-02-03 17:06:00 -0500456 async updateUser(user: string, level: number) {
457 if(!(await this.userExists(user))) await this.createUser(user, level);
458 await this.premium.updateOne({ user: user }, { $set: { level: level } }, { upsert: true });
459 }
460
461 async userExists(user: string): Promise<boolean> {
462 const entry = await this.premium.findOne({ user: user });
463 return entry ? true : false;
464 }
465
466 async createUser(user: string, level: number) {
467 await this.premium.insertOne({ user: user, appliesTo: [], level: level });
468 }
469
TheCodedProfaa3fe992023-02-25 21:53:09 -0500470 async hasPremium(guild: string): Promise<[boolean, string, number, boolean] | null> {
TheCodedProf9c51a7e2023-02-27 17:11:13 -0500471 // [Has premium, user giving premium, level, is mod: if given automatically]
472 const cached = this.cache.get(guild);
473 if (cached && cached[4].getTime() < Date.now()) return [cached[0], cached[1], cached[2], cached[3]];
TheCodedProf94ff6de2023-02-22 17:47:26 -0500474 const entries = await this.premium.find({}).toArray();
TheCodedProf9c51a7e2023-02-27 17:11:13 -0500475 const members = (await client.guilds.fetch(guild)).members.cache
TheCodedProf94ff6de2023-02-22 17:47:26 -0500476 for(const {user} of entries) {
477 const member = members.get(user);
TheCodedProfaa3fe992023-02-25 21:53:09 -0500478 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 -0500479 const modPerms = //TODO: Create list in config for perms
480 member.permissions.has("Administrator") ||
481 member.permissions.has("ManageChannels") ||
482 member.permissions.has("ManageRoles") ||
483 member.permissions.has("ManageEmojisAndStickers") ||
484 member.permissions.has("ManageWebhooks") ||
485 member.permissions.has("ManageGuild") ||
486 member.permissions.has("KickMembers") ||
487 member.permissions.has("BanMembers") ||
488 member.permissions.has("ManageEvents") ||
489 member.permissions.has("ManageMessages") ||
490 member.permissions.has("ManageThreads")
491 const entry = entries.find(e => e.user === member.id);
TheCodedProf9c51a7e2023-02-27 17:11:13 -0500492 if(entry && (entry.level === 3) && modPerms) {
493 this.cache.set(guild, [true, member.id, entry.level, true, new Date(Date.now() + this.cacheTimeout)]);
494 return [true, member.id, entry.level, true];
495 }
TheCodedProf94ff6de2023-02-22 17:47:26 -0500496 }
497 }
Skyler Grey75ea9172022-08-06 10:22:23 +0100498 const entry = await this.premium.findOne({
TheCodedProf94ff6de2023-02-22 17:47:26 -0500499 appliesTo: {
500 $elemMatch: {
501 $eq: guild
502 }
503 }
Skyler Grey75ea9172022-08-06 10:22:23 +0100504 });
TheCodedProf9c51a7e2023-02-27 17:11:13 -0500505 this.cache.set(guild, [entry ? true : false, entry?.user ?? "", entry?.level ?? 0, false, new Date(Date.now() + this.cacheTimeout)]);
TheCodedProfaa3fe992023-02-25 21:53:09 -0500506 return entry ? [true, entry.user, entry.level, false] : null;
TheCodedProf267563a2023-01-21 17:00:57 -0500507 }
508
TheCodedProf633866f2023-02-03 17:06:00 -0500509 async fetchUser(user: string): Promise<PremiumSchema | null> {
TheCodedProf267563a2023-01-21 17:00:57 -0500510 const entry = await this.premium.findOne({ user: user });
TheCodedProf633866f2023-02-03 17:06:00 -0500511 if (!entry) return null;
512 return entry;
513 }
514
TheCodedProf94ff6de2023-02-22 17:47:26 -0500515 async checkAllPremium(member?: GuildMember) {
TheCodedProf633866f2023-02-03 17:06:00 -0500516 const entries = await this.premium.find({}).toArray();
TheCodedProf94ff6de2023-02-22 17:47:26 -0500517 if(member) {
518 const entry = entries.find(e => e.user === member.id);
519 if(entry) {
520 const expiresAt = entry.expiresAt;
521 if(expiresAt) expiresAt < Date.now() ? await this.premium.deleteOne({user: member.id}) : null;
522 }
523 const roles = member.roles;
524 let level = 0;
525 if (roles.cache.has("1066468879309750313")) {
TheCodedProf633866f2023-02-03 17:06:00 -0500526 level = 99;
TheCodedProf94ff6de2023-02-22 17:47:26 -0500527 } else if (roles.cache.has("1066465491713003520")) {
TheCodedProf633866f2023-02-03 17:06:00 -0500528 level = 1;
TheCodedProf94ff6de2023-02-22 17:47:26 -0500529 } else if (roles.cache.has("1066439526496604194")) {
TheCodedProf633866f2023-02-03 17:06:00 -0500530 level = 2;
TheCodedProf94ff6de2023-02-22 17:47:26 -0500531 } else if (roles.cache.has("1066464134322978912")) {
TheCodedProf633866f2023-02-03 17:06:00 -0500532 level = 3;
533 }
TheCodedProf94ff6de2023-02-22 17:47:26 -0500534 await this.updateUser(member.id, level);
TheCodedProf633866f2023-02-03 17:06:00 -0500535 if (level > 0) {
TheCodedProf94ff6de2023-02-22 17:47:26 -0500536 await this.premium.updateOne({ user: member.id }, {$unset: { expiresAt: ""}})
TheCodedProf633866f2023-02-03 17:06:00 -0500537 } else {
TheCodedProf94ff6de2023-02-22 17:47:26 -0500538 await this.premium.updateOne({ user: member.id }, {$set: { expiresAt: (Date.now() + (1000*60*60*24*3)) }})
539 }
540 } else {
541 const members = await (await client.guilds.fetch('684492926528651336')).members.fetch();
542 for(const {roles, id} of members.values()) {
543 const entry = entries.find(e => e.user === id);
544 if(entry) {
545 const expiresAt = entry.expiresAt;
546 if(expiresAt) expiresAt < Date.now() ? await this.premium.deleteOne({user: id}) : null;
547 }
548 let level: number = 0;
549 if (roles.cache.has("1066468879309750313")) {
550 level = 99;
551 } else if (roles.cache.has("1066465491713003520")) {
552 level = 1;
553 } else if (roles.cache.has("1066439526496604194")) {
554 level = 2;
555 } else if (roles.cache.has("1066464134322978912")) {
556 level = 3;
557 }
558 await this.updateUser(id, level);
559 if (level > 0) {
560 await this.premium.updateOne({ user: id }, {$unset: { expiresAt: ""}})
561 } else {
562 await this.premium.updateOne({ user: id }, {$set: { expiresAt: (Date.now() + (1000*60*60*24*3)) }})
563 }
TheCodedProf633866f2023-02-03 17:06:00 -0500564 }
565 }
TheCodedProf267563a2023-01-21 17:00:57 -0500566 }
567
TheCodedProf9c51a7e2023-02-27 17:11:13 -0500568 async addPremium(user: string, guild: string) {
569 const { level } = (await this.fetchUser(user))!;
570 this.cache.set(guild, [true, user, level, false, new Date(Date.now() + this.cacheTimeout)]);
TheCodedProf267563a2023-01-21 17:00:57 -0500571 return this.premium.updateOne({ user: user }, { $addToSet: { appliesTo: guild } }, { upsert: true });
pineafan4edb7762022-06-26 19:21:04 +0100572 }
TheCodedProffc420b72023-01-24 17:14:38 -0500573
574 removePremium(user: string, guild: string) {
TheCodedProf9c51a7e2023-02-27 17:11:13 -0500575 this.cache.set(guild, [false, "", 0, false, new Date(Date.now() + this.cacheTimeout)]);
TheCodedProffc420b72023-01-24 17:14:38 -0500576 return this.premium.updateOne({ user: user }, { $pull: { appliesTo: guild } });
577 }
pineafan4edb7762022-06-26 19:21:04 +0100578}
579
pineafan6fb3e072022-05-20 19:27:23 +0100580export interface GuildConfig {
Skyler Grey75ea9172022-08-06 10:22:23 +0100581 id: string;
582 version: number;
PineaFan100df682023-01-02 13:26:08 +0000583 singleEventNotifications: Record<string, boolean>;
pineafan6fb3e072022-05-20 19:27:23 +0100584 filters: {
585 images: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100586 NSFW: boolean;
587 size: boolean;
588 };
589 malware: boolean;
pineafan6fb3e072022-05-20 19:27:23 +0100590 wordFilter: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100591 enabled: boolean;
pineafan6fb3e072022-05-20 19:27:23 +0100592 words: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100593 strict: string[];
594 loose: string[];
595 };
pineafan6fb3e072022-05-20 19:27:23 +0100596 allowed: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100597 users: string[];
598 roles: string[];
599 channels: string[];
600 };
601 };
pineafan6fb3e072022-05-20 19:27:23 +0100602 invite: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100603 enabled: boolean;
PineaFan538d3752023-01-12 21:48:23 +0000604 allowed: {
605 channels: string[];
606 roles: string[];
607 users: string[];
608 };
Skyler Grey75ea9172022-08-06 10:22:23 +0100609 };
pineafan6fb3e072022-05-20 19:27:23 +0100610 pings: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100611 mass: number;
612 everyone: boolean;
613 roles: boolean;
pineafan6fb3e072022-05-20 19:27:23 +0100614 allowed: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100615 roles: string[];
616 rolesToMention: string[];
617 users: string[];
618 channels: string[];
619 };
620 };
TheCodedProfad0b8202023-02-14 14:27:09 -0500621 clean: {
622 channels: string[];
623 allowed: {
624 user: string[];
625 roles: string[];
626 }
627 }
Skyler Grey75ea9172022-08-06 10:22:23 +0100628 };
TheCodedProfbaee2c12023-02-18 16:11:06 -0500629 autoPublish: {
630 enabled: boolean;
631 channels: string[];
632 }
pineafan6fb3e072022-05-20 19:27:23 +0100633 welcome: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100634 enabled: boolean;
Skyler Grey75ea9172022-08-06 10:22:23 +0100635 role: string | null;
636 ping: string | null;
637 channel: string | null;
638 message: string | null;
639 };
640 stats: Record<string, { name: string; enabled: boolean }>;
pineafan6fb3e072022-05-20 19:27:23 +0100641 logging: {
642 logs: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100643 enabled: boolean;
644 channel: string | null;
Skyler Greyad002172022-08-16 18:48:26 +0100645 toLog: string;
Skyler Grey75ea9172022-08-06 10:22:23 +0100646 };
pineafan6fb3e072022-05-20 19:27:23 +0100647 staff: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100648 channel: string | null;
649 };
pineafan73a7c4a2022-07-24 10:38:04 +0100650 attachments: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100651 channel: string | null;
652 saved: Record<string, string>;
653 };
654 };
pineafan6fb3e072022-05-20 19:27:23 +0100655 verify: {
PineaFandf4996f2023-01-01 14:20:06 +0000656 enabled: boolean;
Skyler Grey75ea9172022-08-06 10:22:23 +0100657 role: string | null;
658 };
pineafan6fb3e072022-05-20 19:27:23 +0100659 tickets: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100660 enabled: boolean;
661 category: string | null;
Skyler Greyad002172022-08-16 18:48:26 +0100662 types: string;
663 customTypes: string[] | null;
Skyler Grey75ea9172022-08-06 10:22:23 +0100664 useCustom: boolean;
665 supportRole: string | null;
666 maxTickets: number;
667 };
pineafan6fb3e072022-05-20 19:27:23 +0100668 moderation: {
669 mute: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100670 timeout: boolean;
671 role: string | null;
672 text: string | null;
673 link: string | null;
674 };
pineafan6fb3e072022-05-20 19:27:23 +0100675 kick: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100676 text: string | null;
677 link: string | null;
678 };
pineafan6fb3e072022-05-20 19:27:23 +0100679 ban: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100680 text: string | null;
681 link: string | null;
682 };
pineafan6fb3e072022-05-20 19:27:23 +0100683 softban: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100684 text: string | null;
685 link: string | null;
686 };
pineafan6fb3e072022-05-20 19:27:23 +0100687 warn: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100688 text: string | null;
689 link: string | null;
690 };
pineafan6fb3e072022-05-20 19:27:23 +0100691 role: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100692 role: string | null;
TheCodedProfd9636e82023-01-17 22:13:06 -0500693 text: null;
694 link: null;
Skyler Grey75ea9172022-08-06 10:22:23 +0100695 };
PineaFane6ba7882023-01-18 20:41:16 +0000696 nick: {
697 text: string | null;
698 link: string | null;
699 }
Skyler Grey75ea9172022-08-06 10:22:23 +0100700 };
pineafan6fb3e072022-05-20 19:27:23 +0100701 tracks: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100702 name: string;
703 retainPrevious: boolean;
704 nullable: boolean;
705 track: string[];
706 manageableBy: string[];
707 }[];
pineafan6fb3e072022-05-20 19:27:23 +0100708 roleMenu: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100709 enabled: boolean;
710 allowWebUI: boolean;
pineafan6fb3e072022-05-20 19:27:23 +0100711 options: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100712 name: string;
713 description: string;
714 min: number;
715 max: number;
pineafan6fb3e072022-05-20 19:27:23 +0100716 options: {
Skyler Grey75ea9172022-08-06 10:22:23 +0100717 name: string;
718 description: string | null;
719 role: string;
720 }[];
721 }[];
722 };
723 tags: Record<string, string>;
pineafan63fc5e22022-08-04 22:04:10 +0100724}
pineafan4edb7762022-06-26 19:21:04 +0100725
726export interface HistorySchema {
Skyler Grey75ea9172022-08-06 10:22:23 +0100727 type: string;
728 guild: string;
729 user: string;
730 moderator: string | null;
pineafan3a02ea32022-08-11 21:35:04 +0100731 reason: string | null;
Skyler Grey75ea9172022-08-06 10:22:23 +0100732 occurredAt: Date;
733 before: string | null;
734 after: string | null;
735 amount: string | null;
pineafan4edb7762022-06-26 19:21:04 +0100736}
737
738export interface ModNoteSchema {
Skyler Grey75ea9172022-08-06 10:22:23 +0100739 guild: string;
740 user: string;
pineafan3a02ea32022-08-11 21:35:04 +0100741 note: string | null;
pineafan4edb7762022-06-26 19:21:04 +0100742}
743
pineafan73a7c4a2022-07-24 10:38:04 +0100744export interface PremiumSchema {
Skyler Grey75ea9172022-08-06 10:22:23 +0100745 user: string;
746 level: number;
Skyler Grey75ea9172022-08-06 10:22:23 +0100747 appliesTo: string[];
TheCodedProf633866f2023-02-03 17:06:00 -0500748 expiresAt?: number;
Skyler Grey75ea9172022-08-06 10:22:23 +0100749}