forked from Galactic/galactic-bot
fucking mess
This commit is contained in:
parent
da8871b0d6
commit
34a006e351
@ -1,13 +1,17 @@
|
||||
const { EventEmitter } = require('events');
|
||||
const options = require('./options.json');
|
||||
|
||||
const ShardManager = require('./middleware/ShardManager.js');
|
||||
const StorageManager = require('./storage/StorageManager.js');
|
||||
|
||||
const { Command, Setting, Inhibitor } = require('./structure/interfaces/');
|
||||
|
||||
class Manager extends EventEmitter {
|
||||
|
||||
constructor(options) {
|
||||
|
||||
this.registry = new Registry(this);
|
||||
this.shardManager = new ShardManager(this, './middleware/client/DiscordClient.js', options.shard)
|
||||
.spawn();
|
||||
this.storageManager = new StorageManager(this, options.storage)
|
||||
.initialize();
|
||||
|
||||
|
66
Registry.js
66
Registry.js
@ -1,8 +1,72 @@
|
||||
const { EventEmitter } = require('events');
|
||||
const { Collection, Util } = require('./util/');
|
||||
|
||||
class Registry extends EventEmitter {
|
||||
|
||||
|
||||
constructor(manager) {
|
||||
|
||||
this.components = new Collection();
|
||||
|
||||
}
|
||||
|
||||
async loadComponents() {
|
||||
|
||||
const directory = path.join(process.cwd(), 'structure/', dir); //Finds directory of component folder relative to current working directory.
|
||||
const files = Util.readdirRecursive(directory); //Loops through all folders in the directory and returns the files.
|
||||
|
||||
const loaded = [];
|
||||
|
||||
for(const path of files) {
|
||||
const func = require(path);
|
||||
if(typeof func !== 'function') {
|
||||
//this.client.logger.error("Attempted to index an invalid function as a component.");
|
||||
continue;
|
||||
}
|
||||
|
||||
const component = new func(this.client); //Instantiates the component class.
|
||||
if(classToHandle && !(component instanceof classToHandle)) {
|
||||
//this.client.logger.error("Attempted to load an invalid class.");
|
||||
continue;
|
||||
}
|
||||
|
||||
loaded.push(await this.loadComponent(component, path));
|
||||
|
||||
}
|
||||
|
||||
return loaded;
|
||||
|
||||
}
|
||||
|
||||
async loadComponent(component, directory) {
|
||||
|
||||
if(!(component instanceof Component)) {
|
||||
//this.client.logger.error("Attempted to load an invalid component.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if(this.components.has(component.resolveable)) {
|
||||
//this.client.logger.error("Attempted to reload an existing component.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if(directory) component.directory = directory;
|
||||
if(component.module && typeof component.module === 'string') { //Sets modules or "groups" for each component, specified by their properties.
|
||||
let module = this.components.get(`module:${component.module}`);
|
||||
if(!module) module = await this.loadComponent(new Module(this.manager, { name: component.module }));
|
||||
this.components.set(module.resolveable, module);
|
||||
|
||||
component.module = module;
|
||||
module.components.set(component.resolveable, component);
|
||||
}
|
||||
|
||||
this.components.set(component.resolveable, component);
|
||||
this.emit('componentUpdate', { component, type: 'LOAD' });
|
||||
return component;
|
||||
}
|
||||
|
||||
async unloadComponent(component) {
|
||||
this.components.delete(component.id);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
9
index.js
Normal file
9
index.js
Normal file
@ -0,0 +1,9 @@
|
||||
const Manager = require('./Manager.js');
|
||||
const options = require('./options.json');
|
||||
|
||||
new Manager(options)
|
||||
.initialize();
|
||||
|
||||
process.on("unhandledRejection", (error) => {
|
||||
console.error("Unhandled promise rejection:", error); //eslint-disable-line no-console
|
||||
});
|
@ -1,7 +1,219 @@
|
||||
const { EventEmitter } = require('events');
|
||||
/* Adopted from Discord.js */
|
||||
|
||||
const path = require('path');
|
||||
const EventEmitter = require('events');
|
||||
|
||||
const Util = require('../../util/Util.js');
|
||||
|
||||
let childProcess = null;
|
||||
let Worker = null;
|
||||
|
||||
class Shard extends EventEmitter {
|
||||
|
||||
constructor(shardManager, id) {
|
||||
|
||||
super();
|
||||
|
||||
this.manager = shardManager.manager;
|
||||
|
||||
if(shardManager.mode === 'process') childProcess = require('child_process');
|
||||
else if(shardManager.mode === 'worker') Worker = require('worker_threads').Worker;
|
||||
|
||||
this.shardManager = shardManager;
|
||||
this.id = id;
|
||||
this.args = shardManager.shardArgs || [];
|
||||
this.execArgv = shardManager.execArgv;
|
||||
this.env = Object.assign({}, process.env, {
|
||||
SHARDING_shardManager: true,
|
||||
SHARDS: this.id,
|
||||
TOTAL_SHARD_COUNT: this.shardManager.totalShards,
|
||||
DISCORD_TOKEN: this.shardManager.token
|
||||
});
|
||||
|
||||
this.ready = false;
|
||||
this.process = null;
|
||||
this.worker = null;
|
||||
|
||||
this._evals = new Map();
|
||||
this._fetches = new Map();
|
||||
|
||||
this._exitListener = this._handleExit.bind(this, undefined);
|
||||
|
||||
}
|
||||
|
||||
async spawn(waitForReady = true) {
|
||||
if(this.process) throw new Error(`[shard${this.id}] Sharding process already exists.`);
|
||||
if(this.worker) throw new Error(`[shard${this.id}] Sharding worker already exists.`);
|
||||
|
||||
if(this.shardManager.mode === 'process') {
|
||||
this.process = childProcess.fork(path.resolve(this.shardManager.file), this.args, {
|
||||
env: this.env,
|
||||
execArgv: this.execArgv
|
||||
})
|
||||
.on('message', this._handleMessage.bind(this))
|
||||
.on('exit', this._exitListener);
|
||||
} else if(this.shardManager.mode === 'worker') {
|
||||
this.worker = new Worker(path.resolve(this.shardManager.file), { workerData: this.env })
|
||||
.on('message', this._handleMessage.bind(this))
|
||||
.on('exit', this._exitListener);
|
||||
}
|
||||
|
||||
this.emit('spawn', this.process || this.worker);
|
||||
|
||||
if(!waitForReady) return this.process || this.worker;
|
||||
await new Promise((resolve, reject) => {
|
||||
this.once('ready', resolve);
|
||||
this.once('disconnect', () => reject(new Error(`[shard${this.id}] Shard disconnected while readying.`)));
|
||||
this.once('death', () => reject(new Error(`[shard${this.id}] Shard died while readying.`)));
|
||||
setTimeout(() => reject(new Error(`[shard${this.id}] Shard timed out while readying.`)), 30000);
|
||||
});
|
||||
|
||||
return this.process || this.worker;
|
||||
|
||||
}
|
||||
|
||||
kill() {
|
||||
if(this.process) {
|
||||
this.process.removeListener('exit', this._exitListener);
|
||||
this.process.kill();
|
||||
} else {
|
||||
this.worker.removeListener('exit', this._exitListener);
|
||||
this.worker.terminate();
|
||||
}
|
||||
|
||||
this._handleExit(false);
|
||||
|
||||
}
|
||||
|
||||
async respawn(delay = 500, waitForReady = true) {
|
||||
this.kill();
|
||||
if(delay > 0) await Util.delayFor(delay);
|
||||
return this.spawn(waitForReady);
|
||||
}
|
||||
|
||||
send(message) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if(this.process) {
|
||||
this.process.send(message, error => {
|
||||
if(error) reject(error); else resolve(this);
|
||||
});
|
||||
} else {
|
||||
this.worker.postMessage(message);
|
||||
resolve(this);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fetchClientValue(prop) {
|
||||
if(this._fetches.has(prop)) return this._fetches.get(prop);
|
||||
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
const child = this.process || this.worker;
|
||||
|
||||
const listener = message => {
|
||||
if(!message || message._fetchProp !== prop) return;
|
||||
child.removeListener('message', listener);
|
||||
this._fetches.delete(prop);
|
||||
resolve(message._result);
|
||||
};
|
||||
child.on('message', listener);
|
||||
|
||||
this.send({ _fetchProp: prop }).catch(err => {
|
||||
child.removeListener('message', listener);
|
||||
this._fetches.delete(prop);
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
|
||||
this._fetches.set(prop, promise);
|
||||
return promise;
|
||||
|
||||
}
|
||||
|
||||
eval(script) {
|
||||
|
||||
if(this._evals.has(script)) return this._evals.get(script);
|
||||
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
const child = this.process || this.worker;
|
||||
|
||||
const listener = message => {
|
||||
if(!message || message._eval !== script) return;
|
||||
child.removeListener('message', listener);
|
||||
this._evals.delete(script);
|
||||
if(!message._error) resolve(message._result); else reject(new Error(message._error));
|
||||
};
|
||||
child.on('message', listener);
|
||||
|
||||
const _eval = typeof script === 'function' ? `(${script})(this)` : script;
|
||||
this.send({ _eval }).catch(err => {
|
||||
child.removeListener('message', listener);
|
||||
this._evals.delete(script);
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
|
||||
this._evals.set(script, promise);
|
||||
return promise;
|
||||
|
||||
}
|
||||
|
||||
_handleMessage(message) {
|
||||
if(message) {
|
||||
if(message._ready) { //Shard ready
|
||||
this.ready = true;
|
||||
this.emit('ready');
|
||||
return;
|
||||
}
|
||||
if(message._disconnect) { //Shard disconnected
|
||||
this.ready = false;
|
||||
this.emit('disconnect');
|
||||
return;
|
||||
}
|
||||
if(message._reconnecting) { //Shard attempting to reconnect
|
||||
this.ready = false;
|
||||
this.emit('reconnecting');
|
||||
return;
|
||||
}
|
||||
if(message._sFetchProp) { //Shard requesting property fetch
|
||||
this.shardManager.fetchClientValues(message._sFetchProp).then(
|
||||
results => this.send({ _sFetchProp: message._sFetchProp, _result: results }),
|
||||
err => this.send({ _sFetchProp: message._sFetchProp, _error: Util.makePlainError(err) })
|
||||
);
|
||||
return;
|
||||
}
|
||||
if(message._sEval) { //Shard requesting eval broadcast
|
||||
this.shardManager.broadcastEval(message._sEval).then(
|
||||
results => this.send({ _sEval: message._sEval, _result: results }),
|
||||
err => this.send({ _sEval: message._sEval, _error: Util.makePlainError(err) })
|
||||
);
|
||||
return;
|
||||
}
|
||||
if(message._sRespawnAll) { //Shard requesting to respawn all shards.
|
||||
const { shardDelay, respawnDelay, waitForReady } = message._sRespawnAll;
|
||||
this.shardManager.respawnAll(shardDelay, respawnDelay, waitForReady).catch(() => {
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.shardManager.emit('message', this, message);
|
||||
|
||||
}
|
||||
|
||||
_handleExit(respawn = this.shardManager.respawn) {
|
||||
this.emit('death', this.process || this.worker);
|
||||
|
||||
this.ready = false;
|
||||
this.process = null;
|
||||
this.worker = null;
|
||||
this._evals.clear();
|
||||
this._fetches.clear();
|
||||
|
||||
if(respawn) this.spawn().catch(err => this.emit('error', err));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Shard;
|
@ -1,3 +1,156 @@
|
||||
class ShardManager {
|
||||
|
||||
}
|
||||
/* Adopted from Discord.js */
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const EventEmitter = require('events');
|
||||
|
||||
const Shard = require('./Shard.js');
|
||||
const Collection = require('../../util/interfaces/Collection.js');
|
||||
const Util = require('../../util/Util.js');
|
||||
|
||||
class ShardManager extends EventEmitter {
|
||||
|
||||
constructor(manager, file, options = {}) {
|
||||
|
||||
super();
|
||||
|
||||
options = Util.mergeDefault({
|
||||
totalShards: 'auto',
|
||||
mode: 'process',
|
||||
respawn: true,
|
||||
shardArgs: [],
|
||||
execArgv: [],
|
||||
token: options.bot.token
|
||||
}, options.shard);
|
||||
|
||||
this.manager = manager;
|
||||
|
||||
this.file = file;
|
||||
if(!file) throw new Error("[shardmanager] File must be specified.");
|
||||
if(!path.isAbsolute(file)) this.file = path.resolve(process.cwd(), file);
|
||||
|
||||
const stats = fs.statSync(this.file);
|
||||
if(!stats.isFile()) throw new Error("[shardmanager] File path does not point to a valid file.");
|
||||
|
||||
this.shardList = options.shardList || 'auto';
|
||||
if(this.shardList !== 'auto') {
|
||||
if(!Array.isArray(this.shardList)) {
|
||||
throw new TypeError("[shardmanager] ShardList must be an array.");
|
||||
}
|
||||
this.shardList = [...new Set(this.shardList)];
|
||||
if(this.shardList.length < 1) throw new RangeError("[shardmanager] ShardList must have one ID.");
|
||||
if(this.shardList.some(shardID => typeof shardID !== 'number'
|
||||
|| isNaN(shardID)
|
||||
|| !Number.isInteger(shardID)
|
||||
|| shardID < 0)
|
||||
) {
|
||||
throw new TypeError("[shardmanager] ShardList must be an array of positive integers.");
|
||||
}
|
||||
}
|
||||
|
||||
this.totalShards = options.totalShards || 'auto';
|
||||
if(this.totalShards !== 'auto') {
|
||||
if(typeof this.totalShards !== 'number' || isNaN(this.totalShards)) {
|
||||
throw new TypeError("[shardmanager] TotalShards must be an integer.");
|
||||
}
|
||||
if(this.totalShards < 1) throw new RangeError("[shardmanager] TotalShards must be at least one.");
|
||||
if(!Number.isInteger(this.totalShards)) {
|
||||
throw new RangeError("[shardmanager] TotalShards must be an integer.");
|
||||
}
|
||||
}
|
||||
|
||||
this.mode = options.mode;
|
||||
if(this.mode !== 'process' && this.mode !== 'worker') {
|
||||
throw new RangeError("[shardmanager] Mode must be either 'worker' or 'process'.");
|
||||
}
|
||||
|
||||
this.respawn = options.respawn;
|
||||
this.shardArgs = options.shardArgs;
|
||||
this.execArgv = options.execArgv;
|
||||
this.token = options.token;
|
||||
this.shards = new Collection();
|
||||
|
||||
process.env.SHARDING_MANAGER = true;
|
||||
process.env.SHARDING_MANAGER_MODE = this.mode;
|
||||
process.env.DISCORD_TOKEN = this.token;
|
||||
|
||||
}
|
||||
|
||||
createShard(id = this.shards.size) {
|
||||
const shard = new Shard(this, id);
|
||||
this.shards.set(id, shard);
|
||||
|
||||
this.emit('shardCreate', shard);
|
||||
return shard;
|
||||
}
|
||||
|
||||
async spawn(amount = this.totalShards, delay = 5500, waitForReady = true) {
|
||||
|
||||
if(amount === 'auto') {
|
||||
amount = await Util.fetchRecommendedShards(this.token);
|
||||
} else {
|
||||
if(typeof amount !== 'number' || isNaN(amount)) {
|
||||
throw new TypeError("[shardmanager] Amount of shards must be a number.");
|
||||
}
|
||||
if(amount < 1) throw new RangeError("[shardmanager] Amount of shards must be at least one.");
|
||||
if(!Number.isInteger(amount)) {
|
||||
throw new TypeError("[shardmanager] Amount of shards must be an integer.");
|
||||
}
|
||||
}
|
||||
|
||||
if(this.shards.size >= amount) throw new Error("[shardmanager] Already spawned all necessary shards.");
|
||||
if(this.shardList === 'auto' || this.totalShards === 'auto' || this.totalShards !== amount) {
|
||||
this.shardList = [...Array(amount).keys()];
|
||||
}
|
||||
if(this.totalShards === 'auto' || this.totalShards !== amount) {
|
||||
this.totalShards = amount;
|
||||
}
|
||||
if(this.shardList.some(id => id >= amount)) {
|
||||
throw new RangeError("[shardmanager] Amount of shards cannot be larger than the highest shard ID.");
|
||||
}
|
||||
|
||||
for(const shardID of this.shardList) {
|
||||
const promises = [];
|
||||
const shard = this.createShard(shardID);
|
||||
promises.push(shard.spawn(waitForReady));
|
||||
if(delay > 0 && this.shards.size !== this.shardList.length - 1) promises.push(Util.delayFor(delay));
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
return this.shards;
|
||||
|
||||
}
|
||||
|
||||
broadcast(message) {
|
||||
const promises = [];
|
||||
for(const shard of this.shards.values()) promises.push(shard.send(message));
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
broadcastEval(script) {
|
||||
const promises = [];
|
||||
for(const shard of this.shards.values()) promises.push(shard.eval(script));
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
fetchClientValues(prop) {
|
||||
if(this.shards.size === 0) return Promise.reject(new Error("[shardmanager] No shards available."));
|
||||
if(this.shards.size !== this.totalShards) return Promise.reject(new Error("[shardmanager] Sharding in progress."));
|
||||
const promises = [];
|
||||
for(const shard of this.shards.values()) promises.push(shard.fetchClientValue(prop));
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
async respawnAll(shardDelay = 5000, respawnDelay = 500, waitForReady = true) {
|
||||
let s = 0;
|
||||
for(const shard of this.shards.values()) {
|
||||
const promises = [shard.respawn(respawnDelay, waitForReady)];
|
||||
if(++s < this.shards.size && shardDelay > 0) promises.push(Util.delayFor(shardDelay));
|
||||
await Promise.all(promises);
|
||||
}
|
||||
return this.shards;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = ShardManager;
|
0
middleware/WebhookManager.js
Normal file
0
middleware/WebhookManager.js
Normal file
41
middleware/client/DiscordClient.js
Normal file
41
middleware/client/DiscordClient.js
Normal file
@ -0,0 +1,41 @@
|
||||
const { Client } = require('discord.js');
|
||||
|
||||
const EventHooker = require('./EventHooker.js');
|
||||
const Dispatcher = require('./Dispatcher.js')
|
||||
const Resolver = require('./Resolver.js');
|
||||
|
||||
const { Guild, User, Message } = require('../../structure/extensions/');
|
||||
|
||||
class DiscordClient extends Client {
|
||||
|
||||
constructor(manager, options) {
|
||||
|
||||
this.manager = manager;
|
||||
this.registry = this.manager.registry;
|
||||
|
||||
this.eventHooker = new EventHooker(this);
|
||||
this.dispatcher = new Dispatcher(this);
|
||||
this.resolver = new Resolver(this);
|
||||
|
||||
|
||||
this._options = options;
|
||||
this._built = false;
|
||||
|
||||
}
|
||||
|
||||
async build() {
|
||||
|
||||
|
||||
|
||||
this._built = true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
module.exports = DiscordClient;
|
||||
|
||||
const client = new DiscordClient();
|
||||
client.build();
|
25
middleware/client/Dispatcher.js
Normal file
25
middleware/client/Dispatcher.js
Normal file
@ -0,0 +1,25 @@
|
||||
class Dispatcher {
|
||||
|
||||
constructor(client) {
|
||||
|
||||
this.client = client;
|
||||
|
||||
}
|
||||
|
||||
async dispatch() {
|
||||
|
||||
const observers = this.client.registry.components
|
||||
.filter(c=>c.type === 'observer' && !c.disabled)
|
||||
.sort((a, b) => b.priority - a.priority);
|
||||
|
||||
for(const observer of observers.values()) {
|
||||
for(let [hook, func] of observer.hooks) {
|
||||
this.client.hooker.hook(hook, func);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Dispatcher;
|
45
middleware/client/EventHooker.js
Normal file
45
middleware/client/EventHooker.js
Normal file
@ -0,0 +1,45 @@
|
||||
const { EventEmitter } = require('events');
|
||||
|
||||
class EventHooker {
|
||||
|
||||
constructor(target) {
|
||||
if(!(target instanceof EventEmitter)) return new TypeError('Invalid EventEmitter passed to EventHooker.');
|
||||
|
||||
this.target = target;
|
||||
this.events = new Map();
|
||||
|
||||
}
|
||||
|
||||
hook(eventName, func) {
|
||||
if(this.events.has(eventName)) {
|
||||
const funcs = this.events.get(eventName);
|
||||
this.events.set(eventName, [ ...funcs, func ]);
|
||||
} else {
|
||||
this.events.set(eventName, [ func ]);
|
||||
this._handleEvent(eventName);
|
||||
}
|
||||
}
|
||||
|
||||
unhook(eventName, func) {
|
||||
if(this.events.has(eventName)) {
|
||||
let funcs = this.events.get(eventName);
|
||||
const index = funcs.indexOf(func);
|
||||
if(index > -1) {
|
||||
funcs.splice(index, 1);
|
||||
this.events.set(eventName, funcs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async _handleEvent(eventName) {
|
||||
this.target.on(eventName, (...args) => {
|
||||
this.events.get(eventName).forEach(async (f) => {
|
||||
const result = f(...args);
|
||||
if(f instanceof Promise) await result;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = EventHooker;
|
134
middleware/client/Resolver.js
Normal file
134
middleware/client/Resolver.js
Normal file
@ -0,0 +1,134 @@
|
||||
class Resolver {
|
||||
|
||||
constructor(client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
components(str = '', type, exact = true) { //used for CommandHandler
|
||||
|
||||
const string = str.toLowerCase();
|
||||
|
||||
const components = this.client.registry.components
|
||||
.filter(c => c.type === type)
|
||||
.filter(exact ? filterExact(string) : filterInexact(string))
|
||||
.array();
|
||||
|
||||
return components || [];
|
||||
|
||||
}
|
||||
|
||||
async resolveMemberAndUser(string, guild) {
|
||||
|
||||
const str = string.toLowerCase();
|
||||
const index = guild ? guild.members : this.client.users;
|
||||
|
||||
let member = null;
|
||||
if(/<@!?(\d{17,21})>/iy.test(str)) { //mentions
|
||||
const matches = /<@!?(\d{17,21})>/iy.exec(str);
|
||||
member = index.get(matches[1]);
|
||||
if(!member) {
|
||||
try {
|
||||
member = await index.fetch(matches[1]);
|
||||
} catch(e) {
|
||||
try {
|
||||
member = await this.client.users.fetch(matches[1]);
|
||||
} catch(e) {} //eslint-disable-line no-empty
|
||||
} //eslint-disable-line no-empty
|
||||
}
|
||||
} else if(/\d{17,21}/iy.test(str)) { //id
|
||||
const matches = /(\d{17,21})/iy.exec(str);
|
||||
member = index.get(matches[1]);
|
||||
if(!member) {
|
||||
try {
|
||||
member = await index.fetch(matches[1]);
|
||||
} catch(e) {
|
||||
try {
|
||||
member = await this.client.users.fetch(matches[1]);
|
||||
} catch(e) {} //eslint-disable-line no-empty
|
||||
} //eslint-disable-line no-empty
|
||||
}
|
||||
} else if(/(.{2,32})#(\d{4})/iy.test(str)) { //username#discrim
|
||||
const matches = /(.{2,32})#(\d{4})/iy.exec(str);
|
||||
member = guild
|
||||
? guild.members.filter(m=>m.user.username === matches[1] && m.user.discriminator === matches[2]).first()
|
||||
: this.client.users.filter(u=>u.username === matches[1] && u.discriminator === matches[2]).first();
|
||||
}
|
||||
return member || null;
|
||||
|
||||
}
|
||||
|
||||
async resolveUsers(resolveables = []) {
|
||||
|
||||
if(typeof resolveables === 'string') resolveables = [ resolveables ];
|
||||
if(resolveables.length === 0) return false;
|
||||
let users = this.client.users;
|
||||
let resolved = [];
|
||||
|
||||
for(let resolveable of resolveables) {
|
||||
|
||||
if(/<@!?([0-9]{17,21})>/.test(resolveable)) {
|
||||
|
||||
let id = resolveable.match(/<@!?([0-9]{17,21})>/)[1];
|
||||
let user = await users.fetch(id).catch(err => { if(err.code === 10013) return false; else { console.warn(err); return false; } });
|
||||
if(user) resolved.push(user);
|
||||
|
||||
} else if(/(id\:)?([0-9]{17,21})/.test(resolveable)) {
|
||||
|
||||
let id = resolveable.match(/(id\:)?([0-9]{17,21})/)[2];
|
||||
let user = await users.fetch(id).catch(err => { if(err.code === 10013) return false; else { console.warn(err); return false; } });
|
||||
if(user) resolved.push(user);
|
||||
|
||||
} else if(/^\@?([\S\s]{1,32})\#([0-9]{4})/.test(resolveable)) {
|
||||
|
||||
let m = resolveable.match(/^\@?([\S\s]{1,32})\#([0-9]{4})/);
|
||||
let username = m[1].toLowerCase();
|
||||
let discrim = m[2].toLowerCase();
|
||||
let user = users.cache.filter(u => {
|
||||
return u.username.toLowerCase() === username && u.discriminator === discrim
|
||||
}).first(1);
|
||||
if(user) resolved.push(user);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return resolved;
|
||||
|
||||
}
|
||||
|
||||
async resolveMember(resolveables = [], strict = false, guild) {
|
||||
|
||||
if(typeof resolveables === 'string') resolveables = [ resolveables ];
|
||||
if(resolveables.length === 0) return false;
|
||||
let members = guild.members;
|
||||
let resolved = [];
|
||||
|
||||
for(let resolveable of resolveables) {
|
||||
|
||||
if(/<@!?([0-9]{17,21})>/.test(resolveable)) {
|
||||
|
||||
let id = resolveable.match(/<@!?([0-9]{17,21})>/)[1];
|
||||
let member = await members.fetch(id).catch(err => { if(err.code === 10007) return false; else { console.warn(err); return false; } });
|
||||
if(member) resolved.push(member);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const filterExact = (search) => {
|
||||
return comp => comp.id.toLowerCase() === search ||
|
||||
comp.resolveable.toLowerCase() === search ||
|
||||
(comp.aliases && (comp.aliases.some(ali => `${comp.type}:${ali}`.toLowerCase() === search) ||
|
||||
comp.aliases.some(ali => ali.toLowerCase() === search)));
|
||||
};
|
||||
|
||||
const filterInexact = (search) => {
|
||||
return comp => comp.id.toLowerCase().includes(search) ||
|
||||
comp.resolveable.toLowerCase().includes(search) ||
|
||||
(comp.aliases && (comp.aliases.some(ali => `${comp.type}:${ali}`.toLowerCase().includes(search)) ||
|
||||
comp.aliases.some(ali => ali.toLowerCase().includes(search))));
|
||||
};
|
@ -4,7 +4,8 @@
|
||||
"description": "New iteration of GalacticBot",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"start": "node index.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -17,8 +18,11 @@
|
||||
},
|
||||
"homepage": "https://github.com/Navy-gif/New-GBot#readme",
|
||||
"dependencies": {
|
||||
"common-tags": "^1.8.0",
|
||||
"discord.js": "discordjs/discord.js",
|
||||
"escape-string-regexp": "^3.0.0",
|
||||
"eslint": "^6.8.0",
|
||||
"mongodb": "^3.5.5"
|
||||
"mongodb": "^3.5.5",
|
||||
"mysql": "^2.18.1"
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,18 @@
|
||||
class Provider {
|
||||
|
||||
constructor(manager, config) {
|
||||
|
||||
if(!config) throw new Error('No config file provided!');
|
||||
if(config && (!config.database || !config.url)) throw new Error('Invalid config file provided!');
|
||||
|
||||
this.manager = manager;
|
||||
this.config = config;
|
||||
this.db;
|
||||
this.loaded = false;
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Provider;
|
@ -0,0 +1,73 @@
|
||||
const Provider = require('./Provider.js');
|
||||
const MySQL = require('mysql');
|
||||
|
||||
class MariaDBProvider extends Provider {
|
||||
|
||||
constructor(manager, config) {
|
||||
|
||||
super(manager, config);
|
||||
|
||||
}
|
||||
|
||||
async init() {
|
||||
|
||||
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} 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;
|
@ -1,19 +1,13 @@
|
||||
const Provider = require('./Provider.js');
|
||||
const { MongoClient } = require('mongodb');
|
||||
|
||||
class MongodbProvider extends Provider {
|
||||
class MongoDBProvider extends Provider {
|
||||
|
||||
constructor(manager, config) {
|
||||
|
||||
super(manager, config);
|
||||
|
||||
if(!config) throw new Error('No config file provided!');
|
||||
if(config && (!config.database || !config.url)) throw new Error('Invalid config file provided!');
|
||||
|
||||
this.config = config;
|
||||
this.client;
|
||||
this.db;
|
||||
this.manager = manager;
|
||||
|
||||
}
|
||||
|
||||
@ -26,6 +20,7 @@ class MongodbProvider extends Provider {
|
||||
this.client = await MongoClient.connect(this.config.url+this.config.database, { useUnifiedTopology: true });
|
||||
this.manager.db = await this.client.db(this.config.database);
|
||||
this.db = this.manager.db;
|
||||
this.loaded = true;
|
||||
// this.manager.logger.print('Database connected.');
|
||||
|
||||
} catch(err) {
|
||||
@ -49,6 +44,8 @@ class MongodbProvider extends Provider {
|
||||
|
||||
find(db, query) {
|
||||
|
||||
if(!this.loaded) throw new Error('MongoDB not connected');
|
||||
|
||||
//if(this.manager.debug) this.manager.logger.debug(`Incoming find query for ${db} with parameters ${JSON.stringify(query)}`);
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
@ -74,6 +71,8 @@ class MongodbProvider extends Provider {
|
||||
|
||||
findOne(db, query) {
|
||||
|
||||
if(!this.loaded) throw new Error('MongoDB not connected');
|
||||
|
||||
//if(this.manager.debug) this.manager.logger.debug(`Incoming findOne query for ${db} with parameters ${JSON.stringify(query)}`);
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
@ -100,7 +99,9 @@ class MongodbProvider extends Provider {
|
||||
*/
|
||||
updateOne(db, query, data, upsert = false) {
|
||||
|
||||
if(this.manager.debug) this.manager.logger.debug(`Incoming updateOne query for ${db} with parameters ${JSON.stringify(filter)}`);
|
||||
if(!this.loaded) throw new Error('MongoDB not connected');
|
||||
|
||||
//if(this.manager.debug) this.manager.logger.debug(`Incoming updateOne query for ${db} with parameters ${JSON.stringify(filter)}`);
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
this.db.collection(db).updateOne(query, { $set: data }, { upsert: upsert }, async (error, result) => {
|
||||
@ -129,6 +130,8 @@ class MongodbProvider extends Provider {
|
||||
*/
|
||||
push(db, query, data, upsert = false) {
|
||||
|
||||
if(!this.loaded) throw new Error('MongoDB not connected');
|
||||
|
||||
//if(this.manager.debug) this.manager.logger.debug(`Incoming push query for ${db}, with upsert ${upsert} and with parameters ${JSON.stringify(filter)} and data ${JSON.stringify(data)}`);
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
@ -154,6 +157,8 @@ class MongodbProvider extends Provider {
|
||||
*/
|
||||
random(db, query = {}, amount = 1) {
|
||||
|
||||
if(!this.loaded) throw new Error('MongoDB not connected');
|
||||
|
||||
//if(this.manager.debug) this.manager.logger.debug(`Incoming random query for ${db} with parameters ${JSON.stringify(filter)} and amount ${amount}`);
|
||||
if(amount > 100) amount = 100;
|
||||
|
||||
@ -172,4 +177,4 @@ class MongodbProvider extends Provider {
|
||||
|
||||
}
|
||||
|
||||
module.exports = MongodbProvider;
|
||||
module.exports = MongoDBProvider;
|
108
structure/components/observers/CommandHandler.js
Normal file
108
structure/components/observers/CommandHandler.js
Normal file
@ -0,0 +1,108 @@
|
||||
const { stripIndents } = require('common-tags');
|
||||
const { escapeRegex } = require('escape-string-regexp');
|
||||
|
||||
const { Observer } = require('../../interfaces');
|
||||
|
||||
class CommandHandler extends Observer {
|
||||
|
||||
constructor(manager) {
|
||||
|
||||
super(manager, {
|
||||
name: 'commandHandler',
|
||||
priority: 5,
|
||||
guarded: true
|
||||
});
|
||||
|
||||
this.manager = manager;
|
||||
|
||||
this.hooks = [
|
||||
['message', this.handleMessage.bind(this)]
|
||||
];
|
||||
|
||||
this.commandPatterns = new Map();
|
||||
|
||||
|
||||
}
|
||||
|
||||
async handleMessage(message) {
|
||||
|
||||
const client = message.client;
|
||||
|
||||
if(!this.manager._built
|
||||
|| !client._built
|
||||
|| message.webhookID
|
||||
|| message.author.bot
|
||||
|| (message.guild && !message.guild.available)) return undefined;
|
||||
|
||||
if(message.guild && !message.member) {
|
||||
await message.guild.members.fetch(message.author.id);
|
||||
}
|
||||
|
||||
const content = message.cleanContent;
|
||||
const args = content.split(' ');
|
||||
const command = await this._getCommand(message, args);
|
||||
if(!command) return undefined;
|
||||
|
||||
return await this.handleCommand(message, command, args);
|
||||
|
||||
}
|
||||
|
||||
async handleCommand(message, command, args) {
|
||||
|
||||
}
|
||||
|
||||
async _getCommand(message, args = []) {
|
||||
|
||||
const pattern = await this._getCommandPattern(message.guild);
|
||||
|
||||
let command = await this._matchCommand(message, args, pattern, 2);
|
||||
if(!command && !message.guild) command = await this._matchCommand(message, args, /^([^\s]+)/i);
|
||||
|
||||
return command || null;
|
||||
|
||||
}
|
||||
|
||||
async _getCommandPattern(guild) {
|
||||
|
||||
const createCommandPattern = (guild = null) => {
|
||||
|
||||
const prefix = this.client._options.discord.prefix;
|
||||
|
||||
const escapedPrefix = escapeRegex(prefix);
|
||||
const pattern = new RegExp(`^(${escapedPrefix}\\s*|<@!?${this.client.user.id}>\\s+(?:${escapedPrefix})?)([^\\s]+)`, 'i');
|
||||
|
||||
const obj = { pattern, prefix };
|
||||
if(guild) {
|
||||
this.client.logger.debug(`Created command pattern ${guild.name}: ${pattern}`);
|
||||
this.commandPatterns.set(guild.id, obj);
|
||||
}
|
||||
|
||||
return obj;
|
||||
};
|
||||
|
||||
if(!guild) return createCommandPattern().pattern;
|
||||
let commandPattern = this.commandPatterns.get(guild.id);
|
||||
|
||||
return commandPattern.pattern;
|
||||
|
||||
}
|
||||
|
||||
async _matchCommand(message, args, pattern, index = 1) {
|
||||
|
||||
const matches = pattern.exec(message.cleanContent);
|
||||
if(!matches) return null;
|
||||
|
||||
const command = message.client.resolver.components(matches[index], 'command', true)[0];
|
||||
if(!command) return null;
|
||||
|
||||
const indice = message.content.startsWith('<@') ? 2 : 1;
|
||||
args.splice(0, indice);
|
||||
|
||||
return command;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
module.exports = CommandHandler;
|
22
structure/extensions/Guild.js
Normal file
22
structure/extensions/Guild.js
Normal file
@ -0,0 +1,22 @@
|
||||
const { Structures } = require('discord.js');
|
||||
|
||||
const Guild = Structures.extend('Guild', (Guild) => {
|
||||
|
||||
class ExtendedGuild extends Guild {
|
||||
|
||||
constructor(...args) {
|
||||
|
||||
super(...args);
|
||||
|
||||
this.storageManager = this.manager.storageManager;
|
||||
this._settings = null; //internal cache of current guild's settings; should ALWAYS stay the same as database.
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return ExtendedGuild;
|
||||
|
||||
});
|
||||
|
||||
module.exports = Guild;
|
21
structure/extensions/Message.js
Normal file
21
structure/extensions/Message.js
Normal file
@ -0,0 +1,21 @@
|
||||
const { Structures } = require('discord.js');
|
||||
|
||||
const Message = Structures.extend('Message', (Message) => {
|
||||
|
||||
class ExtendedMessage extends Message {
|
||||
|
||||
constructor(...args) {
|
||||
|
||||
super(...args);
|
||||
|
||||
this.command = null; //Will set to command if the message induces a command.
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return ExtendedMessage;
|
||||
|
||||
});
|
||||
|
||||
module.exports = Message;
|
22
structure/extensions/User.js
Normal file
22
structure/extensions/User.js
Normal file
@ -0,0 +1,22 @@
|
||||
const { Structures } = require('discord.js');
|
||||
|
||||
const User = Structures.extend('User', (User) => {
|
||||
|
||||
class ExtendedUser extends User {
|
||||
|
||||
constructor(...args) {
|
||||
|
||||
super(...args);
|
||||
|
||||
this.storageManager = this.manager.storageManager;
|
||||
this._settings = null; //internal cache of current users' settings; should ALWAYS stay the same as database.
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return ExtendedUser;
|
||||
|
||||
});
|
||||
|
||||
module.exports = User;
|
5
structure/extensions/index.js
Normal file
5
structure/extensions/index.js
Normal file
@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
Message: require('./Message.js'),
|
||||
Guild: require('./Guild.js'),
|
||||
User: require('./User.js')
|
||||
};
|
@ -1,4 +1,46 @@
|
||||
class Command {
|
||||
const Component = require('./Component.js');
|
||||
|
||||
class Command extends Component {
|
||||
|
||||
constructor(client, opts = {}) {
|
||||
if(!opts) return null;
|
||||
|
||||
super(client, {
|
||||
id: opts.name,
|
||||
type: 'command',
|
||||
disabled: opts.disabled || false,
|
||||
guarded: opts.guarded || false
|
||||
});
|
||||
|
||||
Object.defineProperty(this, 'client', { value: client });
|
||||
|
||||
this.name = opts.name;
|
||||
this.module = opts.module;
|
||||
this.aliases = opts.aliases || [];
|
||||
|
||||
this.description = opts.description || "A basic command.";
|
||||
this.examples = opts.examples || [];
|
||||
this.usage = opts.usage || null;
|
||||
|
||||
this.restricted = Boolean(opts.restricted);
|
||||
this.archivable = opts.archivable === undefined ? false : Boolean(opts.archivable);
|
||||
this.guildOnly = Boolean(opts.guildOnly);
|
||||
|
||||
this.clientPermissions = opts.clientPermissions || [];
|
||||
this.memberPermissions = opts.memberPermissions || [];
|
||||
|
||||
this.throttling = opts.throttling || {
|
||||
usages: 5,
|
||||
duration: 10
|
||||
};
|
||||
|
||||
this._throttles = new Map();
|
||||
|
||||
}
|
||||
|
||||
get moduleResolveable() {
|
||||
return `${this.module.id}:${this.id}`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
79
structure/interfaces/Component.js
Normal file
79
structure/interfaces/Component.js
Normal file
@ -0,0 +1,79 @@
|
||||
class Component {
|
||||
|
||||
constructor(manager, opts = {}) {
|
||||
if(!opts) return null;
|
||||
|
||||
this.manager = manager;
|
||||
|
||||
this.id = opts.id;
|
||||
this.type = opts.type;
|
||||
|
||||
this.directory = null;
|
||||
|
||||
this.guarded = Boolean(opts.guarded);
|
||||
this.disabled = Boolean(opts.disabled);
|
||||
|
||||
this.registry = this.manager.registry;
|
||||
|
||||
}
|
||||
|
||||
enable() {
|
||||
if(this.guarded) return { error: true, code: 'GUARDED' };
|
||||
this.disabled = false;
|
||||
this.registry.emit('componentUpdate', { component: this, type: 'ENABLE' });
|
||||
return { error: false };
|
||||
}
|
||||
|
||||
disable() {
|
||||
if(this.guarded) return { error: true, code: 'GUARDED' };
|
||||
this.disabled = true;
|
||||
this.registry.emit('componentUpdate', { component: this, type: 'DISABLE' });
|
||||
return { error: false };
|
||||
}
|
||||
|
||||
unload() {
|
||||
if(this.guarded) return { error: true, code: 'GUARDED' };
|
||||
if(!this.directory) return { error: true, code: 'MISSING_DIRECTORY' };
|
||||
|
||||
this.registry.unloadComponent(this);
|
||||
delete require.cache[this.filePath];
|
||||
|
||||
this.registry.emit('componentUpdate', { component: this, type: 'UNLOAD' });
|
||||
return { error: false };
|
||||
}
|
||||
|
||||
reload(bypass = false) {
|
||||
if(this.type === 'module') return { error: false };
|
||||
if(this.guarded && !bypass) return { error: true, code: 'GUARDED' };
|
||||
if(!this.directory || !require.cache[this.directory]) return { error: true, code: 'MISSING_DIRECTORY' };
|
||||
|
||||
let cached, newModule;
|
||||
|
||||
try {
|
||||
cached = require.cache[this.directory];
|
||||
delete require.cache[this.directory];
|
||||
newModule = require(this.directory);
|
||||
|
||||
if(typeof newModule === 'function') {
|
||||
newModule = new newModule(this.manager);
|
||||
}
|
||||
|
||||
this.registry.unloadComponent(this);
|
||||
this.registry.emit('componentUpdate', { component: this, type: 'UNLOAD' });
|
||||
this.registry.loadComponent(newModule, this.directory);
|
||||
} catch(error) {
|
||||
if(cached) require.cache[this.directory] = cached;
|
||||
return { error: true, code: 'MISSING_MODULE' };
|
||||
}
|
||||
|
||||
return { error: false };
|
||||
|
||||
}
|
||||
|
||||
get resolveable() {
|
||||
return `${this.type}:${this.id}`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Component;
|
@ -1,4 +1,29 @@
|
||||
class Inhibitor {
|
||||
const Component = require('./Component.js');
|
||||
|
||||
class Inhibitor extends Component {
|
||||
|
||||
constructor(client, opts = {}) {
|
||||
|
||||
super(client, {
|
||||
id: opts.name,
|
||||
type: 'inhibitor',
|
||||
guarded: opts.guarded,
|
||||
disabled: opts.disabled
|
||||
});
|
||||
|
||||
this.name = opts.name;
|
||||
this.guild = Boolean(opts.guild);
|
||||
this.priority = opts.priority || 1;
|
||||
|
||||
}
|
||||
|
||||
_succeed() {
|
||||
return { error: false, inhibitor: this };
|
||||
}
|
||||
|
||||
_fail(message) {
|
||||
return { error: true, inhibitor: this, message };
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
25
structure/interfaces/Module.js
Normal file
25
structure/interfaces/Module.js
Normal file
@ -0,0 +1,25 @@
|
||||
const Component = require('./Component.js');
|
||||
const Collection = require('../../../util/interfaces/Collection.js');
|
||||
|
||||
class Module extends Component {
|
||||
|
||||
constructor(client, opts = {}) {
|
||||
if(!opts) return null;
|
||||
|
||||
super(client, {
|
||||
id: opts.name,
|
||||
type: 'module'
|
||||
});
|
||||
|
||||
Object.defineProperty(this, 'client', {
|
||||
value: client
|
||||
});
|
||||
|
||||
this.name = opts.name;
|
||||
this.components = new Collection();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Module;
|
38
structure/interfaces/Observer.js
Normal file
38
structure/interfaces/Observer.js
Normal file
@ -0,0 +1,38 @@
|
||||
const Component = require('./Component.js');
|
||||
|
||||
class Observer extends Component {
|
||||
|
||||
constructor(client, opts = {}) {
|
||||
|
||||
super(client, {
|
||||
id: opts.name,
|
||||
type: 'observer',
|
||||
guarded: opts.guarded,
|
||||
disabled: opts.disabled
|
||||
});
|
||||
|
||||
this.name = opts.name;
|
||||
this.priority = opts.priority || 1;
|
||||
this.hooks = opts.hooks || [];
|
||||
|
||||
Object.defineProperty(this, 'client', {
|
||||
value: client
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
execute() {
|
||||
return this._continue();
|
||||
}
|
||||
|
||||
_continue() {
|
||||
return { error: false, observer: this };
|
||||
}
|
||||
|
||||
_stop() {
|
||||
return { error: true, observer: this };
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Observer;
|
@ -1,6 +1,44 @@
|
||||
class Setting {
|
||||
const Component = require('./Component.js');
|
||||
|
||||
class Setting extends Component {
|
||||
|
||||
constructor(client, opts = {}) {
|
||||
|
||||
if(!opts) return null;
|
||||
|
||||
super(client, {
|
||||
id: opts.name,
|
||||
type: 'setting',
|
||||
guarded: opts.guarded,
|
||||
disabled: opts.disabled
|
||||
});
|
||||
|
||||
this.name = opts.name;
|
||||
this.module = opts.module;
|
||||
this.restricted = Boolean(opts.restricted);
|
||||
|
||||
this.description = opts.description || "A basic setting.";
|
||||
this.archiveable = Boolean(opts.archiveable);
|
||||
|
||||
this.index = opts.index || opts.name;
|
||||
this.aliases = opts.aliases || [];
|
||||
this.resolve = (opts.resolve && Constants.Resolves.includes(opts.resolve)) ? opts.resolve : 'GUILD';
|
||||
this.default = opts.default;
|
||||
|
||||
this.memberPermissions = opts.memberPermissions || [];
|
||||
this.clientPermissions = opts.clientPermissions || [];
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
module.exports = Setting;
|
||||
module.exports = Setting;
|
||||
|
||||
const Constants = {
|
||||
Resolves: [
|
||||
'GUILD',
|
||||
'USER'
|
||||
]
|
||||
};
|
@ -1,5 +1,6 @@
|
||||
module.exports = {
|
||||
Command: require('./Command.js'),
|
||||
Component: require('./Component.js'),
|
||||
Inhibitor: require('./Inhibitor.js'),
|
||||
Setting: require('./Setting.js')
|
||||
}
|
33
util/Util.js
Normal file
33
util/Util.js
Normal file
@ -0,0 +1,33 @@
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
class Util {
|
||||
|
||||
constructor() {
|
||||
throw new Error("Class may not be instantiated.");
|
||||
}
|
||||
|
||||
static readdirRecursive(directory) {
|
||||
|
||||
const result = [];
|
||||
|
||||
(function read(directory) {
|
||||
const files = fs.readdirSync(directory);
|
||||
for(const file of files) {
|
||||
const filePath = path.join(directory, file);
|
||||
|
||||
if(fs.statSync(filePath).isDirectory()) {
|
||||
read(filePath);
|
||||
} else {
|
||||
result.push(filePath);
|
||||
}
|
||||
}
|
||||
}(directory));
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Util;
|
@ -1,3 +1,5 @@
|
||||
module.exports = {
|
||||
Collection: require('./Collection.js');
|
||||
Collection: require('./Collection.js'),
|
||||
Util: require('./Util.js'),
|
||||
Resolver: require('../middleware/client/Resolver.js')
|
||||
}
|
42
yarn.lock
42
yarn.lock
@ -114,6 +114,11 @@ balanced-match@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
||||
|
||||
bignumber.js@9.0.0:
|
||||
version "9.0.0"
|
||||
resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.0.tgz#805880f84a329b5eac6e7cb6f8274b6d82bdf075"
|
||||
integrity sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==
|
||||
|
||||
bl@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/bl/-/bl-2.2.0.tgz#e1a574cdf528e4053019bb800b041c0ac88da493"
|
||||
@ -205,6 +210,11 @@ combined-stream@^1.0.8:
|
||||
dependencies:
|
||||
delayed-stream "~1.0.0"
|
||||
|
||||
common-tags@^1.8.0:
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937"
|
||||
integrity sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw==
|
||||
|
||||
concat-map@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
@ -283,6 +293,11 @@ escape-string-regexp@^1.0.5:
|
||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
|
||||
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
|
||||
|
||||
escape-string-regexp@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-3.0.0.tgz#1dad9cc28aed682be0de197280f79911a5fccd61"
|
||||
integrity sha512-11dXIUC3umvzEViLP117d0KN6LJzZxh5+9F4E/7WLAAw7GrHk8NpUR+g9iJi/pe9C0py4F8rs0hreyRCwlAuZg==
|
||||
|
||||
eslint-scope@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9"
|
||||
@ -695,6 +710,16 @@ mute-stream@0.0.8:
|
||||
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
|
||||
integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==
|
||||
|
||||
mysql@^2.18.1:
|
||||
version "2.18.1"
|
||||
resolved "https://registry.yarnpkg.com/mysql/-/mysql-2.18.1.tgz#2254143855c5a8c73825e4522baf2ea021766717"
|
||||
integrity sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig==
|
||||
dependencies:
|
||||
bignumber.js "9.0.0"
|
||||
readable-stream "2.3.7"
|
||||
safe-buffer "5.1.2"
|
||||
sqlstring "2.3.1"
|
||||
|
||||
natural-compare@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
|
||||
@ -783,7 +808,7 @@ punycode@^2.1.0:
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
||||
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
|
||||
|
||||
readable-stream@^2.3.5:
|
||||
readable-stream@2.3.7, readable-stream@^2.3.5:
|
||||
version "2.3.7"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
|
||||
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
|
||||
@ -848,16 +873,16 @@ rxjs@^6.5.3:
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
|
||||
|
||||
safe-buffer@^5.1.1, safe-buffer@^5.1.2:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519"
|
||||
integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==
|
||||
|
||||
safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
|
||||
|
||||
"safer-buffer@>= 2.1.2 < 3":
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||
@ -923,6 +948,11 @@ sprintf-js@~1.0.2:
|
||||
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
|
||||
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
|
||||
|
||||
sqlstring@2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/sqlstring/-/sqlstring-2.3.1.tgz#475393ff9e91479aea62dcaf0ca3d14983a7fb40"
|
||||
integrity sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A=
|
||||
|
||||
string-width@^3.0.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961"
|
||||
|
Loading…
Reference in New Issue
Block a user