forked from Galactic/galactic-bot
Merge branch 'master' of https://github.com/Navy-gif/New-GBot
This commit is contained in:
commit
194942f5c4
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
options.json
|
options.json
|
||||||
node_modules
|
node_modules
|
||||||
.vscode/launch.json
|
.vscode/launch.json
|
||||||
|
yarn-error.log
|
22
Intercom.js
22
Intercom.js
@ -1,22 +0,0 @@
|
|||||||
class Intercom {
|
|
||||||
|
|
||||||
constructor(manager, shardManager) {
|
|
||||||
|
|
||||||
this.manager = manager;
|
|
||||||
this.shardManager = shardManager;
|
|
||||||
|
|
||||||
// this.shardManager.on('message', this.receive.bind(this));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
send(shard, message) {
|
|
||||||
shard.send(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
receive(...args) {
|
|
||||||
console.log(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Intercom;
|
|
49
Logger.js
49
Logger.js
@ -1,11 +1,58 @@
|
|||||||
const Winston = require('winston');
|
const { createLogger, format, transports, config } = require('winston');
|
||||||
|
const { combine, label, printf } = format;
|
||||||
|
const moment = require('moment');
|
||||||
|
const chalk = require('chalk');
|
||||||
|
|
||||||
class Logger {
|
class Logger {
|
||||||
|
|
||||||
constructor(manager) {
|
constructor(manager) {
|
||||||
|
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
|
this.logger = createLogger({
|
||||||
|
transports: [
|
||||||
|
new transports.Console(),
|
||||||
|
new transports.File({ filename: `logs/${this.date.replace(/ /g, '-')}.log` }),
|
||||||
|
new transports.File({ filename: `logs/${this.date.replace(/ /g, '-')}-error.log`, level: 'error' })
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
this.client
|
||||||
|
.on('shardCreate', (shard) => this.write(shard, "Shard created.", 'DEBUG'))
|
||||||
|
.on('message', (shard, message) => this._handleMessage(shard, message));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Messages coming from the shards process.send functions.
|
||||||
|
async _handleMessage(shard, message) {
|
||||||
|
if(message._ready
|
||||||
|
|| message._disconnect
|
||||||
|
|| message._reconnecting
|
||||||
|
|| message._sFetchProp
|
||||||
|
|| message._sEval
|
||||||
|
|| message._sRespawnAll) return undefined; //Properties used for discord.js internal sharding, must filter for.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//The MAIN function for writing everything to the logger.
|
||||||
|
async write(shard, string = '', type = 'silly') {
|
||||||
|
if(!config.npm.levels[type]) return undefined;
|
||||||
|
|
||||||
|
const header = `[${this.date}][shard-${this._shardId(shard)}]`;
|
||||||
|
//[04/02/2020 12:52:20][shard-00]
|
||||||
|
|
||||||
|
this.logger.log(type, string)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_shardId(shard) {
|
||||||
|
const id = shard.id;
|
||||||
|
return `${id}`.length === 1 ? `0${id}` : `${id}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
get date() {
|
||||||
|
return moment().format("MM-DD-YYYY hh:mm:ss");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
14
Manager.js
14
Manager.js
@ -2,8 +2,6 @@ const { EventEmitter } = require('events');
|
|||||||
|
|
||||||
const ShardManager = require('./middleware/ShardManager.js');
|
const ShardManager = require('./middleware/ShardManager.js');
|
||||||
const StorageManager = require('./storage/StorageManager.js');
|
const StorageManager = require('./storage/StorageManager.js');
|
||||||
const Registry = require('./Registry.js');
|
|
||||||
const Intercom = require('./Intercom.js');
|
|
||||||
const Logger = require('./Logger.js');
|
const Logger = require('./Logger.js');
|
||||||
|
|
||||||
const { Command, Setting, Inhibitor } = require('./structure/interfaces/');
|
const { Command, Setting, Inhibitor } = require('./structure/interfaces/');
|
||||||
@ -14,13 +12,10 @@ class Manager extends EventEmitter {
|
|||||||
|
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.registry = new Registry(this);
|
this.shardManager = new ShardManager('./structure/client/DiscordClient.js', options);
|
||||||
this.shardManager = new ShardManager('./middleware/client/DiscordClient.js', options);
|
|
||||||
this.storageManager = new StorageManager(this, options.storage)
|
this.storageManager = new StorageManager(this, options.storage)
|
||||||
.initialize();
|
.initialize();
|
||||||
|
|
||||||
this.intercom = new Intercom(this, this.shardManager);
|
|
||||||
|
|
||||||
this.logger = new Logger(this);
|
this.logger = new Logger(this);
|
||||||
|
|
||||||
this._built = false;
|
this._built = false;
|
||||||
@ -28,13 +23,8 @@ class Manager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async build() {
|
async build() {
|
||||||
try {
|
|
||||||
await this.shardManager.spawn();
|
|
||||||
}catch(e) {
|
|
||||||
console.log(e);
|
|
||||||
}
|
|
||||||
await this.registry.loadComponents('components/commands/', Command);
|
|
||||||
|
|
||||||
|
await this.shardManager.spawn();
|
||||||
|
|
||||||
this._built = true;
|
this._built = true;
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ class Shard extends EventEmitter {
|
|||||||
this.once('ready', resolve);
|
this.once('ready', resolve);
|
||||||
this.once('disconnect', () => reject(new Error(`[shard${this.id}] Shard disconnected while readying.`)));
|
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.`)));
|
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.`)), 10000);
|
setTimeout(() => reject(new Error(`[shard${this.id}] Shard timed out while readying.`)), 30000);
|
||||||
});
|
});
|
||||||
|
|
||||||
return this.process || this.worker;
|
return this.process || this.worker;
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
class Transporter {
|
|
||||||
|
|
||||||
constructor(client, opts) {
|
|
||||||
|
|
||||||
this.client = client;
|
|
||||||
|
|
||||||
// process.on('message', this.receive.bind(this));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
async send() {
|
|
||||||
|
|
||||||
process.send();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
async receive(message) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Transporter;
|
|
@ -18,10 +18,12 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/Navy-gif/New-GBot#readme",
|
"homepage": "https://github.com/Navy-gif/New-GBot#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"chalk": "^4.0.0",
|
||||||
"common-tags": "^1.8.0",
|
"common-tags": "^1.8.0",
|
||||||
"discord.js": "discordjs/discord.js",
|
"discord.js": "discordjs/discord.js",
|
||||||
"escape-string-regexp": "^3.0.0",
|
"escape-string-regexp": "^3.0.0",
|
||||||
"eslint": "^6.8.0",
|
"eslint": "^6.8.0",
|
||||||
|
"moment": "^2.24.0",
|
||||||
"mongodb": "^3.5.5",
|
"mongodb": "^3.5.5",
|
||||||
"mysql": "^2.18.1",
|
"mysql": "^2.18.1",
|
||||||
"node-fetch": "^2.6.0",
|
"node-fetch": "^2.6.0",
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
class Provider {
|
class Provider {
|
||||||
|
|
||||||
constructor(manager, config) {
|
constructor(manager, config, name) {
|
||||||
|
|
||||||
if(!config) throw new Error('No config file provided!');
|
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[name];
|
||||||
|
if(config && (!this.config.database || !this.config.host)) throw new Error('Invalid config file provided!' + JSON.stringify(this.config));
|
||||||
|
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
this.config = config;
|
|
||||||
this.db;
|
this.db;
|
||||||
this.loaded = false;
|
this.loaded = false;
|
||||||
|
this.name = name;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,34 @@
|
|||||||
const { Collection } = require('../util/');
|
const { Collection } = require('../util/');
|
||||||
|
const path = require('path');
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
class StorageManager {
|
class StorageManager {
|
||||||
|
|
||||||
constructor(manager) {
|
constructor(manager, options = {}) {
|
||||||
|
|
||||||
this.providers = new Collection();
|
this.providers = new Collection();
|
||||||
|
this.manager = manager;
|
||||||
|
this.options = options;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async initialize() {
|
async initialize() {
|
||||||
|
|
||||||
|
console.log('Initiating storage providers');
|
||||||
|
let _providers = path.join(process.cwd(), 'storage', 'providers');
|
||||||
|
let providers = fs.readdirSync(_providers);
|
||||||
|
|
||||||
|
for(let _provider of providers) {
|
||||||
|
|
||||||
|
let provider = require(path.join(_providers, _provider));
|
||||||
|
provider = new provider(this.manager, this.options);
|
||||||
|
|
||||||
|
await provider.init();
|
||||||
|
|
||||||
|
this.providers.set(provider.name, provider);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
const Provider = require('./Provider.js');
|
const Provider = require('../Provider.js');
|
||||||
const MySQL = require('mysql');
|
const MySQL = require('mysql');
|
||||||
|
|
||||||
class MariaDBProvider extends Provider {
|
class MariaDBProvider extends Provider {
|
||||||
|
|
||||||
constructor(manager, config) {
|
constructor(manager, config) {
|
||||||
|
|
||||||
super(manager, config);
|
super(manager, config, 'mariadb');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,7 +49,7 @@ class MariaDBProvider extends Provider {
|
|||||||
* Query using SQL to MariaDB
|
* Query using SQL to MariaDB
|
||||||
*
|
*
|
||||||
* @param {string} query SQL query string.
|
* @param {string} query SQL query string.
|
||||||
* @param {array} values Array of values to replace ? with in the 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
|
* @returns {object} Returns an object containing the query result
|
||||||
* @memberof MariaDBProvider
|
* @memberof MariaDBProvider
|
||||||
*/
|
*/
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
const Provider = require('./Provider.js');
|
const Provider = require('../Provider.js');
|
||||||
const { MongoClient } = require('mongodb');
|
const { MongoClient } = require('mongodb');
|
||||||
|
|
||||||
class MongoDBProvider extends Provider {
|
class MongoDBProvider extends Provider {
|
||||||
|
|
||||||
constructor(manager, config) {
|
constructor(manager, config) {
|
||||||
|
|
||||||
super(manager, config);
|
super(manager, config, 'mongodb');
|
||||||
|
|
||||||
this.client;
|
this.client;
|
||||||
|
|
||||||
@ -13,7 +13,7 @@ class MongoDBProvider extends Provider {
|
|||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
|
|
||||||
this.manager.logger.log('Initializing mongodb.');
|
//this.manager.logger.log('Initializing mongodb.');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
@ -44,11 +44,11 @@ class MongoDBProvider extends Provider {
|
|||||||
|
|
||||||
find(db, query) {
|
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)}`);
|
//if(this.manager.debug) this.manager.logger.debug(`Incoming find query for ${db} with parameters ${JSON.stringify(query)}`);
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
|
if(!this.loaded) reject(new Error('MongoDB not connected'));
|
||||||
|
|
||||||
this.db.collection(db).find(query, async (error, cursor) => {
|
this.db.collection(db).find(query, async (error, cursor) => {
|
||||||
|
|
||||||
if(error) return reject(error);
|
if(error) return reject(error);
|
||||||
@ -71,11 +71,11 @@ class MongoDBProvider extends Provider {
|
|||||||
|
|
||||||
findOne(db, query) {
|
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)}`);
|
//if(this.manager.debug) this.manager.logger.debug(`Incoming findOne query for ${db} with parameters ${JSON.stringify(query)}`);
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
|
if(!this.loaded) reject(new Error('MongoDB not connected'));
|
||||||
|
|
||||||
this.db.collection(db).findOne(query, async (error, item) => {
|
this.db.collection(db).findOne(query, async (error, item) => {
|
||||||
|
|
||||||
if(error) return reject(error);
|
if(error) return reject(error);
|
||||||
@ -99,11 +99,11 @@ class MongoDBProvider extends Provider {
|
|||||||
*/
|
*/
|
||||||
updateOne(db, query, data, upsert = false) {
|
updateOne(db, query, data, upsert = false) {
|
||||||
|
|
||||||
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)}`);
|
//if(this.manager.debug) this.manager.logger.debug(`Incoming updateOne query for ${db} with parameters ${JSON.stringify(filter)}`);
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
|
if(!this.loaded) reject(new Error('MongoDB not connected'));
|
||||||
|
|
||||||
this.db.collection(db).updateOne(query, { $set: data }, { upsert: upsert }, async (error, result) => {
|
this.db.collection(db).updateOne(query, { $set: data }, { upsert: upsert }, async (error, result) => {
|
||||||
|
|
||||||
if(error) return reject(error);
|
if(error) return reject(error);
|
||||||
@ -130,11 +130,11 @@ class MongoDBProvider extends Provider {
|
|||||||
*/
|
*/
|
||||||
push(db, query, data, upsert = false) {
|
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)}`);
|
//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) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
|
if(!this.loaded) reject(new Error('MongoDB not connected'));
|
||||||
|
|
||||||
this.db.collection(db).updateOne(query, { $push: data }, { upsert: upsert }, async (error, result) => {
|
this.db.collection(db).updateOne(query, { $push: data }, { upsert: upsert }, async (error, result) => {
|
||||||
|
|
||||||
if(error) return reject(error);
|
if(error) return reject(error);
|
||||||
@ -157,13 +157,13 @@ class MongoDBProvider extends Provider {
|
|||||||
*/
|
*/
|
||||||
random(db, query = {}, amount = 1) {
|
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(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;
|
if(amount > 100) amount = 100;
|
||||||
|
|
||||||
return new Promise((resolve, reject)=>{
|
return new Promise((resolve, reject)=>{
|
||||||
|
|
||||||
|
if(!this.loaded) reject(new Error('MongoDB not connected'));
|
||||||
|
|
||||||
this.db.collection(db).aggregate([{ $match: query }, { $sample: {size: amount}}], function(err, item) {
|
this.db.collection(db).aggregate([{ $match: query }, { $sample: {size: amount}}], function(err, item) {
|
||||||
|
|
||||||
if(err) return reject(err);
|
if(err) return reject(err);
|
||||||
|
@ -2,12 +2,14 @@ const { Client } = require('discord.js');
|
|||||||
|
|
||||||
const options = require('../../options.json');
|
const options = require('../../options.json');
|
||||||
|
|
||||||
|
const Registry = require('./Registry.js')
|
||||||
const EventHooker = require('./EventHooker.js');
|
const EventHooker = require('./EventHooker.js');
|
||||||
const Dispatcher = require('./Dispatcher.js')
|
const Dispatcher = require('./Dispatcher.js')
|
||||||
const Resolver = require('./Resolver.js');
|
const Resolver = require('./Resolver.js');
|
||||||
const Transporter = require('./Transporter.js');
|
const Transporter = require('./Transporter.js');
|
||||||
|
|
||||||
const { Guild, User, Message } = require('../../structure/extensions/');
|
const { Guild, User, Message } = require('../../structure/extensions/');
|
||||||
|
const { Command, Observer, Inhibitor, Setting } = require('../../structure/interfaces/');
|
||||||
|
|
||||||
class DiscordClient extends Client {
|
class DiscordClient extends Client {
|
||||||
|
|
||||||
@ -15,6 +17,7 @@ class DiscordClient extends Client {
|
|||||||
|
|
||||||
super(options.bot.clientOptions);
|
super(options.bot.clientOptions);
|
||||||
|
|
||||||
|
this.registry = new Registry(this);
|
||||||
this.eventHooker = new EventHooker(this);
|
this.eventHooker = new EventHooker(this);
|
||||||
this.dispatcher = new Dispatcher(this);
|
this.dispatcher = new Dispatcher(this);
|
||||||
this.resolver = new Resolver(this);
|
this.resolver = new Resolver(this);
|
||||||
@ -23,30 +26,33 @@ class DiscordClient extends Client {
|
|||||||
this._options = options;
|
this._options = options;
|
||||||
this._built = false;
|
this._built = false;
|
||||||
|
|
||||||
process.send({
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
process.on('message', (message) => {
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async build() {
|
async build() {
|
||||||
|
|
||||||
|
if(this._built) return undefined;
|
||||||
|
|
||||||
|
console.log('Building Discord client');
|
||||||
|
|
||||||
await super.login(this._options.bot.token);
|
await super.login(this._options.bot.token);
|
||||||
|
|
||||||
|
await this.registry.loadComponents('components/commands/', Command);
|
||||||
|
await this.registry.loadComponents('components/observers', Observer);
|
||||||
|
|
||||||
this.on('message', (message) => {
|
await this.dispatcher.dispatch();
|
||||||
console.log(message);
|
|
||||||
if(message.content === "kms") {
|
|
||||||
message.reply("ok");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this._built = true;
|
this._built = true;
|
||||||
|
|
||||||
|
this.on('ready', () => {
|
||||||
|
console.log('Client websocket is ready.');
|
||||||
|
});
|
||||||
|
|
||||||
|
this.registry.on('componentUpdate', (comp, type) => {
|
||||||
|
console.log(`[registry][${type}] ${comp.resolveable}`)
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Client built');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -57,3 +63,7 @@ module.exports = DiscordClient;
|
|||||||
|
|
||||||
const client = new DiscordClient(options);
|
const client = new DiscordClient(options);
|
||||||
client.build();
|
client.build();
|
||||||
|
|
||||||
|
process.on("unhandledRejection", (error) => {
|
||||||
|
console.error("Unhandled promise rejection:", error); //eslint-disable-line no-console
|
||||||
|
});
|
@ -14,7 +14,7 @@ class Dispatcher {
|
|||||||
|
|
||||||
for(const observer of observers.values()) {
|
for(const observer of observers.values()) {
|
||||||
for(let [hook, func] of observer.hooks) {
|
for(let [hook, func] of observer.hooks) {
|
||||||
this.client.hooker.hook(hook, func);
|
this.client.eventHooker.hook(hook, func);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
62
structure/client/Logger.js
Normal file
62
structure/client/Logger.js
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
const chalk = require('chalk');
|
||||||
|
|
||||||
|
class Logger {
|
||||||
|
|
||||||
|
constructor(client) {
|
||||||
|
|
||||||
|
this.client = client;
|
||||||
|
|
||||||
|
this.client.hooker.hook('ready', () => {
|
||||||
|
this.transport(`Client connected to ${chalk.bold(this.client.user.tag)} with ${chalk.bold(`${this.client.guilds.size} guild${this.client.guilds.size === 1 ? '' : 's'}`)}.`, { embed: true, type: 'SUCCESS' });
|
||||||
|
});
|
||||||
|
|
||||||
|
this.client.hooker.hook('componentUpdate', ({ component, type }) => {
|
||||||
|
this.info(`Component ${chalk.bold(component.resolveable)} was ${chalk.bold(Constants.ComponentTypes[type])}.`);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.client.hooker.hook('reconnect', () => {
|
||||||
|
this.warn(`Shard is reconnecting.`, { embed: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async transport(message = 'N/A', opts = {}) {
|
||||||
|
process.send({ message, ...opts });
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Quick & Dirty Functions */
|
||||||
|
|
||||||
|
log(message, opts = {}) {
|
||||||
|
this.transport(message, { ...opts, type: 'LOG' });
|
||||||
|
}
|
||||||
|
|
||||||
|
info(message, opts = {}) {
|
||||||
|
this.transport(message, { ...opts, type: 'INFO' });
|
||||||
|
}
|
||||||
|
|
||||||
|
warn(message, opts = {}) {
|
||||||
|
this.transport(message, { ...opts, type: 'WARN' });
|
||||||
|
}
|
||||||
|
|
||||||
|
debug(message, opts = {}) {
|
||||||
|
this.transport(message, { ...opts, type: 'DEBUG' });
|
||||||
|
}
|
||||||
|
|
||||||
|
error(message, opts = {}) {
|
||||||
|
this.transport(message, { ...opts, type: 'ERROR' });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Logger;
|
||||||
|
|
||||||
|
const Constants = {
|
||||||
|
ComponentTypes: {
|
||||||
|
LOAD: 'loaded',
|
||||||
|
UNLOAD: 'unloaded',
|
||||||
|
RELOAD: 'reloaded',
|
||||||
|
ENABLE: 'enabled',
|
||||||
|
DISABLE: 'disabled'
|
||||||
|
}
|
||||||
|
};
|
@ -1,21 +1,24 @@
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
const { EventEmitter } = require('events');
|
const { EventEmitter } = require('events');
|
||||||
const { Collection, Util } = require('./util/');
|
const { Collection, Util } = require('../../util/');
|
||||||
|
|
||||||
|
const { Component, Module } = require('../../structure/interfaces');
|
||||||
|
|
||||||
class Registry extends EventEmitter {
|
class Registry extends EventEmitter {
|
||||||
|
|
||||||
constructor(manager) {
|
constructor(client) {
|
||||||
|
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
this.client = client;
|
||||||
this.components = new Collection();
|
this.components = new Collection();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadComponents(dir) {
|
async loadComponents(dir, classToHandle) {
|
||||||
|
|
||||||
const directory = path.join(process.cwd(), 'structure/', dir); //Finds directory of component folder relative to current working directory.
|
const directory = path.join(process.cwd(), 'structure/client/', 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 files = Util.readdirRecursive(directory); //Loops through all folders in the directory and returns the files.
|
||||||
|
|
||||||
const loaded = [];
|
const loaded = [];
|
||||||
@ -56,7 +59,7 @@ class Registry extends EventEmitter {
|
|||||||
if(directory) component.directory = directory;
|
if(directory) component.directory = directory;
|
||||||
if(component.module && typeof component.module === 'string') { //Sets modules or "groups" for each component, specified by their properties.
|
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}`);
|
let module = this.components.get(`module:${component.module}`);
|
||||||
if(!module) module = await this.loadComponent(new Module(this.manager, { name: component.module }));
|
if(!module) module = await this.loadComponent(new Module(this.client, { name: component.module }));
|
||||||
this.components.set(module.resolveable, module);
|
this.components.set(module.resolveable, module);
|
||||||
|
|
||||||
component.module = module;
|
component.module = module;
|
@ -57,6 +57,14 @@ class Resolver {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve several user resolveables
|
||||||
|
*
|
||||||
|
* @param {array<string>} [resolveables=[]] an array of user resolveables (name, id, tag)
|
||||||
|
* @param {boolean} [strict=false] whether or not to attempt resolving by partial usernames
|
||||||
|
* @returns {array || boolean} Array of resolved users or false if none were resolved
|
||||||
|
* @memberof Resolver
|
||||||
|
*/
|
||||||
async resolveUsers(resolveables = [], strict = false) {
|
async resolveUsers(resolveables = [], strict = false) {
|
||||||
|
|
||||||
if(typeof resolveables === 'string') resolveables = [ resolveables ];
|
if(typeof resolveables === 'string') resolveables = [ resolveables ];
|
||||||
@ -100,11 +108,20 @@ class Resolver {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return resolved;
|
return resolved.length > 0 ? resolved : false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async resolveMember(resolveables = [], strict = false, guild) {
|
/**
|
||||||
|
* Resolve multiple member resolveables
|
||||||
|
*
|
||||||
|
* @param {array<string>} [resolveables=[]] an array of member resolveables (name, nickname, tag, id)
|
||||||
|
* @param {boolean} [strict=false] whether or not to attempt resolving by partial matches
|
||||||
|
* @param {Guild} guild the guild in which to look for members
|
||||||
|
* @returns {array<GuildMember> || boolean} an array of resolved members or false if none were resolved
|
||||||
|
* @memberof Resolver
|
||||||
|
*/
|
||||||
|
async resolveMembers(resolveables = [], guild, strict = false) {
|
||||||
|
|
||||||
if(typeof resolveables === 'string') resolveables = [ resolveables ];
|
if(typeof resolveables === 'string') resolveables = [ resolveables ];
|
||||||
if(resolveables.length === 0) return false;
|
if(resolveables.length === 0) return false;
|
||||||
@ -151,7 +168,109 @@ class Resolver {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return resolved;
|
return resolved.length > 0 ? resolved : false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve multiple channels
|
||||||
|
*
|
||||||
|
* @param {array<string>} [resolveables=[]] an array of channel resolveables (name, id)
|
||||||
|
* @param {guild} guild the guild in which to look for channels
|
||||||
|
* @param {boolean} [strict=false] whether or not partial names are resolved
|
||||||
|
* @returns {array<GuildChannel> || false} an array of guild channels or false if none were resolved
|
||||||
|
* @memberof Resolver
|
||||||
|
*/
|
||||||
|
async resolveChannels(resolveables = [], guild, strict = false) {
|
||||||
|
|
||||||
|
if(typeof resolveables === 'string') resolveables = [ resolveables ];
|
||||||
|
if(resolveables.length === 0) return false;
|
||||||
|
let channels = guild.channels;
|
||||||
|
let resolved = [];
|
||||||
|
|
||||||
|
for(let resolveable of resolveables) {
|
||||||
|
|
||||||
|
let channel = channels.resolve(resolveable);
|
||||||
|
if(channel) {
|
||||||
|
resolved.push(channel);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = /^\#?([a-z0-9\-\_0]*)/i;
|
||||||
|
let id = /^\<\#([0-9]*)\>/i;
|
||||||
|
|
||||||
|
if(name.test(resolveable)) {
|
||||||
|
|
||||||
|
let match = resolveable.match(name);
|
||||||
|
let ch = match[1].toLowerCase();
|
||||||
|
|
||||||
|
let channel = channels.cache.filter(c => {
|
||||||
|
if(!strict) return c.name.toLowerCase().includes(ch)
|
||||||
|
return c.name.toLowerCase() === ch;
|
||||||
|
}).first(1);
|
||||||
|
|
||||||
|
if(channel) resolved.push(channel);
|
||||||
|
|
||||||
|
} else if(id.test(resolveable)) {
|
||||||
|
|
||||||
|
let match = resolveable.match(id);
|
||||||
|
let ch = match[1];
|
||||||
|
|
||||||
|
let channel = channels.resolve(ch);
|
||||||
|
|
||||||
|
if(channel) resolved.push(channel);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolved.length > 0 ? resolved : false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve multiple roles
|
||||||
|
*
|
||||||
|
* @param {array<string>} [resolveables=[]] an array of roles resolveables (name, id)
|
||||||
|
* @param {Guild} guild the guild in which to look for roles
|
||||||
|
* @param {boolean} [strict=false] whether or not partial names are resolved
|
||||||
|
* @returns {array<GuildRole> || false} an array of roles or false if none were resolved
|
||||||
|
* @memberof Resolver
|
||||||
|
*/
|
||||||
|
async resolveRoles(resolveables = [], guild, strict = false) {
|
||||||
|
|
||||||
|
if(typeof resolveables === 'string') resolveables = [ resolveables ];
|
||||||
|
if(resolveables.length === 0) return false;
|
||||||
|
let roles = guild.roles;
|
||||||
|
let resolved = [];
|
||||||
|
|
||||||
|
for(let resolveable of resolveables) {
|
||||||
|
|
||||||
|
let id = /^(<@&)?([0-9]{16,22})>?/i;
|
||||||
|
|
||||||
|
if(id.test(resolveable)) {
|
||||||
|
|
||||||
|
let match = resolveable.match(id);
|
||||||
|
let r_id = match[2];
|
||||||
|
|
||||||
|
let role = await roles.fetch(r_id).catch(console.error);
|
||||||
|
|
||||||
|
if(role) resolved.push(role);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
let role = roles.cache.filter(r => {
|
||||||
|
if(!strict) return r.name.toLowerCase().includes(resolveable.toLowerCase());
|
||||||
|
return r.name.toLowerCase() === resolveable.toLowerCase();
|
||||||
|
}).first(1);
|
||||||
|
|
||||||
|
if(role) resolved.push(role);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolved.length > 0 ? reoslved : false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
45
structure/client/components/commands/utility/Ping.js
Normal file
45
structure/client/components/commands/utility/Ping.js
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
const { Command, Argument } = require('../../../../interfaces/');
|
||||||
|
|
||||||
|
class PingCommand extends Command {
|
||||||
|
|
||||||
|
constructor(client) {
|
||||||
|
|
||||||
|
super(client, {
|
||||||
|
name: 'ping',
|
||||||
|
module: 'utility',
|
||||||
|
description: "Determines the ping of the bot.",
|
||||||
|
arguments: [
|
||||||
|
new Argument(client, {
|
||||||
|
name: 'apple',
|
||||||
|
type: 'BOOLEAN',
|
||||||
|
types: ['VERBAL']
|
||||||
|
}),
|
||||||
|
new Argument(client, {
|
||||||
|
name: 'banana',
|
||||||
|
aliases: ['bans', 'bananas'],
|
||||||
|
type: 'INTEGER',
|
||||||
|
types: ['FLAG', 'VERBAL'],
|
||||||
|
default: 0
|
||||||
|
}),
|
||||||
|
new Argument(client, {
|
||||||
|
name: 'carrot',
|
||||||
|
aliases: ['cars', 'carrots'],
|
||||||
|
type: 'STRING',
|
||||||
|
required: true,
|
||||||
|
types: ['FLAG', 'VERBAL']
|
||||||
|
})
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
this.client = client;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async execute(message) {
|
||||||
|
message.reply("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = PingCommand;
|
360
structure/client/components/observers/CommandHandler.js
Normal file
360
structure/client/components/observers/CommandHandler.js
Normal file
@ -0,0 +1,360 @@
|
|||||||
|
const { stripIndents } = require('common-tags');
|
||||||
|
const escapeRegex = require('escape-string-regexp');
|
||||||
|
|
||||||
|
const { Observer } = require('../../../interfaces/');
|
||||||
|
|
||||||
|
class CommandHandler extends Observer {
|
||||||
|
|
||||||
|
constructor(client) {
|
||||||
|
|
||||||
|
super(client, {
|
||||||
|
name: 'commandHandler',
|
||||||
|
priority: 5,
|
||||||
|
guarded: true
|
||||||
|
});
|
||||||
|
|
||||||
|
this.client = client;
|
||||||
|
|
||||||
|
this.hooks = [
|
||||||
|
['message', this.handleMessage.bind(this)]
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleMessage(message) {
|
||||||
|
|
||||||
|
//const time1 = new Date().getTime();
|
||||||
|
|
||||||
|
if(!this.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.content;
|
||||||
|
const args = content.split(' ');
|
||||||
|
const { command, newArgs } = await this._getCommand(message, args);
|
||||||
|
if(!command) return undefined;
|
||||||
|
|
||||||
|
message.command = command;
|
||||||
|
|
||||||
|
await this.handleCommand(message, newArgs);
|
||||||
|
|
||||||
|
//const time2 = new Date().getTime();
|
||||||
|
//console.log(`${time2-time1}ms`);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async _getCommand(message, [arg1, arg2, ...args]) {
|
||||||
|
|
||||||
|
const prefix = this.client._options.bot.prefix; //Change this for guild prefix settings.
|
||||||
|
let command = null;
|
||||||
|
let remains = [];
|
||||||
|
|
||||||
|
if(arg1 && arg1.startsWith(prefix)) {
|
||||||
|
const commandName = arg1.slice(1);
|
||||||
|
command = await this._matchCommand(message, commandName);
|
||||||
|
remains = [arg2, ...args];
|
||||||
|
} else if(arg1 && arg2 && arg1.startsWith('<@')){
|
||||||
|
const pattern = new RegExp(`^(<@!?${this.client.user.id}>)`, 'i');
|
||||||
|
if(arg2 && pattern.test(arg1)) {
|
||||||
|
command = await this._matchCommand(message, arg2);
|
||||||
|
}
|
||||||
|
remains = args
|
||||||
|
}
|
||||||
|
|
||||||
|
return { command, newArgs: remains };
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async _matchCommand(message, commandName) {
|
||||||
|
|
||||||
|
const command = this.client.resolver.components(commandName, 'command', true)[0];
|
||||||
|
if(!command) return null;
|
||||||
|
|
||||||
|
//Eventually search for custom commands here.
|
||||||
|
|
||||||
|
return command;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Command Handling */
|
||||||
|
|
||||||
|
async handleCommand(message, args) {
|
||||||
|
|
||||||
|
const inhibitor = await this._handleInhibitors(message);
|
||||||
|
if(inhibitor.error) {
|
||||||
|
message.channel.send(`${inhibitor.resolveable} failed to pass.`);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsedArguments = await this._parseArguments(message, args);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async _handleInhibitors(message) {
|
||||||
|
|
||||||
|
const inhibitors = this.client.registry.components.filter(c=>c.type === 'inhibitor' && !c.disabled);
|
||||||
|
if(inhibitors.size === 0) return { error: false };
|
||||||
|
|
||||||
|
const promises = [];
|
||||||
|
for(const inhibitor of inhibitors.values()) {
|
||||||
|
if(inhibitor.guild && !message.guild) continue;
|
||||||
|
promises.push((async () => {
|
||||||
|
let inhibited = inhibitor.execute(message);
|
||||||
|
if(inhibited instanceof Promise) inhibited = await inhibited;
|
||||||
|
return inhibited;
|
||||||
|
})());
|
||||||
|
}
|
||||||
|
|
||||||
|
const reasons = (await Promise.all(promises)).filter(p=>p.error);
|
||||||
|
if(reasons.length === 0) return { error: false };
|
||||||
|
|
||||||
|
reasons.sort((a, b) => b.inhibitor.priority - a.inhibitor.priority);
|
||||||
|
return reasons[0];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async _parseArguments(message, args = []) {
|
||||||
|
|
||||||
|
const command = message.command;
|
||||||
|
let parsedArguments = [];
|
||||||
|
const parsedFlags = {};
|
||||||
|
|
||||||
|
const { shortFlags, longFlags, keys } = await this._createFlags(command.arguments);
|
||||||
|
const regex = new RegExp(`([0-9]*)(${Object.keys(longFlags).map(k=>escapeRegex(k)).join('|')})([0-9]*)`, 'i');
|
||||||
|
console.log(regex);
|
||||||
|
|
||||||
|
let currentArgument = null;
|
||||||
|
let params = [];
|
||||||
|
|
||||||
|
for(let i=0; i<args.length; i++) {
|
||||||
|
const word = args[i];
|
||||||
|
|
||||||
|
if(!word) continue;
|
||||||
|
const [one,two,...chars] = word.split('');
|
||||||
|
if(one === '-' && two !== '-') {
|
||||||
|
const name = [ two, ...chars ].join('');
|
||||||
|
if(!keys.includes(name)) continue;
|
||||||
|
currentArgument = shortFlags[name];
|
||||||
|
if(currentArgument.required && !args[i+1]) {
|
||||||
|
console.error(`Argument ${currentArgument.name} is required and was not provided.`);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
} else if((one === '-' && two === '-') || one === '—') { //Handling for "long dash" on mobile phones x_x
|
||||||
|
const name = one === '—'
|
||||||
|
? [ two, ...chars ].join('').toLowerCase()
|
||||||
|
: chars.join('').toLowerCase(); //can convert to lowercase now that shortFlags are out of the way.
|
||||||
|
if(!keys.includes(name)) continue;
|
||||||
|
currentArgument = longFlags[name];
|
||||||
|
if(currentArgument.required && !args[i+1]) {
|
||||||
|
console.error(`Argument ${currentArgument.name} is required and was not provided.`);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
let match = regex.exec(word);
|
||||||
|
if(match && match[2]) {
|
||||||
|
currentArgument = longFlags[match[2]];
|
||||||
|
if(params.length > 0 && ['INTEGER', 'FLOAT'].includes(currentArgument.type)) { //15 pts
|
||||||
|
console.log("asgsaiughasiguassag")
|
||||||
|
const lastItem = params[params.length-1];
|
||||||
|
const beforeError = await this._handleTypeParsing(currentArgument, lastItem);
|
||||||
|
if(beforeError) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
params.pop();
|
||||||
|
currentArgument.value = lastItem;
|
||||||
|
parsedArguments.push(currentArgument);
|
||||||
|
currentArgument = null;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const value = match[1] || match[3];
|
||||||
|
const error = await this._handleTypeParsing(currentArgument, value);
|
||||||
|
if(value) {
|
||||||
|
if(error) {
|
||||||
|
if(currentArgument.required) {
|
||||||
|
console.error(`Argument ${currentArgument.name} is required and failed to meet requirements.`);
|
||||||
|
return undefined;
|
||||||
|
} else {
|
||||||
|
parsedArguments.push(currentArgument);
|
||||||
|
currentArgument = null;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
currentArgument.value = value;
|
||||||
|
parsedArguments.push(currentArgument);
|
||||||
|
currentArgument = null;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(currentArgument) {
|
||||||
|
const error = await this._handleTypeParsing(currentArgument, word);
|
||||||
|
if(error) {
|
||||||
|
if(currentArgument.default) {
|
||||||
|
params.push(word);
|
||||||
|
currentArgument.value = currentArgument.default;
|
||||||
|
parsedArguments.push(currentArgument);
|
||||||
|
currentArgument = null;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(currentArgument.required) {
|
||||||
|
if(currentArgument.infinite) {
|
||||||
|
if(currentArgument.value.length === 0) {
|
||||||
|
console.error(`1 Argument ${currentArgument.name} is required and failed to meet requirements.`);
|
||||||
|
return undefined;
|
||||||
|
} else {
|
||||||
|
parsedArguments.push(currentArgument);
|
||||||
|
currentArgument = null;
|
||||||
|
params.push(word);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error(`2 Argument ${currentArgument.name} is required and failed to meet requirements.`);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
currentArgument = null;
|
||||||
|
params.push(word);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(currentArgument.infinite) continue;
|
||||||
|
parsedArguments.push(currentArgument);
|
||||||
|
currentArgument = null;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
params.push(word);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
await message.channel.send(stripIndents`**arguments:** ${parsedArguments.map(a=>`${a.name}: ${a.value}`).join(' | ')}
|
||||||
|
**words:** ${params.join(', ')}`);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async _handleTypeParsing(argument, string) {
|
||||||
|
|
||||||
|
const parse = async (argument, string) => {
|
||||||
|
|
||||||
|
const { error, value } = await this.constructor.parseType(argument.type, string); //Cannot access static functions through "this".
|
||||||
|
if(error) return { error: true };
|
||||||
|
|
||||||
|
if(['INTEGER', 'FLOAT'].includes(argument.type)) {
|
||||||
|
const { min, max } = argument;
|
||||||
|
if(value > max && max !== null) {
|
||||||
|
return { error: true };
|
||||||
|
}
|
||||||
|
if(value < min && min !== null) {
|
||||||
|
return { error: true };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { error: false, value };
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const { error, value } = await parse(argument, string);
|
||||||
|
|
||||||
|
if(!error) {
|
||||||
|
argument.infinite
|
||||||
|
? argument.value.push(value)
|
||||||
|
: argument.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return error;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async _createFlags(args) {
|
||||||
|
|
||||||
|
let shortFlags = {};
|
||||||
|
let longFlags = {};
|
||||||
|
let keys = [];
|
||||||
|
|
||||||
|
for(const arg of args) {
|
||||||
|
|
||||||
|
let letters = [];
|
||||||
|
let names = [ arg.name, ...arg.aliases ];
|
||||||
|
keys = [...keys, ...names];
|
||||||
|
|
||||||
|
for(const name of names) {
|
||||||
|
longFlags[name] = arg;
|
||||||
|
|
||||||
|
if(!arg.types.includes('FLAG')) continue;
|
||||||
|
|
||||||
|
let letter = name.slice(0, 1);
|
||||||
|
if(letters.includes(letter)) continue;
|
||||||
|
if(keys.includes(letter)) letter = letter.toUpperCase();
|
||||||
|
if(keys.includes(letter)) break;
|
||||||
|
|
||||||
|
keys.push(letter);
|
||||||
|
letters.push(letter);
|
||||||
|
|
||||||
|
shortFlags[letter] = arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return { shortFlags, longFlags, keys };
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static async parseType(type, str) { //this is in the class for a reason, will soon reference to a user resolver etc.
|
||||||
|
|
||||||
|
//INTEGER AND FLOAT ARE SAME FUNCTION
|
||||||
|
|
||||||
|
const types = {
|
||||||
|
STRING: (str) => {
|
||||||
|
return { error: false, value: `${str}` };
|
||||||
|
},
|
||||||
|
INTEGER: (str) => {
|
||||||
|
const int = parseInt(str);
|
||||||
|
if(Number.isNaN(int)) return { error: true }
|
||||||
|
return { error: false, value: int };
|
||||||
|
},
|
||||||
|
FLOAT: (str) => {
|
||||||
|
const float = parseInt(str);
|
||||||
|
if(Number.isNaN(float)) return { error: true }
|
||||||
|
return { error: false, value: float };
|
||||||
|
},
|
||||||
|
BOOLEAN: (str) => {
|
||||||
|
const truthy = ['yes', 'y', 'true', 't', 'on', 'enable'];
|
||||||
|
const falsey = ['no', 'n', 'false', 'f', 'off', 'disable'];
|
||||||
|
if(typeof str === 'boolean') return { error: false, value: str };
|
||||||
|
|
||||||
|
if(typeof str === 'string') str = str.toLowerCase();
|
||||||
|
if(truthy.includes(str)) return { error: false, value: true };
|
||||||
|
if(falsey.includes(str)) return { error: false, value: false };
|
||||||
|
return { error: true };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return await types[type](str);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = CommandHandler;
|
@ -1,108 +0,0 @@
|
|||||||
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;
|
|
@ -12,6 +12,23 @@ const Guild = Structures.extend('Guild', (Guild) => {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async resolveMembers(members, strict = false) {
|
||||||
|
|
||||||
|
return await this.client.resolver.resolveMembers(members, this, strict);
|
||||||
|
}
|
||||||
|
|
||||||
|
async resolveChannels(channels, strict = false) {
|
||||||
|
|
||||||
|
return await this.client.resolver.resolveChannels(channels, this, strict);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async resolveRoles(roles, strict = false) {
|
||||||
|
|
||||||
|
return await this.client.resolver.resolveRoles(roles, this, strict);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ExtendedGuild;
|
return ExtendedGuild;
|
||||||
|
@ -12,6 +12,10 @@ const Message = Structures.extend('Message', (Message) => {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async respond() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ExtendedMessage;
|
return ExtendedMessage;
|
||||||
|
@ -8,8 +8,14 @@ const User = Structures.extend('User', (User) => {
|
|||||||
|
|
||||||
super(...args);
|
super(...args);
|
||||||
|
|
||||||
this._settings = null; //internal cache of current users' settings; should ALWAYS stay the same as database.
|
|
||||||
|
|
||||||
|
this._settings = null; //internal cache of current users' settings; should ALWAYS stay the same as database.
|
||||||
|
this._cached = Date.now();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
get timeSinceCached() {
|
||||||
|
return Date.now()-this._cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
62
structure/interfaces/Argument.js
Normal file
62
structure/interfaces/Argument.js
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
const { Util } = require('../../util');
|
||||||
|
|
||||||
|
class Argument {
|
||||||
|
|
||||||
|
constructor(client, options = {}) {
|
||||||
|
|
||||||
|
this.client = client;
|
||||||
|
|
||||||
|
this.name = options.name;
|
||||||
|
this.description = options.description;
|
||||||
|
this.aliases = options.aliases || []; //Aliases will work for both verbal and flag types. Careful for multiple aliases starting with different letters: more flags, more confusing.
|
||||||
|
|
||||||
|
this.type = (options.type && Constants.Types.includes(options.type) ? options.type : 'BOOLEAN'); //What type the argument is ['STRING', 'INTEGER', 'FLOAT', 'BOOLEAN'].
|
||||||
|
|
||||||
|
this.types = options.types || ['FLAG', 'VERBAL']; //['FLAG'], ['VERBAL'], or ['FLAG', 'VERBAL']. Declares if argument can be used verbally-only, flag-only, or both.
|
||||||
|
this.prompts = options.prompts || {
|
||||||
|
MISSING: `Argument **${this.name}** is missing and is required.`,
|
||||||
|
INVALID: `Argument **${this.name}** must be a \`${this.type}\` value.`
|
||||||
|
} //Default prompts to be replied to the user if an argument is missing or invalid.
|
||||||
|
|
||||||
|
//NOTE: Instead of telling the person the argument is missing and is required, ask them and continue the command execution afterwards. More work to do.
|
||||||
|
|
||||||
|
this.required = options.type === 'BOOLEAN' ? false : Boolean(options.required); //If the argument must be required for the command to work. Booleans
|
||||||
|
this.default = options.default || Constants.Defaults[options.type];
|
||||||
|
this.infinite = Boolean(options.infinite); //Accepts infinite amount of arguments e.g. -u @nolan @navy. If false, will only detect one user.
|
||||||
|
// Will turn value into an array instead of a string!!!
|
||||||
|
|
||||||
|
this.min = typeof options.min === 'number' ? options.min : null; //Min/max will only be used for INTEGER/FLOAT types.
|
||||||
|
this.max = typeof options.max === 'number' ? options.max : null;
|
||||||
|
|
||||||
|
this.parser = options.parser || null; //Option to pass a function to verify values.
|
||||||
|
|
||||||
|
this.value = this.infinite ? [] : null; //The value provided to the flag; assigned in the command handler.
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Argument;
|
||||||
|
|
||||||
|
const Constants = {
|
||||||
|
Defaults: { //these dont really mean anything, just default values. Most important one is the boolean one.
|
||||||
|
STRING: 'okay',
|
||||||
|
INTEGER: 5,
|
||||||
|
FLOAT: 2.5,
|
||||||
|
BOOLEAN: true
|
||||||
|
},
|
||||||
|
Types: [
|
||||||
|
'STRING',
|
||||||
|
'INTEGER',
|
||||||
|
'FLOAT',
|
||||||
|
'BOOLEAN',
|
||||||
|
'MEMBER',
|
||||||
|
'CHANNEL'
|
||||||
|
],
|
||||||
|
ArgumentTypes: [
|
||||||
|
'FLAG',
|
||||||
|
'VERBAL'
|
||||||
|
]
|
||||||
|
};
|
@ -2,17 +2,17 @@ const Component = require('./Component.js');
|
|||||||
|
|
||||||
class Command extends Component {
|
class Command extends Component {
|
||||||
|
|
||||||
constructor(client, opts = {}) {
|
constructor(manager, opts = {}) {
|
||||||
if(!opts) return null;
|
if(!opts) return null;
|
||||||
|
|
||||||
super(client, {
|
super(manager, {
|
||||||
id: opts.name,
|
id: opts.name,
|
||||||
type: 'command',
|
type: 'command',
|
||||||
disabled: opts.disabled || false,
|
disabled: opts.disabled || false,
|
||||||
guarded: opts.guarded || false
|
guarded: opts.guarded || false
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.defineProperty(this, 'client', { value: client });
|
this.manager = manager;
|
||||||
|
|
||||||
this.name = opts.name;
|
this.name = opts.name;
|
||||||
this.module = opts.module;
|
this.module = opts.module;
|
||||||
@ -25,6 +25,7 @@ class Command extends Component {
|
|||||||
this.restricted = Boolean(opts.restricted);
|
this.restricted = Boolean(opts.restricted);
|
||||||
this.archivable = opts.archivable === undefined ? false : Boolean(opts.archivable);
|
this.archivable = opts.archivable === undefined ? false : Boolean(opts.archivable);
|
||||||
this.guildOnly = Boolean(opts.guildOnly);
|
this.guildOnly = Boolean(opts.guildOnly);
|
||||||
|
this.arguments = opts.arguments || [];
|
||||||
|
|
||||||
this.clientPermissions = opts.clientPermissions || [];
|
this.clientPermissions = opts.clientPermissions || [];
|
||||||
this.memberPermissions = opts.memberPermissions || [];
|
this.memberPermissions = opts.memberPermissions || [];
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
class Component {
|
class Component {
|
||||||
|
|
||||||
constructor(manager, opts = {}) {
|
constructor(client, opts = {}) {
|
||||||
if(!opts) return null;
|
if(!opts) return null;
|
||||||
|
|
||||||
this.manager = manager;
|
this.client = client;
|
||||||
|
|
||||||
this.id = opts.id;
|
this.id = opts.id;
|
||||||
this.type = opts.type;
|
this.type = opts.type;
|
||||||
@ -13,7 +13,7 @@ class Component {
|
|||||||
this.guarded = Boolean(opts.guarded);
|
this.guarded = Boolean(opts.guarded);
|
||||||
this.disabled = Boolean(opts.disabled);
|
this.disabled = Boolean(opts.disabled);
|
||||||
|
|
||||||
this.registry = this.manager.registry;
|
this.registry = this.client.registry;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ class Component {
|
|||||||
newModule = require(this.directory);
|
newModule = require(this.directory);
|
||||||
|
|
||||||
if(typeof newModule === 'function') {
|
if(typeof newModule === 'function') {
|
||||||
newModule = new newModule(this.manager);
|
newModule = new newModule(this.client);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.registry.unloadComponent(this);
|
this.registry.unloadComponent(this);
|
||||||
|
@ -1,19 +1,17 @@
|
|||||||
const Component = require('./Component.js');
|
const Component = require('./Component.js');
|
||||||
const Collection = require('../../../util/interfaces/Collection.js');
|
const { Collection } = require('../../util');
|
||||||
|
|
||||||
class Module extends Component {
|
class Module extends Component {
|
||||||
|
|
||||||
constructor(client, opts = {}) {
|
constructor(manager, opts = {}) {
|
||||||
if(!opts) return null;
|
if(!opts) return null;
|
||||||
|
|
||||||
super(client, {
|
super(manager, {
|
||||||
id: opts.name,
|
id: opts.name,
|
||||||
type: 'module'
|
type: 'module'
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.defineProperty(this, 'client', {
|
this.manager = manager;
|
||||||
value: client
|
|
||||||
});
|
|
||||||
|
|
||||||
this.name = opts.name;
|
this.name = opts.name;
|
||||||
this.components = new Collection();
|
this.components = new Collection();
|
||||||
|
@ -15,10 +15,6 @@ class Observer extends Component {
|
|||||||
this.priority = opts.priority || 1;
|
this.priority = opts.priority || 1;
|
||||||
this.hooks = opts.hooks || [];
|
this.hooks = opts.hooks || [];
|
||||||
|
|
||||||
Object.defineProperty(this, 'client', {
|
|
||||||
value: client
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
execute() {
|
execute() {
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
|
Argument: require('./Argument.js'),
|
||||||
Command: require('./Command.js'),
|
Command: require('./Command.js'),
|
||||||
Component: require('./Component.js'),
|
Component: require('./Component.js'),
|
||||||
Inhibitor: require('./Inhibitor.js'),
|
Inhibitor: require('./Inhibitor.js'),
|
||||||
Setting: require('./Setting.js')
|
Setting: require('./Setting.js'),
|
||||||
}
|
Module: require('./Module.js'),
|
||||||
|
Observer: require('./Observer.js')
|
||||||
|
};
|
@ -1,5 +1,4 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
Collection: require('./Collection.js'),
|
Collection: require('./Collection.js'),
|
||||||
Util: require('./Util.js'),
|
Util: require('./Util.js')
|
||||||
Resolver: require('../middleware/client/Resolver.js')
|
|
||||||
}
|
}
|
1175
yarn-error.log
1175
yarn-error.log
File diff suppressed because it is too large
Load Diff
13
yarn.lock
13
yarn.lock
@ -169,6 +169,14 @@ chalk@^3.0.0:
|
|||||||
ansi-styles "^4.1.0"
|
ansi-styles "^4.1.0"
|
||||||
supports-color "^7.1.0"
|
supports-color "^7.1.0"
|
||||||
|
|
||||||
|
chalk@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.0.0.tgz#6e98081ed2d17faab615eb52ac66ec1fe6209e72"
|
||||||
|
integrity sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==
|
||||||
|
dependencies:
|
||||||
|
ansi-styles "^4.1.0"
|
||||||
|
supports-color "^7.1.0"
|
||||||
|
|
||||||
chardet@^0.7.0:
|
chardet@^0.7.0:
|
||||||
version "0.7.0"
|
version "0.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
|
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
|
||||||
@ -787,6 +795,11 @@ mkdirp@^0.5.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
minimist "^1.2.5"
|
minimist "^1.2.5"
|
||||||
|
|
||||||
|
moment@^2.24.0:
|
||||||
|
version "2.24.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"
|
||||||
|
integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==
|
||||||
|
|
||||||
mongodb@^3.5.5:
|
mongodb@^3.5.5:
|
||||||
version "3.5.5"
|
version "3.5.5"
|
||||||
resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-3.5.5.tgz#1334c3e5a384469ac7ef0dea69d59acc829a496a"
|
resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-3.5.5.tgz#1334c3e5a384469ac7ef0dea69d59acc829a496a"
|
||||||
|
Loading…
Reference in New Issue
Block a user