muskytape
Version:
Framework não oficial do Discord.js
176 lines (156 loc) • 5.99 kB
JavaScript
const discord = require('discord.js');
const CommandoRegistry = require('./registry');
const CommandDispatcher = require('./dispatcher');
const GuildSettingsHelper = require('./providers/helper');
/**
* Discord.js Client with a command framework
* @extends {Client}
*/
class CommandoClient extends discord.Client {
/**
* Options for a CommandoClient
* @typedef {ClientOptions} CommandoClientOptions
* @property {string} [commandPrefix=!] - Default command prefix
* @property {number} [commandEditableDuration=30] - Time in seconds that command messages should be editable
* @property {boolean} [nonCommandEditable=true] - Whether messages without commands can be edited to a command
* @property {string|string[]|Set<string>} [owner] - ID of the bot owner's Discord user, or multiple IDs
* @property {string} [invite] - Invite URL to the bot's support server
*/
/**
* @param {CommandoClientOptions} [options] - Options for the client
*/
constructor(options = {}) {
if(typeof options.commandPrefix === 'undefined') options.commandPrefix = '!';
if(options.commandPrefix === null) options.commandPrefix = '';
if(typeof options.commandEditableDuration === 'undefined') options.commandEditableDuration = 30;
if(typeof options.nonCommandEditable === 'undefined') options.nonCommandEditable = true;
super(options);
/**
* The client's command registry
* @type {CommandoRegistry}
*/
this.registry = new CommandoRegistry(this);
/**
* The client's command dispatcher
* @type {CommandDispatcher}
*/
this.dispatcher = new CommandDispatcher(this, this.registry);
/**
* The client's setting provider
* @type {?SettingProvider}
*/
this.provider = null;
/**
* Shortcut to use setting provider methods for the global settings
* @type {GuildSettingsHelper}
*/
this.settings = new GuildSettingsHelper(this, null);
/**
* Internal global command prefix, controlled by the {@link CommandoClient#commandPrefix} getter/setter
* @type {?string}
* @private
*/
this._commandPrefix = null;
// Set up command handling
const msgErr = err => { this.emit('error', err); };
this.on('message', message => { this.dispatcher.handleMessage(message).catch(msgErr); });
this.on('messageUpdate', (oldMessage, newMessage) => {
this.dispatcher.handleMessage(newMessage, oldMessage).catch(msgErr);
});
// Fetch the owner(s)
if(options.owner) {
this.once('ready', () => {
if(options.owner instanceof Array || options.owner instanceof Set) {
for(const owner of options.owner) {
this.users.fetch(owner).catch(err => {
this.emit('warn', `Unable to fetch owner ${owner}.`);
this.emit('error', err);
});
}
} else {
this.users.fetch(options.owner).catch(err => {
this.emit('warn', `Unable to fetch owner ${options.owner}.`);
this.emit('error', err);
});
}
});
}
}
/**
* Global command prefix. An empty string indicates that there is no default prefix, and only mentions will be used.
* Setting to `null` means that the default prefix from {@link CommandoClient#options} will be used instead.
* @type {string}
* @emits {@link CommandoClient#commandPrefixChange}
*/
get commandPrefix() {
if(typeof this._commandPrefix === 'undefined' || this._commandPrefix === null) return this.options.commandPrefix;
return this._commandPrefix;
}
set commandPrefix(prefix) {
this._commandPrefix = prefix;
this.emit('commandPrefixChange', null, this._commandPrefix);
}
/**
* Owners of the bot, set by the {@link CommandoClientOptions#owner} option
* <info>If you simply need to check if a user is an owner of the bot, please instead use
* {@link CommandoClient#isOwner}.</info>
* @type {?Array<User>}
* @readonly
*/
get owners() {
if(!this.options.owner) return null;
if(typeof this.options.owner === 'string') return [this.users.cache.get(this.options.owner)];
const owners = [];
for(const owner of this.options.owner) owners.push(this.users.cache.get(owner));
return owners;
}
/**
* Checks whether a user is an owner of the bot (in {@link CommandoClientOptions#owner})
* @param {UserResolvable} user - User to check for ownership
* @return {boolean}
*/
isOwner(user) {
if(!this.options.owner) return false;
user = this.users.resolve(user);
if(!user) throw new RangeError('Unable to resolve user.');
if(typeof this.options.owner === 'string') return user.id === this.options.owner;
if(this.options.owner instanceof Array) return this.options.owner.includes(user.id);
if(this.options.owner instanceof Set) return this.options.owner.has(user.id);
throw new RangeError('The client\'s "owner" option is an unknown value.');
}
/**
* Sets the setting provider to use, and initialises it once the client is ready
* @param {SettingProvider|Promise<SettingProvider>} provider Provider to use
* @return {Promise<void>}
*/
async setProvider(provider) {
const newProvider = await provider;
this.provider = newProvider;
if(this.readyTimestamp) {
this.emit('debug', `Provider set to ${newProvider.constructor.name} - initialising...`);
await newProvider.init(this);
this.emit('debug', 'Provider finished initialisation.');
return undefined;
}
this.emit('debug', `Provider set to ${newProvider.constructor.name} - will initialise once ready.`);
await new Promise(resolve => {
this.once('ready', () => {
this.emit('debug', `Initialising provider...`);
resolve(newProvider.init(this));
});
});
/**
* Emitted upon the client's provider finishing initialisation
* @event CommandoClient#providerReady
* @param {SettingProvider} provider - Provider that was initialised
*/
this.emit('providerReady', provider);
this.emit('debug', 'Provider finished initialisation.');
return undefined;
}
async destroy() {
await super.destroy();
if(this.provider) await this.provider.destroy();
}
}
module.exports = CommandoClient;