Merge branch 'slash-commands' of https://github.com/Navy-gif/New-Gbot into slash-commands

This commit is contained in:
nolan 2021-08-25 13:50:54 -07:00
commit 79a9ba618b
11 changed files with 568 additions and 1 deletions

View File

@ -24,7 +24,17 @@
},
"storage": {
"mongodb": {
"database": "galactic"
"database": "galactic",
"tables": [
"infractions",
"guilds",
"messages",
"attachments",
"users",
"permissions",
"role_cache",
"webhooks"
]
}
}
}

View File

@ -2,6 +2,7 @@ const { Client } = require('discord.js');
const { Logger, Intercom, EventHooker, LocaleLoader, Registry, Dispatcher, Resolver } = require('./client/');
const { Observer, Command } = require('./interfaces/');
const StorageManager = require('./storage/StorageManager.js');
const { DefaultGuild } = require('../constants/');
@ -20,6 +21,7 @@ class DiscordClient extends Client {
this.intercom = new Intercom(this);
this.logger = new Logger(this);
this.localeLoader = new LocaleLoader(this);
this.storageManager = new StorageManager(this, options.storage);
this.registry = new Registry(this);
this.dispatcher = new Dispatcher(this);

View File

@ -0,0 +1,65 @@
const path = require('path');
const fs = require('fs');
const chalk = require('chalk');
const { Provider } = require('./interfaces/');
class StorageManager {
constructor(client, options = {}) {
this.client = client;
this.providers = {};
this.options = options;
}
async initialize() {
// this.manager.logger.write('debug', "Initializing storage providers.");
const _providers = path.join(process.cwd(), "structure/storage/providers");
const providers = fs.readdirSync(_providers);
for (const _provider of providers) {
let provider = require(path.join(_providers, _provider));
provider = new provider(this.client, this.options);
this.providers[provider.name] = provider;
await provider.initialize();
this._log(`Provider ${chalk.bold(provider.name)} was ${chalk.bold('loaded')}.`, provider);
await provider.loadTables();
}
return this;
}
_getName(instance) {
if (instance instanceof Provider) return instance.name.substring(0, 5);
return `${instance.provider.name.substring(0, 5)}:${instance.name}`;
}
_error(info, instance = null) {
this.client.logger.error(`${chalk.bold(`[STORA]`)} ${instance ? `(${this._getName(instance)}) ` : ''}${info}`);
}
_log(info, instance = null) {
this.client.logger.info(`${chalk.bold(`[STORA]`)} ${instance ? `(${this._getName(instance)}) ` : ''}${info}`);
}
get mongodb() {
return this.providers.mongodb;
}
get mariadb() {
return this.providers.mariadb;
}
}
module.exports = StorageManager;

View File

@ -0,0 +1,14 @@
class MariadbTable {
constructor(client, provider, opts = {}) {
this.client = client;
this.provider = provider;
this.name = opts.name;
}
}
module.exports = MariadbTable;

View File

@ -0,0 +1,191 @@
const { ObjectId } = require('mongodb');
class MongodbTable {
constructor(client, provider, opts = {}) {
this.client = client;
this.provider = provider;
this.name = opts.name;
}
//Data Search
find(query, opts = {}) { //opts: { projection: ... }
query = this._handleData(query);
return new Promise((resolve, reject) => {
if (!this.provider._initialized) return reject(new Error('MongoDB is not connected.'));
this.collection.find(query, opts, async (error, cursor) => {
if (error) return reject(error);
return resolve(await cursor.toArray());
});
});
}
findOne(query, opts = {}) { //opts: { projection: ..., sort: ... }
query = this._handleData(query);
return new Promise((resolve, reject) => {
if (!this.provider._initialized) return reject(new Error('MongoDB is not connected.'));
this.collection.findOne(query, opts, async (error, item) => {
if (error) return reject(error);
return resolve(item);
});
});
}
aggregate(query) {
query = this._handleData(query);
return new Promise((resolve, reject) => {
if (!this.provider._initialized) return reject(new Error('MongoDB is not connected.'));
this.collection.aggregate(query, (error, item) => {
if (error) return reject(error);
return resolve(item.toArray());
});
});
}
random(query, amount = 1) {
query = this._handleData(query);
if (amount > 100) amount = 100;
return new Promise((resolve, reject) => {
if (!this.provider._initialized) return reject(new Error('MongoDB is not connected.'));
this.collection.aggregate([{ $match: query }, { $sample: { size: amount } }], (error, item) => {
if (error) return reject(error);
return resolve(item);
});
});
}
//Data Manipulation
insertOne(data) {
data = this._handleData(data);
return new Promise((resolve, reject) => {
if (!this.provider._initialized) return reject(new Error('MongoDB is not connected.'));
this.collection.insertOne(data, (error, result) => {
if (error) return reject(error);
return resolve(result);
});
});
}
//NOTE: insertMany?
deleteOne(query) {
query = this._handleData(query);
return new Promise((resolve, reject) => {
if (!this.provider._initialized) return reject(new Error('MongoDB is not connected.'));
this.collection.deleteOne(query, (error, result) => {
if (error) return reject(error);
return resolve(result);
});
});
}
deleteMany(query) {
query = this._handleData(query);
return new Promise((resolve, reject) => {
if (!this.provider._initialized) return reject(new Error('MongoDB is not connected.'));
this.collection.deleteMany(query, (error, result) => {
if (error) return reject(error);
return resolve(result);
});
});
}
updateOne(query, data, upsert = true) {
query = this._handleData(query);
return new Promise((resolve, reject) => {
if (!this.provider._initialized) return reject(new Error('MongoDB is not connected.'));
this.collection.updateOne(query, { $set: data }, { upsert }, async (error, result) => {
if (error) return reject(error);
const { matchedCount, upsertedCount, modifiedCount } = result;
return resolve({ matched: matchedCount, upserted: upsertedCount, modified: modifiedCount });
});
});
}
removeProperty(query, data) {
query = this._handleData(query);
return new Promise((resolve, reject) => {
if (!this.provider._initialized) return reject(new Error('MongoDB is not connected.'));
const unset = {};
for (const field of data) unset[field] = '';
this.collection.updateOne(query, { $unset: unset }, async (error, result) => {
if (error) return reject(error);
const { matchedCount, modifiedCount } = result;
return resolve({ matched: matchedCount, modified: modifiedCount });
});
});
}
push(query, data, upsert = true) {
query = this._handleData(query);
return new Promise((resolve, reject) => {
if (!this.provider._initialized) return reject(new Error('MongoDB is not connected.'));
this.collection.updateOne(query, { $push: data }, { upsert }, async (error, result) => {
if (error) return reject(error);
return resolve(result);
});
});
}
//Statistics
stats(options = {}) {
return new Promise((resolve, reject) => {
if (!this.provider._initialized) return reject(new Error('MongoDB is not connected.'));
this.collection.stats(options, (error, statistics) => {
if (error) return reject(error);
const { ns, size, count, avgObjSize, freeStorageSize, capped } = statistics;
return resolve({
index: ns,
averageSize: avgObjSize,
currentSize: size,
remainingSize: freeStorageSize,
maximumSize: size + freeStorageSize,
count,
capped
});
});
});
}
count(query, options = {}) {
return new Promise((resolve, reject) => {
if (!this.provider._initialized) return reject(new Error('MongoDB is not connected.'));
this.collection.countDocuments(query, options, (error, result) => {
if (error) return reject(error);
return resolve(result);
});
});
}
//Lazy Function
_handleData(data) { //Convert data._id to Mongo ObjectIds (gets converted to plaintext through shard communication)
if (data._id) {
if (typeof data._id === 'string') data._id = ObjectId(data._id);
else if (typeof data._id === 'object') data._id = {
$in: Object.values(data._id)[0].map((id) => {
return ObjectId(id);
})
};
}
return data;
}
//Getters
get collection() {
return this.provider.db.collection(this.name);
}
}
module.exports = MongodbTable;

View File

@ -0,0 +1,93 @@
const path = require('path');
const chalk = require('chalk');
const Util = require('../../../Util.js');
const MongodbTable = require('./MongodbTable.js');
const MariadbTable = require('./MariadbTable.js');
const Constants = {
Tables: {
'mongodb': MongodbTable,
'mariadb': MariadbTable
}
};
class Provider {
constructor(client, opts) {
if (!opts.config) throw new Error('No config file provided!');
this.config = opts.config[opts.name];
if (this.config && (!this.config.database || !this.config.host)) throw new Error('Invalid config file provided!' + JSON.stringify(this.config));
this.client = client;
this.storageManager = client.storageManager;
this.name = opts.name;
this.connection = null;
this.db = null;
this.tables = {};
this._initialized = false;
this._class = Constants.Tables[opts.name];
}
async loadTables() {
const directory = path.join(process.cwd(), 'structure/storage/');
const files = await Util.readdirRecursive(path.join(directory, `tables/${this.name}`));
const loaded = [];
for (const file of files) {
const func = require(file);
if (typeof func !== 'function') {
this.storageManager._error("Attempted to index an invalid function as a table.", this);
delete require.cache[file];
continue;
}
const table = new func(this.client, this);
if (this._class && !(table instanceof this._class)) {
this.storageManager._error("Attempted to load an invalid class as a table.", this);
delete require.cache[file];
continue;
}
loaded.push(await this.loadTable(table));
}
for (const table of this.config.tables) {
if (this.tables[table]) continue;
else loaded.push(await this.loadTable(new this._class(this.client, this, {
name: table
})));
}
return loaded;
}
async loadTable(table) {
if (this.tables[table.name]) {
this.storageManager._error("Attempted to load an existing table.", table);
return null;
}
this.tables[table.name] = table;
this.storageManager._log(`Table ${chalk.bold(table.name)} was ${chalk.bold('loaded')}.`, table);
return table;
}
_error(err) {
this.storageManager._error(err, this);
}
}
module.exports = Provider;

View File

@ -0,0 +1,5 @@
module.exports = {
Provider: require('./Provider.js'),
MongodbTable: require('./MongodbTable.js'),
MariadbTable: require('./MariadbTable.js')
};

View File

@ -0,0 +1,76 @@
const { Provider } = require('../interfaces/');
const MySQL = require('mysql');
class MariaDBProvider extends Provider {
constructor(client, config) {
super(client, {
name: 'mariadb',
config
});
}
async initialize() {
try {
this.db = MySQL.createPool(this.config);
this.db.on('connection', async (connection) => {
// this.manager.logger.log('MariaDB connected.');
connection.on('error', (err) => {
// this.manager.logger.error('MariaDB errored.', err);
});
connection.on('close', (data) => {
// this.manager.logger.log('MariaDB connection closed.', data);
});
});
this.loaded = true;
} catch (err) {
// this.manager.logger.error('MariaDB connection failed.', err);
}
return this;
}
close() {
this.db.end();
}
/**
* Query using SQL to MariaDB
*
* @param {string} query SQL query string.
* @param {array<Object>} values Array of values to replace ? with in the query string
* @returns {object} Returns an object containing the query result
* @memberof MariaDBProvider
*/
query(query, values) {
if (!this.loaded) throw new Error('MariaDB not connected');
return new Promise((resolve, reject) => {
this.db.query(query, values, (err, result) => {
if (err) reject(err);
resolve(result);
});
});
}
}
module.exports = MariaDBProvider;

View File

@ -0,0 +1,77 @@
const { Provider } = require('../interfaces/');
const { MongoClient } = require('mongodb');
class MongoDBProvider extends Provider {
constructor(client, config) {
super(client, {
name: 'mongodb',
config
});
}
async initialize() {
try {
this.connection = await MongoClient.connect(process.env.MONGODB_HOST + this.config.database, { useUnifiedTopology: true });
this.db = await this.connection.db(this.config.database);
return this._initialized = true; //eslint-disable-line no-return-assign
} catch (err) {
this._error(err);
return false;
}
}
stats(options = {}) {
return new Promise((resolve, reject) => {
if (!this._initialized) return reject(new Error('MongoDB is not connected.'));
this.db.stats(options, (error, result) => {
if (error) reject(error);
resolve(result);
});
});
}
get guilds() {
return this.tables.guilds;
}
get permissions() {
return this.tables.permissions;
}
get infractions() {
return this.tables.infractions;
}
get messages() {
return this.tables.messages;
}
get attachments() {
return this.tables.attachments;
}
get users() {
return this.tables.users;
}
// eslint-disable-next-line camelcase
get role_cache() {
return this.tables.role_cache;
}
get webhooks() {
return this.tables.webhooks;
}
}
module.exports = MongoDBProvider;

View File

@ -0,0 +1,17 @@
const { MariadbTable } = require('../../interfaces/');
class InfractionsTable extends MariadbTable {
constructor(client, provider) {
super(client, provider, {
name: 'activity'
});
this.provider = provider;
}
}
module.exports = InfractionsTable;

View File

@ -0,0 +1,17 @@
const { MongodbTable } = require('../../interfaces/');
class InfractionsTable extends MongodbTable {
constructor(client, provider) {
super(client, provider, {
name: 'infractions'
});
this.provider = provider;
}
}
module.exports = InfractionsTable;