summaryrefslogtreecommitdiff
path: root/src/database
diff options
context:
space:
mode:
author8cy <[email protected]>2020-07-19 04:36:14 -0700
committer8cy <[email protected]>2020-07-19 04:36:14 -0700
commitf00bcd79995bc8d7af3f297cab8085fd28b89435 (patch)
tree9a4ddd1f0a945b1f20d98c20e621c0d8a69bb8ad /src/database
downloadwater-waifu-re-f00bcd79995bc8d7af3f297cab8085fd28b89435.tar.xz
water-waifu-re-f00bcd79995bc8d7af3f297cab8085fd28b89435.zip
:star:
Diffstat (limited to 'src/database')
-rw-r--r--src/database/index.ts5
-rw-r--r--src/database/models/Guild.ts22
-rw-r--r--src/database/models/Reaction.ts41
-rw-r--r--src/database/structures/SettingsProvider.ts211
-rw-r--r--src/database/util/constants.ts8
5 files changed, 287 insertions, 0 deletions
diff --git a/src/database/index.ts b/src/database/index.ts
new file mode 100644
index 0000000..29401cb
--- /dev/null
+++ b/src/database/index.ts
@@ -0,0 +1,5 @@
+import Guild from './models/Guild';
+import Reaction from './models/Reaction';
+import SettingsProvider from './structures/SettingsProvider';
+
+export { SettingsProvider, Guild, Reaction };
diff --git a/src/database/models/Guild.ts b/src/database/models/Guild.ts
new file mode 100644
index 0000000..7ed73d5
--- /dev/null
+++ b/src/database/models/Guild.ts
@@ -0,0 +1,22 @@
+import { Document, Schema, model } from 'mongoose';
+
+export interface Guild extends Document {
+ id: string;
+ prefix: string;
+ premium: boolean;
+ expiresAt: Date;
+}
+
+const Guild: Schema = new Schema(
+ {
+ id: String,
+ prefix: String,
+ premium: Boolean,
+ expiresAt: Date,
+ },
+ {
+ strict: false,
+ },
+);
+
+export default model<Guild>('Guild', Guild);
diff --git a/src/database/models/Reaction.ts b/src/database/models/Reaction.ts
new file mode 100644
index 0000000..f72a799
--- /dev/null
+++ b/src/database/models/Reaction.ts
@@ -0,0 +1,41 @@
+import { Document, Schema, model } from 'mongoose';
+
+export interface Reaction extends Document {
+ guildID: string;
+ messageID: string;
+ channelID: string;
+ userID: string;
+ id: string;
+ emoji: string;
+ emojiType: string;
+ roleID: string;
+ uses: number;
+ expiresAt?: Date;
+ type: number;
+ active: boolean;
+}
+
+const Reaction: Schema = new Schema(
+ {
+ guildID: String,
+ messageID: String,
+ channelID: String,
+ userID: String,
+ id: String,
+ emoji: String,
+ emojiType: String,
+ roleID: String,
+ uses: Number,
+ expiresAt: Date,
+ type: Number,
+ active: {
+ type: Boolean,
+ default: true,
+ },
+ },
+ {
+ strict: false,
+ },
+);
+
+export default model<Reaction>('Reaction', Reaction);
diff --git a/src/database/structures/SettingsProvider.ts b/src/database/structures/SettingsProvider.ts
new file mode 100644
index 0000000..8ca6b92
--- /dev/null
+++ b/src/database/structures/SettingsProvider.ts
@@ -0,0 +1,211 @@
+import { Collection } from 'discord.js';
+import { connect, Model, connection, Connection } from 'mongoose';
+import { Logger } from 'winston';
+import ReactionModel, { Reaction } from '../models/Reaction';
+import GuildModel, { Guild } from '../models/Guild';
+import { MONGO_EVENTS } from '../util/constants';
+import ReactionClient from '../../bot/client/ReactionClient';
+
+let i = 0;
+
+/**
+ * The key, model and cached collection of a database model.
+ * @interface
+ */
+interface Combo {
+ key: string;
+ model: Model<any>;
+ cache: Collection<string, any>;
+}
+
+/**
+ * The Settings Provider that handles all database reads and rights.
+ * @private
+ */
+export default class SettingsProvider {
+ protected readonly client: ReactionClient;
+
+ protected readonly guilds: Collection<string, Guild> = new Collection();
+ protected readonly reactions: Collection<string, Reaction> = new Collection();
+
+ protected readonly GuildModel = GuildModel;
+ protected readonly ReactionModel = ReactionModel;
+
+ /**
+ *
+ * @param {GiveawayClient} client - The extended Akairo Client
+ */
+ public constructor(client: ReactionClient) {
+ this.client = client;
+ }
+
+ /**
+ * Retuns all the collection caches.
+ * @returns {Object}
+ */
+ public get cache() {
+ return {
+ guilds: this.guilds,
+ reactions: this.reactions,
+ };
+ }
+
+ /**
+ * Returns the database combos
+ * @returns {Combo[]}
+ */
+ public get combos(): Combo[] {
+ return [
+ {
+ key: 'guild',
+ model: this.GuildModel,
+ cache: this.guilds,
+ },
+ {
+ key: 'reaction',
+ model: this.ReactionModel,
+ cache: this.reactions,
+ },
+ ];
+ }
+
+ /**
+ * Creates a new database document with the provided collection name and data.
+ * @param {string} type - The collection name
+ * @param {object} data - The data for the new document
+ * @returns {Docuement}
+ */
+ public async new(type: 'guild', data: Partial<Guild>): Promise<Guild>;
+ public async new(type: 'reaction', data: Partial<Reaction>): Promise<Reaction>;
+ public async new(type: string, data: object): Promise<object> {
+ const combo = this.combos.find(c => c.key === type);
+ if (combo) {
+ const document = new combo.model(data);
+ await document.save();
+ this.client.logger.data(`[DATABASE] Made new ${combo.model.modelName} document with ID of ${document._id}.`);
+ combo.cache.set(document.id, document);
+ return document;
+ }
+ throw Error(`"${type}" is not a valid model key.`);
+ }
+
+ /**
+ * Updates the a database document's data.
+ * @param {Types} type - The collection name
+ * @param {object} key - The search paramaters for the document
+ * @param {object} data - The data you wish to overwrite in the update
+ * @returns {Promise<Faction | Guild | null>}
+ */
+ public async set(type: 'guild', key: Partial<Guild>, data: Partial<Guild>): Promise<Guild | null>;
+ public async set(type: 'reaction', key: Partial<Reaction>, data: Partial<Reaction>): Promise<Reaction | null>;
+ public async set(type: string, key: object, data: object): Promise<object | null> {
+ const combo = this.combos.find(c => c.key === type);
+ if (combo) {
+ const document = await combo.model.findOneAndUpdate(key, { $set: data }, { new: true });
+ if (document) {
+ this.client.logger.verbose(`[DATABASE] Edited ${combo.model.modelName} document with ID of ${document._id}.`);
+ combo.cache.set(document.id, document);
+ return document;
+ }
+ return null;
+ }
+ throw Error(`"${type}" is not a valid model key.`);
+ }
+
+ /**
+ * Removes a database document.
+ * @param {Types} type - The collection name
+ * @param {object} data - The search paramaters for the document
+ * @returns {Promise<Faction | Guild | null>>} The document that was removed, if any.
+ */
+ public async remove(type: 'guild', data: Partial<Guild>): Promise<Guild | null>;
+ public async remove(type: 'user', data: Partial<Reaction>): Promise<Reaction | null>;
+ public async remove(type: string, data: object): Promise<object | null> {
+ const combo = this.combos.find(c => c.key === type);
+ if (combo) {
+ const document = await combo.model.findOneAndRemove(data);
+ if (document) {
+ this.client.logger.verbose(`[DATABASE] Edited ${combo.model.modelName} document with ID of ${document._id}.`);
+ combo.cache.delete(document.id);
+ return document;
+ }
+ return null;
+ }
+ throw Error(`"${type}" is not a valid model key.`);
+ }
+
+ /**
+ * Caching all database documents.
+ * @returns {number} The amount of documents cached total.
+ * @private
+ */
+ private async _cacheAll(): Promise<number> {
+ for (const combo of this.combos) await this._cache(combo);
+ return i;
+ }
+
+ /**
+ * Caching each collection's documents.
+ * @param {Combo} combo - The combo name
+ * @returns {number} The amount of documents cached from that collection.
+ * @private
+ */
+ private async _cache(combo: Combo): Promise<any> {
+ const items = await combo.model.find();
+ for (const i of items) combo.cache.set(i.id, i);
+ this.client.logger.verbose(
+ `[DATABASE]: Cached ${items.length.toLocaleString('en-US')} items from ${combo.model.modelName}.`,
+ );
+ return (i += items.length);
+ }
+
+ /**
+ * Connect to the database
+ * @param {string} url - the mongodb uri
+ * @returns {Promise<number | Logger>} Returns a
+ */
+ private async _connect(url: string | undefined): Promise<Logger | number> {
+ if (url) {
+ const start = Date.now();
+ try {
+ await connect(url, {
+ useCreateIndex: true,
+ useNewUrlParser: true,
+ useFindAndModify: false,
+ useUnifiedTopology: true,
+ });
+ } catch (err) {
+ this.client.logger.error(`[DATABASE] Error when connecting to MongoDB:\n${err.stack}`);
+ process.exit(1);
+ }
+ return this.client.logger.verbose(`[DATABASE] Connected to MongoDB in ${Date.now() - start}ms.`);
+ }
+ this.client.logger.error('[DATABASE] No MongoDB url provided!');
+ return process.exit(1);
+ }
+
+ /**
+ * Adds all the listeners to the mongo connection.
+ * @param connection - The mongoose connection
+ * @returns {void}
+ * @private
+ */
+ private _addListeners(connection: Connection): void {
+ for (const [event, msg] of Object.entries(MONGO_EVENTS)) {
+ connection.on(event, () => this.client.logger.data(`[DATABASE]: ${msg}`));
+ }
+ }
+
+ /**
+ * Starts the Settings Provider
+ * @returns {SettingsProvider}
+ */
+ public async init(): Promise<this> {
+ this._addListeners(connection);
+ await this._connect(process.env.MONGO);
+ this.client.logger.verbose(`[DATABASE]: Now caching ${this.combos.length} schema documents.`);
+ await this._cacheAll();
+ this.client.logger.info(`[DATABASE] [LAUNCHED] Successfully connected and cached ${i} documents.`);
+ return this;
+ }
+}
diff --git a/src/database/util/constants.ts b/src/database/util/constants.ts
new file mode 100644
index 0000000..911a28a
--- /dev/null
+++ b/src/database/util/constants.ts
@@ -0,0 +1,8 @@
+export const MONGO_EVENTS = {
+ connecting: 'Connecting to MongoDB...',
+ connected: 'Successfully connected to MongoDB.',
+ disconnecting: 'Disconnecting from MongoDB...',
+ disconnected: 'Disconnected from MongoDB...',
+ close: 'MongoDB connection closed.',
+ reconnected: 'Successfully reconnected to MongoDB.',
+} as { [key: string]: string };