const { Client } = require('discord.js'); const escapeRegex = require('escape-string-regexp'); const options = require('../../options.json'); const Registry = require('./Registry.js'); const EventHooker = require('./EventHooker.js'); const Dispatcher = require('./Dispatcher.js'); const Resolver = require('./Resolver.js'); const Logger = require('./Logger.js'); const LocaleLoader = require('../language/LocaleLoader.js'); const RateLimiter = require('./RateLimiter.js'); const StorageManager = require('../storage/StorageManager.js'); const ModerationManager = require('../moderation/ModerationManager.js'); const { Guild, GuildMember, User, Message, TextChannel, Role } = require('../../structure/extensions/'); //eslint-disable-line no-unused-vars const { Command, Observer, Inhibitor, Setting } = require('../../structure/interfaces/'); const { DefaultGuild, DefaultUser } = require('../../util/defaults/'); const { Emojis } = require('../../util/'); class DiscordClient extends Client { constructor(options) { super(options.bot.clientOptions); this.registry = new Registry(this); this.eventHooker = new EventHooker(this); this.dispatcher = new Dispatcher(this); this.resolver = new Resolver(this); this.logger = new Logger(this); this.localeLoader = new LocaleLoader(this); this.rateLimiter = new RateLimiter(this); this.storageManager = new StorageManager(this, options.storage); this.moderationManager = new ModerationManager(this); this._options = options; this._built = false; this._devOnly = options.devOnly; //TODO: Default config for users and guilds. this._defaultConfig = {}; this._permissionCheck = null; this._evals = new Map(); process.on('message', this._handleMessage.bind(this)); if (this._options.libDebug) this.on('debug', this.logger.debug.bind(this.logger)); this.on('ready', () => { this.user.setActivity(`over ${this.guilds.cache.size} servers.`, { type: 'WATCHING' }); }); } async build() { if(this._built) return undefined; const beforeTime = Date.now(); await super.login(this._options.bot.token); this.localeLoader.loadLanguages(); await this.storageManager.initialize(); await this.registry.loadComponents('components/inhibitors/', Inhibitor); await this.registry.loadComponents('components/commands/', Command); await this.registry.loadComponents('components/observers/', Observer); await this.registry.loadComponents('components/settings/', Setting); await this.moderationManager.initialize(); await this.dispatcher.dispatch(); this.logger.info(`Took ${Date.now()-beforeTime}ms to build the client.`); this._built = true; return this._built; } format(language, index, parameters = { }, code = false) { let template = this.localeLoader.template(language, index); //.languages[language][index]; for(const emoji of Object.keys(Emojis)) { parameters[`emoji_${emoji}`] = Emojis[emoji]; } if(!template) { return `**Missing language index \`${language} [${index}]\` in languages. Contact a bot developer about this.**`; } for (const [param, val] of Object.entries(parameters)) { // eslint-disable-next-line require-unicode-regexp template = template.replace(new RegExp(`{${escapeRegex(param.toLowerCase())}}`, 'gi'), val); } if(code) { try { // eslint-disable-next-line no-eval template = eval(template); } catch(error) { this.client.logger.error(`Error in locale ${language}:${index} while executing code.\n${error.stack || error}`); } } return template; } async resolveUsers() { // eslint-disable-next-line prefer-rest-params return this.resolver.resolveUsers(...arguments); } async resolveUser() { // eslint-disable-next-line prefer-rest-params return this.resolver.resolveUser(...arguments); } async _handleMessage(message) { //Handle misc. messages. if (message._evalResult) this.evalResult(message); } async managerEval(script) { return new Promise((resolve, reject) => { this._evals.set(script, { resolve, reject }); process.send({ _mEval: true, script }); }); } evalResult({ script, result, error }) { const promise = this._evals.get(script); if (result) promise.resolve(result); else promise.reject(error); this._evals.delete(script); } defaultConfig(type) { if(this._defaultConfig[type]) return this._defaultConfig[type]; const settings = this.registry.components.filter((c) => c.type === 'setting' && c.resolve === type); let def = type === 'GUILD' ? DefaultGuild : DefaultUser; for(const setting of settings.values()) { if(setting.default !== null) { def = { ...def, ...setting.default }; } } this._defaultConfig[type] = def; return def; } get permissions() { if(this._permissionCheck) return this._permissionCheck; return this._permissionCheck = this.registry.components.get('inhibitor:permissions'); //eslint-disable-line no-return-assign } get prefix() { return this._options.prefix; } get storage() { return this.storageManager; } async aggregateStatistics() { } } module.exports = DiscordClient; const client = new DiscordClient(options); client.build(); process.on("unhandledRejection", (error) => { console.error("Unhandled promise rejection:", error); //eslint-disable-line no-console });