diff --git a/TODO b/TODO index 3316dd2..91d025a 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,2 @@ Server rules verificationRequired on welcome -!!Premium diff --git a/src/config/format.ts b/src/config/format.ts index a80094e..e32bef6 100644 --- a/src/config/format.ts +++ b/src/config/format.ts @@ -2,7 +2,7 @@ import fs from "fs"; import * as readLine from "node:readline/promises"; -const defaultDict: Record = { +const defaultDict: Record> = { developmentToken: "Your development bot token (Used for testing in one server, rather than production)", developmentGuildID: "Your development guild ID", enableDevelopment: true, @@ -15,7 +15,17 @@ const defaultDict: Record = { userContextFolder: "Your built user context folder (usually dist/context/users)", verifySecret: "If using verify, enter a code here which matches the secret sent back by your website. You can use a random code if you do not have one already. (Optional)", - mongoUrl: "Your Mongo connection string, e.g. mongodb://127.0.0.1:27017", + mongoUsername: "Your Mongo username (optional)", + mongoPassword: "Your Mongo password (optional)", + mongoDatabase: "Your Mongo database name (optional, e.g. Nucleus)", + mongoHost: "Your Mongo host (optional, e.g. localhost:27017)", + mongoOptions: { + username: "", + password: "", + database: "", + host: "", + authSource: "", + }, baseUrl: "Your website where buttons such as Verify and Role menu will link to, e.g. https://example.com/", pastebinApiKey: "An API key for pastebin (optional)", pastebinUsername: "Your pastebin username (optional)", @@ -97,6 +107,9 @@ export default async function (walkthrough = false) { json[key] = toWrite; break; } + case "mongoOptions": { + break; + } default: { json[key] = await getInput(`\x1b[36m${key} \x1b[0m(\x1b[35m${defaultDict[key]}\x1b[0m) > `); } @@ -107,8 +120,7 @@ export default async function (walkthrough = false) { } } if (walkthrough && !(json["mongoUrl"] ?? false)) json["mongoUrl"] = "mongodb://127.0.0.1:27017"; - if (!((json["mongoUrl"] as string | undefined) ?? "").endsWith("/")) json["mongoUrl"] += "/"; - if (!((json["baseUrl"] as string | undefined) ?? "").endsWith("/")) json["baseUrl"] += "/"; + if (!((json["baseUrl"] as string | undefined) ?? "").endsWith("/")) (json["baseUrl"] as string) += "/"; let hosts; try { hosts = fs.readFileSync("/etc/hosts", "utf8").toString().split("\n"); @@ -125,6 +137,13 @@ export default async function (walkthrough = false) { } json["mongoUrl"] = (json["mongoUrl"]! as string).replace("localhost", localhost!); json["baseUrl"] = (json["baseUrl"]! as string).replace("localhost", localhost!); + json["mongoOptions"] = { + username: json["username"] as string, + password: json["password"] as string, + database: json["database"] as string, + host: json["host"] as string, + authSource: json["authSource"] as string, + }; fs.writeFileSync("./src/config/main.ts", "export default " + JSON.stringify(json, null, 4) + ";"); diff --git a/src/config/main.d.ts b/src/config/main.d.ts index be6a253..6549234 100644 --- a/src/config/main.d.ts +++ b/src/config/main.d.ts @@ -10,7 +10,12 @@ declare const config: { messageContextFolder: string, userContextFolder: string, verifySecret: string, - mongoUrl: string, + mongoOptions: { + username: string, + password: string, + database: string, + host: string, + }, baseUrl: string, pastebinApiKey: string, pastebinUsername: string, diff --git a/src/utils/database.ts b/src/utils/database.ts index 0688888..1e2d3ba 100644 --- a/src/utils/database.ts +++ b/src/utils/database.ts @@ -5,10 +5,22 @@ import config from "../config/main.js"; import client from "../utils/client.js"; import * as crypto from "crypto"; -const mongoClient = new MongoClient(config.mongoUrl); +// config.mongoOptions.host, { +// auth: { +// username: config.mongoOptions.username, +// password: config.mongoOptions.password +// }, +// authSource: config.mongoOptions.authSource +// } +// mongodb://emails:SweetLife2023!!@127.0.0.1:28180/saveEmail?retryWrites=true&w=majority +const username = encodeURIComponent(config.mongoOptions.username); +const password = encodeURIComponent(config.mongoOptions.password); +const mongoClient = new MongoClient(username ? `mongodb://${username}:${password}@${config.mongoOptions.host}` : `mongodb://${config.mongoOptions.host}`, {authSource: "admin"}); await mongoClient.connect(); const database = mongoClient.db(); +const collectionOptions = { authdb: "admin" }; + export class Guilds { guilds: Collection; defaultData: GuildConfig | null; @@ -25,11 +37,13 @@ export class Guilds { } async read(guild: string): Promise { + console.log("Guild read") const entry = await this.guilds.findOne({ id: guild }); return Object.assign({}, this.defaultData, entry); } async write(guild: string, set: object | null, unset: string[] | string = []) { + console.log("Guild write") // eslint-disable-next-line @typescript-eslint/no-explicit-any const uo: Record = {}; if (!Array.isArray(unset)) unset = [unset]; @@ -44,6 +58,7 @@ export class Guilds { // eslint-disable-next-line @typescript-eslint/no-explicit-any async append(guild: string, key: string, value: any) { + console.log("Guild append") if (Array.isArray(value)) { await this.guilds.updateOne( { id: guild }, @@ -70,6 +85,7 @@ export class Guilds { value: any, innerKey?: string | null ) { + console.log("Guild remove") if (innerKey) { await this.guilds.updateOne( { id: guild }, @@ -98,6 +114,7 @@ export class Guilds { } async delete(guild: string) { + console.log("Guild delete") await this.guilds.deleteOne({ id: guild }); } } @@ -114,6 +131,13 @@ interface TranscriptEmbed { text: string; iconURL?: string; }; + color?: number; + timestamp?: string; + author?: { + name: string; + iconURL?: string; + url?: string; + }; } interface TranscriptComponent { @@ -178,17 +202,19 @@ export class Transcript { } async create(transcript: Omit) { + console.log("Transcript create") let code; do { code = crypto.randomBytes(64).toString("base64").replace(/=/g, "").replace(/\//g, "_").replace(/\+/g, "-"); } while (await this.transcripts.findOne({ code: code })); - const doc = await this.transcripts.insertOne(Object.assign(transcript, { code: code })); + const doc = await this.transcripts.insertOne(Object.assign(transcript, { code: code }), collectionOptions); if(doc.acknowledged) return code; else return null; } async read(code: string) { + console.log("Transcript read") return await this.transcripts.findOne({ code: code }); } @@ -233,7 +259,7 @@ export class Transcript { topRole: { color: message.member!.roles.highest.color }, - iconURL: member!.user.displayAvatarURL({ forceStatic: true}), + iconURL: message.member!.user.displayAvatarURL({ forceStatic: true}), bot: message.author.bot }, createdTimestamp: message.createdTimestamp @@ -252,10 +278,17 @@ export class Transcript { inline: field.inline ?? false } }); + if (embed.color) obj.color = embed.color; + if (embed.timestamp) obj.timestamp = embed.timestamp if (embed.footer) obj.footer = { text: embed.footer.text, }; if (embed.footer?.iconURL) obj.footer!.iconURL = embed.footer.iconURL; + if (embed.author) obj.author = { + name: embed.author.name + }; + if (embed.author?.iconURL) obj.author!.iconURL = embed.author.iconURL; + if (embed.author?.url) obj.author!.url = embed.author.url; return obj; }); if (message.components.length > 0) msg.components = message.components.map(component => component.components.map(child => { @@ -347,6 +380,7 @@ export class History { after?: string | null, amount?: string | null ) { + console.log("History create"); await this.histories.insertOne({ type: type, guild: guild, @@ -357,10 +391,11 @@ export class History { before: before ?? null, after: after ?? null, amount: amount ?? null - }); + }, collectionOptions); } async read(guild: string, user: string, year: number) { + console.log("History read"); const entry = (await this.histories .find({ guild: guild, @@ -375,6 +410,7 @@ export class History { } async delete(guild: string) { + console.log("History delete"); await this.histories.deleteMany({ guild: guild }); } } @@ -394,14 +430,17 @@ export class ScanCache { } async read(hash: string) { + console.log("ScanCache read"); return await this.scanCache.findOne({ hash: hash }); } async write(hash: string, data: boolean, tags?: string[]) { - await this.scanCache.insertOne({ hash: hash, data: data, tags: tags ?? [], addedAt: new Date() }); + console.log("ScanCache write"); + await this.scanCache.insertOne({ hash: hash, data: data, tags: tags ?? [], addedAt: new Date() }, collectionOptions); } async cleanup() { + console.log("ScanCache cleanup"); await this.scanCache.deleteMany({ addedAt: { $lt: new Date(Date.now() - (1000 * 60 * 60 * 24 * 31)) }, hash: { $not$text: "http"} }); } } @@ -414,10 +453,12 @@ export class PerformanceTest { } async record(data: PerformanceDataSchema) { + console.log("PerformanceTest record"); data.timestamp = new Date(); - await this.performanceData.insertOne(data); + await this.performanceData.insertOne(data, collectionOptions); } async read() { + console.log("PerformanceTest read"); return await this.performanceData.find({}).toArray(); } } @@ -441,15 +482,18 @@ export class ModNotes { } async create(guild: string, user: string, note: string | null) { + console.log("ModNotes create"); await this.modNotes.updateOne({ guild: guild, user: user }, { $set: { note: note } }, { upsert: true }); } async read(guild: string, user: string) { + console.log("ModNotes read"); const entry = await this.modNotes.findOne({ guild: guild, user: user }); return entry?.note ?? null; } async delete(guild: string) { + console.log("ModNotes delete"); await this.modNotes.deleteMany({ guild: guild }); } } @@ -465,20 +509,24 @@ export class Premium { } async updateUser(user: string, level: number) { + console.log("Premium updateUser"); if(!(await this.userExists(user))) await this.createUser(user, level); await this.premium.updateOne({ user: user }, { $set: { level: level } }, { upsert: true }); } async userExists(user: string): Promise { + console.log("Premium userExists"); const entry = await this.premium.findOne({ user: user }); return entry ? true : false; } async createUser(user: string, level: number) { - await this.premium.insertOne({ user: user, appliesTo: [], level: level }); + console.log("Premium createUser"); + await this.premium.insertOne({ user: user, appliesTo: [], level: level }, collectionOptions); } async hasPremium(guild: string): Promise<[boolean, string, number, boolean] | null> { + console.log("Premium hasPremium"); // [Has premium, user giving premium, level, is mod: if given automatically] const cached = this.cache.get(guild); if (cached && cached[4].getTime() < Date.now()) return [cached[0], cached[1], cached[2], cached[3]]; @@ -518,12 +566,14 @@ export class Premium { } async fetchUser(user: string): Promise { + console.log("Premium fetchUser"); const entry = await this.premium.findOne({ user: user }); if (!entry) return null; return entry; } async checkAllPremium(member?: GuildMember) { + console.log("Premium checkAllPremium"); const entries = await this.premium.find({}).toArray(); if(member) { const entry = entries.find(e => e.user === member.id); @@ -577,12 +627,14 @@ export class Premium { } async addPremium(user: string, guild: string) { + console.log("Premium addPremium"); const { level } = (await this.fetchUser(user))!; this.cache.set(guild, [true, user, level, false, new Date(Date.now() + this.cacheTimeout)]); return this.premium.updateOne({ user: user }, { $addToSet: { appliesTo: guild } }, { upsert: true }); } removePremium(user: string, guild: string) { + console.log("Premium removePremium"); this.cache.set(guild, [false, "", 0, false, new Date(Date.now() + this.cacheTimeout)]); return this.premium.updateOne({ user: user }, { $pull: { appliesTo: guild } }); } diff --git a/src/utils/eventScheduler.ts b/src/utils/eventScheduler.ts index 5c461ad..a79a260 100644 --- a/src/utils/eventScheduler.ts +++ b/src/utils/eventScheduler.ts @@ -10,7 +10,7 @@ class EventScheduler { constructor() { this.agenda = new Agenda({ db: { - address: config.mongoUrl + "Nucleus", + address: config.mongoOptions.host, collection: "eventScheduler" } });