-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
228 lines (201 loc) · 8.84 KB
/
index.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
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
const {Client, TextChannel} = require('discord.js');
/**
* Options for a command
* @typedef {Object} CommandOptions
* @property {number} [minArgs=0] Minimum arguments required to trigger the event
* @property {number} [maxArgs=-1] Maximum arguments allowed to trigger the event
* @property {boolean} [displayInHelp=true] If false, the command will not be displayed in the help
* @property {string} [helpMessage="No help available"] Contains the help message displayed when user uses help command
* @property {UsageMessage} [usageMessage=""] Contains the message displayed when the command is badly used.
* @property {PermissionResolvable} [requiredPermission=0] Store the permission required to use the command (Will be ignored in DM/Group DM)
* @property {boolean} [dmAllowed=false] If false, the event isn't triggered when the command is sent by PM
*/
const DefaultCommandOptions = {
minArgs: 0,
maxArgs: -1,
displayInHelp: true,
helpMessage: 'No help available',
usageMessage: '',
requiredPermission: 0,
dmAllowed: false,
};
/**
* The callback function called when a valid command is typed
* @typedef {function} CommandCallback
* @param {Message} [message] Discord.js Message sent by user
* @param {string} [commandName] Command name, without the prefix
* @param {string[]} [args] Arguments passed to the command, sliced following spaces
*/
/**
* The usage message. It's a string, but you can type %p, which will be replaced by the prefix, %c, replaced by the command name (without prefix), and %f, replaced by the full command (with prefix)
* @typedef {string} UsageMessage
*/
/**
* Represents a bot which supports commands.
* {@link https://discord.js.org/#/docs/main/stable/class/Client | Discord.js Client class documentation.}
* @extends Client
*/
class CommandClient extends Client {
/**
* Class constructor
* @param {string} [prefix='!'] Prefix for commands.
* @param {ClientOptions} [options={}] Options to be passed to the Client.
* @constructor
*/
constructor(prefix = '!', options = {}) {
super(options);
/**
* Prefix of the command
* @type {string}
*/
this.prefix = prefix;
/**
* Set if the help is enabled
* @type {boolean}
*/
this.enableHelp = true;
/**
* All registered commands
* @type {Object.<string, CommandInfos>}
* @private
*/
this._registeredCommands = [];
/**
* Message displayed when usage doesn't have enough privileges to run command.
* @type {string}
* @default "You aren't allowed to run this command."
*/
this.commandNotAllowedMessage = "You aren't allowed to run this command.";
this.on("message", (message) => {
if(message.content.startsWith(prefix)) {
let args = message.content.substr(this.prefix.length).split(' ');
const commandName = args[0];
args.shift();
if(typeof this._registeredCommands[commandName] !== 'undefined') {
const commandData = this._registeredCommands[commandName];
const commandOptions = commandData.options;
const isDm = (message.channel.type === 'dm' || message.channel.type === 'group');
if(isDm && !commandOptions.dmAllowed)
return;
if(args.length < commandOptions.minArgs) {
this._displayUsageMessage(message, commandData);
return;
}
if(commandOptions.maxArgs >= 0 && args.length > commandOptions.maxArgs) {
this._displayUsageMessage(message, commandData);
return;
}
if(!isDm && commandOptions.requiredPermission !== 0 && (!(message.channel instanceof TextChannel) || !message.channel.permissionsFor(message.author).has(commandOptions.requiredPermission))) {
this._replyMessage(message, this.commandNotAllowedMessage);
return;
}
commandData.callback(message, commandName, args);
}
}
});
this.registerCommand("help", (message) => {
if(this.enableHelp) {
const dm = (message.channel.type === 'dm' || message.channel.type === 'group');
if (dm || !(message.channel instanceof TextChannel) || message.channel.permissionsFor(this.user).has("SEND_MESSAGES")) {
let help = '';
for(let key in this._registeredCommands) {
if(this._registeredCommands.hasOwnProperty(key)) {
let commandData = this._registeredCommands[key];
if (commandData.options.displayInHelp) {
if (!dm || commandData.options.dmAllowed) {
help += `\`${this.prefix}${commandData.name}\`: ${commandData.options.helpMessage}\n`;
}
}
}
}
if(help !== '') {
message.channel.send(help);
}
}
}
}, {
helpMessage: "List all available commands and their usages",
dmAllowed: true
})
}
/**
* Register a new command
* @param {string} command Command without the prefix
* @param {CommandCallback} callback Callback function called when the command is triggered
* @param {CommandOptions} [options={}] Options evaluated when the command is triggered
*/
registerCommand(command, callback, options = {}) {
let o = {};
Object.assign(o, DefaultCommandOptions);
Object.assign(o, options);
/**
* Stores the informations about the command
* @typedef {{name: string, callback: CommandCallback, options: CommandOptions}} CommandInfos
*/
const commandInfos = {
name: command,
callback: callback,
options: o,
};
this._registeredCommands[command] = commandInfos;
}
/**
* Unregisters a command if registered
* @param {string} command Command to unregister
*/
unregisterCommand(command) {
if(typeof this._registeredCommands[command] !== 'undefined') {
delete this._registeredCommands[command];
}
}
/**
* Edit command datas (callback, and options, if specified)
* @param {string} command The command to edit
* @param {CommandCallback} callback The new callback function
* @param {CommandOptions} [options={}] The options to edit (if an existing option isn't set, its value will be unchanged)
*/
editCommandData(command,callback,options = {}) {
if(typeof this._registeredCommands[command] !== 'undefined') {
this._registeredCommands[command].callback = callback;
Object.assign(this._registeredCommands[command].options, options);
}
}
/**
* Edit command options
* @param {string} command The command to edit
* @param {CommandOptions} options The options to edit (if an existing option isn't set, its value will be unchanged)
*/
editCommandOptions(command, options) {
if(typeof this._registeredCommands[command] !== 'undefined') {
Object.assign(this._registeredCommands[command].options, options);
}
}
/**
* Display the usage message if existing.
* @param {Message} message the original Discord message
* @param {CommandInfos} commandInfos
* @private
*/
_displayUsageMessage(message, commandInfos) {
const options = commandInfos.options;
if(options.usageMessage !== '') {
let usageMessage = options.usageMessage;
usageMessage = usageMessage.replace("%p", this.prefix);
usageMessage = usageMessage.replace("%f", this.prefix + commandInfos.name);
usageMessage = usageMessage.replace("%c", commandInfos.name);
this._replyMessage(message, "Usage: `" + usageMessage + "`")
}
}
/**
* Used to reply to a message, an check to avoid errors
* @param {Message} originalMessage Original message posted by user
* @param {string} messageToReply Content of the reply
* @private
*/
_replyMessage(originalMessage, messageToReply) {
if(messageToReply !== '' && (originalMessage.channel.type === 'dm' || originalMessage.channel.type === 'group' || !(originalMessage.channel instanceof TextChannel) || originalMessage.channel.permissionsFor(this.user).has("SEND_MESSAGES"))) {
originalMessage.reply(messageToReply);
}
}
}
exports.CommandClient = CommandClient;