blob: c0ae9be586c25c82443aa5cd938eb2a965d7c6e0 [file] [log] [blame]
pineafan4edb7762022-06-26 19:21:04 +01001import Discord from 'discord.js';
2import { Collection, MongoClient } from 'mongodb';
pineafan6702cef2022-06-13 17:52:37 +01003import structuredClone from '@ungap/structured-clone';
pineafan4edb7762022-06-26 19:21:04 +01004import config from '../config/main.json' assert {type: 'json'};
5
6
7const mongoClient = new MongoClient(config.mongoUrl);
8await mongoClient.connect()
9const database = mongoClient.db("Nucleus");
pineafan6fb3e072022-05-20 19:27:23 +010010
11
12export const Entry = data => {
13 data = data ?? {};
pineafan6702cef2022-06-13 17:52:37 +010014 data.getKey = key => data[key]
pineafan6fb3e072022-05-20 19:27:23 +010015 return {
16 get(target, prop, receiver) {
17 let dataToReturn = data[prop]
18 if (dataToReturn === null ) return Reflect.get(target, prop, receiver);
19 if (typeof dataToReturn === "object" && !Array.isArray(dataToReturn)) dataToReturn = new Proxy(
20 Reflect.get(target, prop, receiver),
21 Entry(dataToReturn),
22 )
23 return dataToReturn ?? Reflect.get(target, prop, receiver);
24 }
25 }
26}
27
28
pineafan4edb7762022-06-26 19:21:04 +010029export class Guilds {
pineafan6fb3e072022-05-20 19:27:23 +010030 guilds: Collection<GuildConfig>;
31 defaultData: GuildConfig;
pineafan4edb7762022-06-26 19:21:04 +010032 async setup() {
33 this.guilds = database.collection<GuildConfig>("guilds");
pineafan6fb3e072022-05-20 19:27:23 +010034 this.defaultData = (await import("../config/default.json", { assert: { type: "json" }})).default as unknown as GuildConfig;
35 return this;
36 }
37
38 async read(guild: string) {
39 let entry = await this.guilds.findOne({ id: guild });
pineafan6702cef2022-06-13 17:52:37 +010040 return new Proxy(structuredClone(this.defaultData), Entry(entry)) as unknown as GuildConfig
pineafan6fb3e072022-05-20 19:27:23 +010041 }
42
pineafan6702cef2022-06-13 17:52:37 +010043 async write(guild: string, set: object = {}, unset: string[] = []) {
44 let uo = {}
45 for (let key of unset) {
46 uo[key] = "";
47 }
48 await this.guilds.updateOne({ id: guild }, {
49 $unset: uo,
50 $set: set
51 }, { upsert: true });
52 }
53
54 async append(guild: string, key: string, value: any) {
55 if (Array.isArray(value)) {
56 await this.guilds.updateOne({ id: guild }, {
57 $addToSet: { [key]: { $each: value } }
58 }, { upsert: true });
59 } else {
60 await this.guilds.updateOne({ id: guild }, {
61 $addToSet: { [key]: value }
62 }, { upsert: true });
63 }
64 }
65
66 async remove(guild: string, key: string, value: any) {
67 if (Array.isArray(value)) {
68 await this.guilds.updateOne({ id: guild }, {
69 $pullAll: { [key]: value }
70 }, { upsert: true });
71 } else {
72 await this.guilds.updateOne({ id: guild }, {
73 $pullAll: { [key]: [value] }
74 }, { upsert: true });
75 }
pineafan6fb3e072022-05-20 19:27:23 +010076 }
77}
78
pineafan4edb7762022-06-26 19:21:04 +010079
80export class History {
81 histories: Collection<HistorySchema>;
82 defaultData: GuildConfig;
83
84 async setup() {
85 this.histories = database.collection<HistorySchema>("history");
86 return this;
87 }
88
89 async create(type: string, guild: string, user: Discord.User, moderator: Discord.User | null, reason: string | null, before?: null, after?: null, amount?: null) {
90 await this.histories.insertOne({
91 type: type,
92 guild: guild,
93 user: user.id,
94 moderator: moderator.id,
95 reason: reason,
96 occurredAt: new Date(),
97 before: before,
98 after: after,
99 amount: amount
100 });
101 }
102
103 async read(guild: string, user: string, year: number) {
104 let entry = (await this.histories.find({
105 guild: guild,
106 user: user,
107 occurredAt: {
108 $gte: new Date(year - 1, 11, 31, 23, 59, 59),
109 $lt: new Date(year + 1, 0, 1, 0, 0, 0)
110 }
111 }).toArray()) as HistorySchema[];
112 return entry;
113 }
114}
115
116export class ModNotes {
117 modNotes: Collection<ModNoteSchema>;
118 defaultData: GuildConfig;
119
120 async setup() {
121 this.modNotes = database.collection<ModNoteSchema>("modNotes");
122 return this;
123 }
124
125 async create(guild: string, user: string, note: string | null) {
126 await this.modNotes.updateOne({ guild: guild, user: user }, { $set: { note: note }}, { upsert: true });
127 }
128
129 async read(guild: string, user: string) {
130 let entry = await this.modNotes.findOne({ guild: guild, user: user });
131 return entry?.note ?? null;
132 }
133}
134
pineafan73a7c4a2022-07-24 10:38:04 +0100135export class Premium {
136 premium: Collection<PremiumSchema>;
pineafan4edb7762022-06-26 19:21:04 +0100137
138 async setup() {
pineafan73a7c4a2022-07-24 10:38:04 +0100139 this.premium = database.collection<PremiumSchema>("premium");
pineafan4edb7762022-06-26 19:21:04 +0100140 return this;
141 }
142
pineafan73a7c4a2022-07-24 10:38:04 +0100143 async hasPremium(guild: string) {
144 let entry = await this.premium.findOne({ appliesTo: { $in: [guild] } });
145 return entry != null;
pineafan4edb7762022-06-26 19:21:04 +0100146 }
147}
148
pineafan6fb3e072022-05-20 19:27:23 +0100149export interface GuildConfig {
150 id: string,
151 version: number,
152 singleEventNotifications: {
153 statsChannelDeleted: boolean
154 }
155 filters: {
156 images: {
157 NSFW: boolean,
158 size: boolean
159 },
160 malware: boolean,
161 wordFilter: {
162 enabled: boolean,
163 words: {
164 strict: string[],
165 loose: string[]
166 },
167 allowed: {
168 users: string[],
169 roles: string[],
170 channels: string[]
171 }
172 },
173 invite: {
174 enabled: boolean,
175 allowed: {
176 users: string[],
177 channels: string[],
178 roles: string[]
179 }
180 },
181 pings: {
182 mass: number,
183 everyone: boolean,
184 roles: boolean,
185 allowed: {
186 roles: string[],
187 rolesToMention: string[],
188 users: string[],
189 channels: string[]
190 }
191 }
192 }
193 welcome: {
194 enabled: boolean,
195 verificationRequired: {
196 message: boolean,
pineafan6702cef2022-06-13 17:52:37 +0100197 role: string | null
pineafan6fb3e072022-05-20 19:27:23 +0100198 },
pineafan6702cef2022-06-13 17:52:37 +0100199 welcomeRole: string | null,
200 channel: string | null,
201 message: string | null,
pineafan6fb3e072022-05-20 19:27:23 +0100202 }
203 stats: {
204 enabled: boolean,
pineafan6702cef2022-06-13 17:52:37 +0100205 channel: string | null,
206 text: string | null,
pineafan6fb3e072022-05-20 19:27:23 +0100207 }[]
208 logging: {
209 logs: {
210 enabled: boolean,
pineafan6702cef2022-06-13 17:52:37 +0100211 channel: string | null,
212 toLog: string | null,
pineafan6fb3e072022-05-20 19:27:23 +0100213 },
214 staff: {
pineafan6702cef2022-06-13 17:52:37 +0100215 channel: string | null,
pineafan73a7c4a2022-07-24 10:38:04 +0100216 },
217 attachments: {
218 channel: string | null,
219 saved: {} // {channelID+messageID: log url (string)}
pineafan6fb3e072022-05-20 19:27:23 +0100220 }
221 }
222 verify: {
223 enabled: boolean,
pineafan6702cef2022-06-13 17:52:37 +0100224 role: string | null,
pineafan6fb3e072022-05-20 19:27:23 +0100225 }
226 tickets: {
227 enabled: boolean,
pineafan6702cef2022-06-13 17:52:37 +0100228 category: string | null,
229 types: string | null,
pineafan6fb3e072022-05-20 19:27:23 +0100230 customTypes: string[],
pineafan6702cef2022-06-13 17:52:37 +0100231 useCustom: boolean,
232 supportRole: string | null,
pineafan6fb3e072022-05-20 19:27:23 +0100233 maxTickets: number
234 }
235 moderation: {
236 mute: {
237 timeout: boolean,
pineafan6702cef2022-06-13 17:52:37 +0100238 role: string | null,
239 text: string | null,
240 link: string | null
pineafan6fb3e072022-05-20 19:27:23 +0100241 },
242 kick: {
pineafan6702cef2022-06-13 17:52:37 +0100243 text: string | null,
244 link: string | null
pineafan6fb3e072022-05-20 19:27:23 +0100245 },
246 ban: {
pineafan6702cef2022-06-13 17:52:37 +0100247 text: string | null,
248 link: string | null
pineafan6fb3e072022-05-20 19:27:23 +0100249 },
250 softban: {
pineafan6702cef2022-06-13 17:52:37 +0100251 text: string | null,
252 link: string | null
pineafan6fb3e072022-05-20 19:27:23 +0100253 },
254 warn: {
pineafan6702cef2022-06-13 17:52:37 +0100255 text: string | null,
256 link: string | null
pineafan6fb3e072022-05-20 19:27:23 +0100257 },
258 role: {
pineafan6702cef2022-06-13 17:52:37 +0100259 role: string | null,
pineafan6fb3e072022-05-20 19:27:23 +0100260 }
261 }
262 tracks: {
263 name: string,
264 retainPrevious: boolean,
265 nullable: boolean,
266 track: string[],
267 manageableBy: string[]
268 }[]
269 roleMenu: {
270 enabled: boolean,
271 allowWebUI: boolean,
272 options: {
273 name: string,
274 description: string,
275 min: number,
276 max: number,
277 options: {
278 name: string,
pineafan4edb7762022-06-26 19:21:04 +0100279 description: string | null,
pineafan6fb3e072022-05-20 19:27:23 +0100280 role: string
281 }[]
282 }[]
283 }
284 tags: {}
pineafan4edb7762022-06-26 19:21:04 +0100285};
286
287export interface HistorySchema {
288 type: string,
289 guild: string,
290 user: string,
291 moderator: string | null,
292 reason: string,
293 occurredAt: Date,
294 before: string | null,
295 after: string | null,
296 amount: string | null
297}
298
299export interface ModNoteSchema {
300 guild: string,
301 user: string,
302 note: string
303}
304
pineafan73a7c4a2022-07-24 10:38:04 +0100305export interface PremiumSchema {
306 user: string,
307 level: number,
308 expires: Date,
309 appliesTo: string[]
pineafan4edb7762022-06-26 19:21:04 +0100310}