blob: 5b1d6d9b74aa0361edffc7bec6b4c5894bef4474 [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
pineafan02ba0232022-07-24 22:16:15 +010066 async remove(guild: string, key: string, value: any, innerKey?: string) {
67 if (innerKey) {
68 await this.guilds.updateOne({ id: guild }, {
69 $pull: { [key]: { [innerKey]: { $eq: value } } }
70 }, { upsert: true });
71 }
72 else if (Array.isArray(value)) {
pineafan6702cef2022-06-13 17:52:37 +010073 await this.guilds.updateOne({ id: guild }, {
74 $pullAll: { [key]: value }
75 }, { upsert: true });
76 } else {
77 await this.guilds.updateOne({ id: guild }, {
78 $pullAll: { [key]: [value] }
79 }, { upsert: true });
80 }
pineafan6fb3e072022-05-20 19:27:23 +010081 }
82}
83
pineafan4edb7762022-06-26 19:21:04 +010084
85export class History {
86 histories: Collection<HistorySchema>;
87 defaultData: GuildConfig;
88
89 async setup() {
90 this.histories = database.collection<HistorySchema>("history");
91 return this;
92 }
93
94 async create(type: string, guild: string, user: Discord.User, moderator: Discord.User | null, reason: string | null, before?: null, after?: null, amount?: null) {
95 await this.histories.insertOne({
96 type: type,
97 guild: guild,
98 user: user.id,
99 moderator: moderator.id,
100 reason: reason,
101 occurredAt: new Date(),
102 before: before,
103 after: after,
104 amount: amount
105 });
106 }
107
108 async read(guild: string, user: string, year: number) {
109 let entry = (await this.histories.find({
110 guild: guild,
111 user: user,
112 occurredAt: {
113 $gte: new Date(year - 1, 11, 31, 23, 59, 59),
114 $lt: new Date(year + 1, 0, 1, 0, 0, 0)
115 }
116 }).toArray()) as HistorySchema[];
117 return entry;
118 }
119}
120
121export class ModNotes {
122 modNotes: Collection<ModNoteSchema>;
123 defaultData: GuildConfig;
124
125 async setup() {
126 this.modNotes = database.collection<ModNoteSchema>("modNotes");
127 return this;
128 }
129
130 async create(guild: string, user: string, note: string | null) {
131 await this.modNotes.updateOne({ guild: guild, user: user }, { $set: { note: note }}, { upsert: true });
132 }
133
134 async read(guild: string, user: string) {
135 let entry = await this.modNotes.findOne({ guild: guild, user: user });
136 return entry?.note ?? null;
137 }
138}
139
pineafan73a7c4a2022-07-24 10:38:04 +0100140export class Premium {
141 premium: Collection<PremiumSchema>;
pineafan4edb7762022-06-26 19:21:04 +0100142
143 async setup() {
pineafan73a7c4a2022-07-24 10:38:04 +0100144 this.premium = database.collection<PremiumSchema>("premium");
pineafan4edb7762022-06-26 19:21:04 +0100145 return this;
146 }
147
pineafan73a7c4a2022-07-24 10:38:04 +0100148 async hasPremium(guild: string) {
149 let entry = await this.premium.findOne({ appliesTo: { $in: [guild] } });
150 return entry != null;
pineafan4edb7762022-06-26 19:21:04 +0100151 }
152}
153
pineafan6fb3e072022-05-20 19:27:23 +0100154export interface GuildConfig {
155 id: string,
156 version: number,
157 singleEventNotifications: {
158 statsChannelDeleted: boolean
159 }
160 filters: {
161 images: {
162 NSFW: boolean,
163 size: boolean
164 },
165 malware: boolean,
166 wordFilter: {
167 enabled: boolean,
168 words: {
169 strict: string[],
170 loose: string[]
171 },
172 allowed: {
173 users: string[],
174 roles: string[],
175 channels: string[]
176 }
177 },
178 invite: {
179 enabled: boolean,
180 allowed: {
181 users: string[],
182 channels: string[],
183 roles: string[]
184 }
185 },
186 pings: {
187 mass: number,
188 everyone: boolean,
189 roles: boolean,
190 allowed: {
191 roles: string[],
192 rolesToMention: string[],
193 users: string[],
194 channels: string[]
195 }
196 }
197 }
198 welcome: {
199 enabled: boolean,
200 verificationRequired: {
201 message: boolean,
pineafan6702cef2022-06-13 17:52:37 +0100202 role: string | null
pineafan6fb3e072022-05-20 19:27:23 +0100203 },
pineafan6702cef2022-06-13 17:52:37 +0100204 welcomeRole: string | null,
205 channel: string | null,
206 message: string | null,
pineafan6fb3e072022-05-20 19:27:23 +0100207 }
pineafan02ba0232022-07-24 22:16:15 +0100208 stats: {}
pineafan6fb3e072022-05-20 19:27:23 +0100209 logging: {
210 logs: {
211 enabled: boolean,
pineafan6702cef2022-06-13 17:52:37 +0100212 channel: string | null,
213 toLog: string | null,
pineafan6fb3e072022-05-20 19:27:23 +0100214 },
215 staff: {
pineafan6702cef2022-06-13 17:52:37 +0100216 channel: string | null,
pineafan73a7c4a2022-07-24 10:38:04 +0100217 },
218 attachments: {
219 channel: string | null,
220 saved: {} // {channelID+messageID: log url (string)}
pineafan6fb3e072022-05-20 19:27:23 +0100221 }
222 }
223 verify: {
224 enabled: boolean,
pineafan6702cef2022-06-13 17:52:37 +0100225 role: string | null,
pineafan6fb3e072022-05-20 19:27:23 +0100226 }
227 tickets: {
228 enabled: boolean,
pineafan6702cef2022-06-13 17:52:37 +0100229 category: string | null,
230 types: string | null,
pineafan6fb3e072022-05-20 19:27:23 +0100231 customTypes: string[],
pineafan6702cef2022-06-13 17:52:37 +0100232 useCustom: boolean,
233 supportRole: string | null,
pineafan6fb3e072022-05-20 19:27:23 +0100234 maxTickets: number
235 }
236 moderation: {
237 mute: {
238 timeout: boolean,
pineafan6702cef2022-06-13 17:52:37 +0100239 role: string | null,
240 text: string | null,
241 link: string | null
pineafan6fb3e072022-05-20 19:27:23 +0100242 },
243 kick: {
pineafan6702cef2022-06-13 17:52:37 +0100244 text: string | null,
245 link: string | null
pineafan6fb3e072022-05-20 19:27:23 +0100246 },
247 ban: {
pineafan6702cef2022-06-13 17:52:37 +0100248 text: string | null,
249 link: string | null
pineafan6fb3e072022-05-20 19:27:23 +0100250 },
251 softban: {
pineafan6702cef2022-06-13 17:52:37 +0100252 text: string | null,
253 link: string | null
pineafan6fb3e072022-05-20 19:27:23 +0100254 },
255 warn: {
pineafan6702cef2022-06-13 17:52:37 +0100256 text: string | null,
257 link: string | null
pineafan6fb3e072022-05-20 19:27:23 +0100258 },
259 role: {
pineafan6702cef2022-06-13 17:52:37 +0100260 role: string | null,
pineafan6fb3e072022-05-20 19:27:23 +0100261 }
262 }
263 tracks: {
264 name: string,
265 retainPrevious: boolean,
266 nullable: boolean,
267 track: string[],
268 manageableBy: string[]
269 }[]
270 roleMenu: {
271 enabled: boolean,
272 allowWebUI: boolean,
273 options: {
274 name: string,
275 description: string,
276 min: number,
277 max: number,
278 options: {
279 name: string,
pineafan4edb7762022-06-26 19:21:04 +0100280 description: string | null,
pineafan6fb3e072022-05-20 19:27:23 +0100281 role: string
282 }[]
283 }[]
284 }
285 tags: {}
pineafan4edb7762022-06-26 19:21:04 +0100286};
287
288export interface HistorySchema {
289 type: string,
290 guild: string,
291 user: string,
292 moderator: string | null,
293 reason: string,
294 occurredAt: Date,
295 before: string | null,
296 after: string | null,
297 amount: string | null
298}
299
300export interface ModNoteSchema {
301 guild: string,
302 user: string,
303 note: string
304}
305
pineafan73a7c4a2022-07-24 10:38:04 +0100306export interface PremiumSchema {
307 user: string,
308 level: number,
309 expires: Date,
310 appliesTo: string[]
pineafan4edb7762022-06-26 19:21:04 +0100311}