This repository has been archived by the owner on Dec 24, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 243
/
client.js
177 lines (158 loc) · 6.02 KB
/
client.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
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 {boolean} [unknownCommandResponse=true] - Whether the bot should respond to an unknown 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;
if(typeof options.unknownCommandResponse === 'undefined') options.unknownCommandResponse = 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.get(this.options.owner)];
const owners = [];
for(const owner of this.options.owner) owners.push(this.users.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) {
provider = await provider;
this.provider = provider;
if(this.readyTimestamp) {
this.emit('debug', `Provider set to ${provider.constructor.name} - initialising...`);
await provider.init(this);
this.emit('debug', 'Provider finished initialisation.');
return undefined;
}
this.emit('debug', `Provider set to ${provider.constructor.name} - will initialise once ready.`);
await new Promise(resolve => {
this.once('ready', () => {
this.emit('debug', `Initialising provider...`);
resolve(provider.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;