spacecommands 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +519 -0
  3. package/dist/Command.js +384 -0
  4. package/dist/CommandHandler.js +339 -0
  5. package/dist/FeatureHandler.js +89 -0
  6. package/dist/SlashCommands.js +220 -0
  7. package/dist/command-checks/channel-specific.js +30 -0
  8. package/dist/command-checks/guild-only-check.js +24 -0
  9. package/dist/command-checks/has-entitlement.js +52 -0
  10. package/dist/command-checks/has-permissions.js +39 -0
  11. package/dist/command-checks/has-roles.js +69 -0
  12. package/dist/command-checks/has-valid-arguments.js +54 -0
  13. package/dist/command-checks/in-cooldown.js +42 -0
  14. package/dist/command-checks/is-enabled.js +31 -0
  15. package/dist/command-checks/is-not-test-only.js +8 -0
  16. package/dist/command-checks/owner-only-check.js +22 -0
  17. package/dist/commands/channelonly.js +88 -0
  18. package/dist/commands/command.js +87 -0
  19. package/dist/commands/help/!ReactionListener.js +217 -0
  20. package/dist/commands/help/!get-first-embed.js +57 -0
  21. package/dist/commands/help/help.js +97 -0
  22. package/dist/commands/language.js +52 -0
  23. package/dist/commands/prefix.js +42 -0
  24. package/dist/commands/requiredrole.js +61 -0
  25. package/dist/commands/slash.js +102 -0
  26. package/dist/enums/CommandErrors.js +12 -0
  27. package/dist/enums/Events.js +9 -0
  28. package/dist/features/message-upsert.js +15 -0
  29. package/dist/get-all-files.js +25 -0
  30. package/dist/handlers/AutoModHandler.js +316 -0
  31. package/dist/handlers/ComponentHandler.js +110 -0
  32. package/dist/handlers/ContextMenuHandler.js +113 -0
  33. package/dist/handlers/EntitlementHandler.js +193 -0
  34. package/dist/handlers/ModalHandler.js +71 -0
  35. package/dist/handlers/PollHandler.js +230 -0
  36. package/dist/index.js +339 -0
  37. package/dist/message-handler.js +118 -0
  38. package/dist/models/channel-commands.js +49 -0
  39. package/dist/models/cooldown.js +51 -0
  40. package/dist/models/disabled-commands.js +45 -0
  41. package/dist/models/languages.js +46 -0
  42. package/dist/models/prefixes.js +46 -0
  43. package/dist/models/required-roles.js +49 -0
  44. package/dist/mongo.js +25 -0
  45. package/dist/permissions.js +39 -0
  46. package/dist/utils/ComponentBuilder.js +144 -0
  47. package/dist/utils/InteractionCollector.js +80 -0
  48. package/messages.json +391 -0
  49. package/package.json +72 -0
  50. package/typings.d.ts +276 -0
@@ -0,0 +1,339 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const fs_1 = __importDefault(require("fs"));
7
+ const path_1 = __importDefault(require("path"));
8
+ const Command_1 = __importDefault(require("./Command"));
9
+ const get_all_files_1 = __importDefault(require("./get-all-files"));
10
+ const disabled_commands_1 = __importDefault(require("./models/disabled-commands"));
11
+ const required_roles_1 = __importDefault(require("./models/required-roles"));
12
+ const cooldown_1 = __importDefault(require("./models/cooldown"));
13
+ const channel_commands_1 = __importDefault(require("./models/channel-commands"));
14
+ const permissions_1 = require("./permissions");
15
+ const CommandErrors_1 = __importDefault(require("./enums/CommandErrors"));
16
+ const Events_1 = __importDefault(require("./enums/Events"));
17
+ const replyFromCheck = async (reply, message) => {
18
+ if (!reply) {
19
+ return new Promise((resolve) => {
20
+ resolve('No reply provided.');
21
+ });
22
+ }
23
+ if (typeof reply === 'string') {
24
+ return message.reply({
25
+ content: reply,
26
+ });
27
+ }
28
+ else {
29
+ let embeds = [];
30
+ if (Array.isArray(reply)) {
31
+ embeds = reply;
32
+ }
33
+ else {
34
+ embeds.push(reply);
35
+ }
36
+ return message.reply({
37
+ embeds,
38
+ });
39
+ }
40
+ };
41
+ class CommandHandler {
42
+ _commands = new Map();
43
+ _client = null;
44
+ _commandChecks = new Map();
45
+ constructor(instance, client, dir, disabledDefaultCommands, typeScript = false) {
46
+ this._client = client;
47
+ this.setUp(instance, client, dir, disabledDefaultCommands, typeScript);
48
+ }
49
+ async setUp(instance, client, dir, disabledDefaultCommands, typeScript = false) {
50
+ // Do not pass in TS here because this should always compiled to JS
51
+ for (const [file, fileName] of (0, get_all_files_1.default)(path_1.default.join(__dirname, 'commands'))) {
52
+ if (disabledDefaultCommands.includes(fileName)) {
53
+ continue;
54
+ }
55
+ await this.registerCommand(instance, client, file, fileName, true);
56
+ }
57
+ // Do not pass in TS here because this should always compiled to JS
58
+ for (const [file, fileName] of (0, get_all_files_1.default)(path_1.default.join(__dirname, 'command-checks'))) {
59
+ this._commandChecks.set(fileName, require(file));
60
+ }
61
+ if (dir) {
62
+ if (!fs_1.default.existsSync(dir)) {
63
+ throw new Error(`Commands directory "${dir}" doesn't exist!`);
64
+ }
65
+ const files = (0, get_all_files_1.default)(dir, typeScript ? '.ts' : '');
66
+ const amount = files.length;
67
+ console.log(`SpaceCommands > Loaded ${amount} command${amount === 1 ? '' : 's'}.`);
68
+ for (const [file, fileName] of files) {
69
+ await this.registerCommand(instance, client, file, fileName);
70
+ }
71
+ if (instance.isDBConnected()) {
72
+ await this.fetchDisabledCommands();
73
+ await this.fetchRequiredRoles();
74
+ await this.fetchChannelOnly();
75
+ }
76
+ this._commands.forEach(async (command) => {
77
+ command.verifyDatabaseCooldowns();
78
+ if (instance.isDBConnected()) {
79
+ const results = await cooldown_1.default.find({
80
+ name: command.names[0],
81
+ type: command.globalCooldown ? 'global' : 'per-user',
82
+ });
83
+ for (const { _id, cooldown } of results) {
84
+ const [name, guildId, userId] = _id.split('-');
85
+ command.setCooldown(guildId, userId, cooldown);
86
+ }
87
+ }
88
+ });
89
+ client.on('messageCreate', async (message) => {
90
+ const guild = message.guild;
91
+ let content = message.content;
92
+ const prefix = instance.getPrefix(guild).toLowerCase();
93
+ if (!content.toLowerCase().startsWith(prefix)) {
94
+ return;
95
+ }
96
+ if (instance.ignoreBots && message.author.bot) {
97
+ return;
98
+ }
99
+ // Remove the prefix
100
+ content = content.substring(prefix.length);
101
+ const args = content.split(/[ ]+/g);
102
+ // Remove the "command", leaving just the arguments
103
+ const firstElement = args.shift();
104
+ if (!firstElement) {
105
+ return;
106
+ }
107
+ // Ensure the user input is lower case because it is stored as lower case in the map
108
+ const name = firstElement.toLowerCase();
109
+ const command = this._commands.get(name);
110
+ if (!command) {
111
+ return;
112
+ }
113
+ const { error, slash } = command;
114
+ if (slash === true) {
115
+ return;
116
+ }
117
+ const { member, author: user, channel } = message;
118
+ for (const [checkName, checkFunction,] of this._commandChecks.entries()) {
119
+ if (!(await checkFunction(guild, command, instance, member, user, (reply) => {
120
+ return replyFromCheck(reply, message);
121
+ }, args, name, channel))) {
122
+ return;
123
+ }
124
+ }
125
+ try {
126
+ command.execute(message, args);
127
+ }
128
+ catch (e) {
129
+ if (error) {
130
+ error({
131
+ error: CommandErrors_1.default.EXCEPTION,
132
+ command,
133
+ message,
134
+ info: {
135
+ error: e,
136
+ },
137
+ });
138
+ }
139
+ else {
140
+ message.reply(instance.messageHandler.get(guild, 'EXCEPTION'));
141
+ console.error(e);
142
+ }
143
+ instance.emit(Events_1.default.COMMAND_EXCEPTION, command, message, e);
144
+ }
145
+ });
146
+ }
147
+ const decrementCountdown = () => {
148
+ this._commands.forEach((command) => {
149
+ command.decrementCooldowns();
150
+ });
151
+ setTimeout(decrementCountdown, 1000);
152
+ };
153
+ decrementCountdown();
154
+ }
155
+ async registerCommand(instance, client, file, fileName, builtIn = false) {
156
+ let configuration = await require(file);
157
+ // person is using 'export default' so we import the default instead
158
+ if (configuration.default && Object.keys(configuration).length === 1) {
159
+ configuration = configuration.default;
160
+ }
161
+ const { name = fileName, category, commands, aliases, init, callback, run, execute, error, description, requiredPermissions, permissions, slash, expectedArgs, expectedArgsTypes, minArgs, options = [], } = configuration;
162
+ const { testOnly } = configuration;
163
+ if (run || execute) {
164
+ throw new Error(`Command located at "${file}" has either a "run" or "execute" function. Please rename that function to "callback".`);
165
+ }
166
+ let names = commands || aliases || [];
167
+ if (!name && (!names || names.length === 0)) {
168
+ throw new Error(`Command located at "${file}" does not have a name, commands array, or aliases array set. Please set at lease one property to specify the command name.`);
169
+ }
170
+ if (typeof names === 'string') {
171
+ names = [names];
172
+ }
173
+ if (typeof name !== 'string') {
174
+ throw new Error(`Command located at "${file}" does not have a string as a name.`);
175
+ }
176
+ if (name && !names.includes(name.toLowerCase())) {
177
+ names.unshift(name.toLowerCase());
178
+ }
179
+ if (requiredPermissions || permissions) {
180
+ for (const perm of requiredPermissions || permissions) {
181
+ if (!permissions_1.permissionList.includes(perm)) {
182
+ throw new Error(`Command located at "${file}" has an invalid permission node: "${perm}". Permissions must be all upper case and be one of the following: "${[
183
+ ...permissions_1.permissionList,
184
+ ].join('", "')}"`);
185
+ }
186
+ }
187
+ }
188
+ const missing = [];
189
+ if (!category) {
190
+ missing.push('Category');
191
+ }
192
+ if (!description) {
193
+ missing.push('Description');
194
+ }
195
+ if (missing.length && instance.showWarns) {
196
+ console.warn(`SpaceCommands > Command "${names[0]}" does not have the following properties: ${missing}.`);
197
+ }
198
+ if (testOnly && !instance.testServers.length) {
199
+ console.warn(`SpaceCommands > Command "${names[0]}" has "testOnly" set to true, but no test servers are defined.`);
200
+ }
201
+ if (slash !== undefined && typeof slash !== 'boolean' && slash !== 'both') {
202
+ throw new Error(`SpaceCommands > Command "${names[0]}" has a "slash" property that is not boolean "true" or string "both".`);
203
+ }
204
+ if (!slash && options.length) {
205
+ throw new Error(`SpaceCommands > Command "${names[0]}" has an "options" property but is not a slash command.`);
206
+ }
207
+ if (slash && !(builtIn && !instance.isDBConnected())) {
208
+ if (!description) {
209
+ throw new Error(`SpaceCommands > A description is required for command "${names[0]}" because it is a slash command.`);
210
+ }
211
+ if (minArgs !== undefined && !expectedArgs) {
212
+ throw new Error(`SpaceCommands > Command "${names[0]}" has "minArgs" property defined without "expectedArgs" property as a slash command.`);
213
+ }
214
+ if (options.length) {
215
+ for (const key in options) {
216
+ const name = options[key].name;
217
+ let lowerCase = name.toLowerCase();
218
+ if (name !== lowerCase && instance.showWarns) {
219
+ console.log(`SpaceCommands > Command "${names[0]}" has an option of "${name}". All option names must be lower case for slash commands. SpaceCommands will modify this for you.`);
220
+ }
221
+ if (lowerCase.match(/\s/g)) {
222
+ lowerCase = lowerCase.replace(/\s/g, '_');
223
+ console.log(`SpaceCommands > Command "${names[0]}" has an option of "${name}" with a white space in it. It is a best practice for option names to only be one word. SpaceCommands will modify this for you.`);
224
+ }
225
+ options[key].name = lowerCase;
226
+ }
227
+ }
228
+ else if (expectedArgs) {
229
+ const split = expectedArgs
230
+ .substring(1, expectedArgs.length - 1)
231
+ .split(/[>\]] [<\[]/);
232
+ for (let a = 0; a < split.length; ++a) {
233
+ const item = split[a];
234
+ options.push({
235
+ name: item.replace(/ /g, '-').toLowerCase(),
236
+ description: item,
237
+ type: expectedArgsTypes && expectedArgsTypes.length >= a
238
+ ? expectedArgsTypes[a]
239
+ : 'STRING',
240
+ required: a < minArgs,
241
+ });
242
+ }
243
+ }
244
+ const slashCommands = instance.slashCommands;
245
+ if (testOnly) {
246
+ for (const id of instance.testServers) {
247
+ await slashCommands.create(names[0], description, options, id);
248
+ }
249
+ }
250
+ else {
251
+ await slashCommands.create(names[0], description, options);
252
+ }
253
+ }
254
+ if (callback) {
255
+ if (init) {
256
+ init(client, instance);
257
+ }
258
+ const command = new Command_1.default(instance, client, names, callback, error, configuration);
259
+ for (const name of names) {
260
+ // Ensure the alias is lower case because we read as lower case later on
261
+ this._commands.set(name.toLowerCase(), command);
262
+ }
263
+ }
264
+ }
265
+ get commands() {
266
+ const results = [];
267
+ const added = [];
268
+ this._commands.forEach(({ names, category = '', description = '', expectedArgs = '', hidden = false, testOnly = false, }) => {
269
+ if (!added.includes(names[0])) {
270
+ results.push({
271
+ names: [...names],
272
+ category,
273
+ description,
274
+ syntax: expectedArgs,
275
+ hidden,
276
+ testOnly,
277
+ });
278
+ added.push(names[0]);
279
+ }
280
+ });
281
+ return results;
282
+ }
283
+ getCommandsByCategory(category, visibleOnly) {
284
+ const results = [];
285
+ for (const command of this.commands) {
286
+ if (visibleOnly && command.hidden) {
287
+ continue;
288
+ }
289
+ if (command.category === category) {
290
+ results.push(command);
291
+ }
292
+ }
293
+ return results;
294
+ }
295
+ getCommand(name) {
296
+ return this._commands.get(name);
297
+ }
298
+ getICommand(name) {
299
+ return this.commands.find((command) => command.names?.includes(name));
300
+ }
301
+ async fetchDisabledCommands() {
302
+ const results = await disabled_commands_1.default.find({});
303
+ for (const result of results) {
304
+ const { guildId, command } = result;
305
+ this._commands.get(command)?.disable(guildId);
306
+ }
307
+ }
308
+ async fetchRequiredRoles() {
309
+ const results = await required_roles_1.default.find({});
310
+ for (const result of results) {
311
+ const { guildId, command, requiredRoles } = result;
312
+ const cmd = this._commands.get(command);
313
+ if (cmd) {
314
+ for (const roleId of requiredRoles) {
315
+ cmd.addRequiredRole(guildId, roleId);
316
+ }
317
+ }
318
+ }
319
+ }
320
+ async fetchChannelOnly() {
321
+ const results = await channel_commands_1.default.find({});
322
+ for (const result of results) {
323
+ const { command, guildId, channels } = result;
324
+ const cmd = this._commands.get(command);
325
+ if (!cmd) {
326
+ continue;
327
+ }
328
+ const guild = this._client?.guilds.cache.get(guildId);
329
+ if (!guild) {
330
+ continue;
331
+ }
332
+ cmd.setRequiredChannels(guild, command, channels
333
+ .toString()
334
+ .replace(/\"\[\]/g, '')
335
+ .split(','));
336
+ }
337
+ }
338
+ }
339
+ exports.default = CommandHandler;
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ const fs_1 = __importDefault(require("fs"));
6
+ const path_1 = __importDefault(require("path"));
7
+ const get_all_files_1 = __importDefault(require("./get-all-files"));
8
+ class FeatureHandler {
9
+ _features = new Map(); // <Feature name, Disabled GuildIDs>
10
+ _client;
11
+ _instance;
12
+ constructor(client, instance, dir, typeScript = false) {
13
+ this._client = client;
14
+ this._instance = instance;
15
+ this.setup(dir, typeScript);
16
+ }
17
+ setup = async (dir, typeScript) => {
18
+ // Register built in features
19
+ for (const [file, fileName] of (0, get_all_files_1.default)(path_1.default.join(__dirname, 'features'), typeScript ? '.ts' : '')) {
20
+ this.registerFeature(require(file), fileName);
21
+ }
22
+ if (!dir) {
23
+ return;
24
+ }
25
+ if (!fs_1.default.existsSync(dir)) {
26
+ throw new Error(`Listeners directory "${dir}" doesn't exist!`);
27
+ }
28
+ const files = (0, get_all_files_1.default)(dir, typeScript ? '.ts' : '');
29
+ const amount = files.length;
30
+ if (amount > 0) {
31
+ console.log(`SpaceCommands > Loading ${amount} listener${amount === 1 ? '' : 's'}...`);
32
+ for (const [file, fileName] of files) {
33
+ const debug = `SpaceCommands DEBUG > Feature "${fileName}" load time`;
34
+ if (this._instance.debug) {
35
+ console.time(debug);
36
+ }
37
+ this.registerFeature(require(file), fileName);
38
+ if (this._instance.debug) {
39
+ console.timeEnd(debug);
40
+ }
41
+ }
42
+ }
43
+ else {
44
+ console.log(`SpaceCommands > Loaded ${amount} listener${amount === 1 ? '' : 's'}.`);
45
+ }
46
+ };
47
+ registerFeature = (file, fileName) => {
48
+ let func = file;
49
+ const { config } = file;
50
+ if (file.default) {
51
+ func = file.default;
52
+ }
53
+ let testOnly = false;
54
+ if (config) {
55
+ const { displayName, dbName } = config;
56
+ if (config.testOnly) {
57
+ testOnly = true;
58
+ }
59
+ const missing = [];
60
+ if (!displayName)
61
+ missing.push('displayName');
62
+ if (!dbName)
63
+ missing.push('dbName');
64
+ if (missing.length && this._instance.showWarns) {
65
+ console.warn(`SpaceCommands > Feature "${fileName}" has a config file that doesn't contain the following properties: ${missing}`);
66
+ }
67
+ }
68
+ else if (this._instance.showWarns) {
69
+ console.warn(`SpaceCommands > Feature "${fileName}" does not export a config object.`);
70
+ }
71
+ if (typeof func !== 'function') {
72
+ return;
73
+ }
74
+ const isEnabled = (guildId) => {
75
+ if (testOnly && !this._instance.testServers.includes(guildId)) {
76
+ return false;
77
+ }
78
+ return this.isEnabled(guildId, file);
79
+ };
80
+ if (config && config.loadDBFirst === true) {
81
+ console.warn(`SpaceCommands > config.loadDBFirst in features is no longer required. MongoDB is now connected to before any features or commands are loaded.`);
82
+ }
83
+ func(this._client, this._instance, isEnabled);
84
+ };
85
+ isEnabled = (guildId, feature) => {
86
+ return !(this._features.get(feature) || []).includes(guildId);
87
+ };
88
+ }
89
+ module.exports = FeatureHandler;
@@ -0,0 +1,220 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ const path_1 = __importDefault(require("path"));
6
+ const get_all_files_1 = __importDefault(require("./get-all-files"));
7
+ class SlashCommands {
8
+ _client;
9
+ _instance;
10
+ _commandChecks = new Map();
11
+ _autocompleteHandlers = new Map();
12
+ constructor(instance, listen, typeScript) {
13
+ this._instance = instance;
14
+ this._client = instance.client;
15
+ this.setUp(listen, typeScript);
16
+ }
17
+ async setUp(listen, typeScript = false) {
18
+ // Do not pass in TS here because this should always compiled to JS
19
+ for (const [file, fileName] of (0, get_all_files_1.default)(path_1.default.join(__dirname, 'command-checks'))) {
20
+ this._commandChecks.set(fileName, require(file));
21
+ }
22
+ const replyFromCheck = async (reply, interaction) => {
23
+ if (!reply) {
24
+ return new Promise((resolve) => {
25
+ resolve('No reply provided.');
26
+ });
27
+ }
28
+ if (typeof reply === 'string') {
29
+ return interaction.reply({
30
+ content: reply,
31
+ ephemeral: this._instance.ephemeral,
32
+ });
33
+ }
34
+ else {
35
+ let embeds = [];
36
+ if (Array.isArray(reply)) {
37
+ embeds = reply;
38
+ }
39
+ else {
40
+ embeds.push(reply);
41
+ }
42
+ return interaction.reply({
43
+ embeds,
44
+ ephemeral: this._instance.ephemeral,
45
+ });
46
+ }
47
+ };
48
+ if (listen) {
49
+ // Handle autocomplete interactions
50
+ this._client.on('interactionCreate', async (interaction) => {
51
+ if (!interaction.isAutocomplete()) {
52
+ return;
53
+ }
54
+ const handler = this._autocompleteHandlers.get(interaction.commandName);
55
+ if (handler) {
56
+ try {
57
+ await handler(interaction);
58
+ }
59
+ catch (error) {
60
+ console.error(`SpaceCommands > Error in autocomplete for "${interaction.commandName}":`, error);
61
+ }
62
+ }
63
+ });
64
+ // Handle command interactions
65
+ this._client.on('interactionCreate', async (interaction) => {
66
+ if (!interaction.isCommand()) {
67
+ return;
68
+ }
69
+ const { user, commandName, options, guild, channelId } = interaction;
70
+ const member = interaction.member;
71
+ const channel = guild?.channels.cache.get(channelId) || null;
72
+ const command = this._instance.commandHandler.getCommand(commandName);
73
+ if (!command) {
74
+ interaction.reply({
75
+ content: this._instance.messageHandler.get(guild, 'INVALID_SLASH_COMMAND'),
76
+ ephemeral: this._instance.ephemeral,
77
+ });
78
+ return;
79
+ }
80
+ const args = [];
81
+ options.data.forEach(({ value }) => {
82
+ args.push(String(value));
83
+ });
84
+ for (const [checkName, checkFunction,] of this._commandChecks.entries()) {
85
+ if (!(await checkFunction(guild, command, this._instance, member, user, (reply) => {
86
+ return replyFromCheck(reply, interaction);
87
+ }, args, commandName, channel))) {
88
+ return;
89
+ }
90
+ }
91
+ this.invokeCommand(interaction, commandName, options, args);
92
+ });
93
+ }
94
+ }
95
+ getCommands(guildId) {
96
+ if (guildId) {
97
+ return this._client.guilds.cache.get(guildId)?.commands;
98
+ }
99
+ return this._client.application?.commands;
100
+ }
101
+ async get(guildId) {
102
+ const commands = this.getCommands(guildId);
103
+ if (commands) {
104
+ // @ts-ignore
105
+ await commands.fetch();
106
+ return commands.cache;
107
+ }
108
+ return new Map();
109
+ }
110
+ didOptionsChange(command, options) {
111
+ return (command.options?.filter((opt, index) => {
112
+ return (opt?.required !== options[index]?.required &&
113
+ opt?.name !== options[index]?.name &&
114
+ opt?.options?.length !== options.length);
115
+ }).length !== 0);
116
+ }
117
+ async create(name, description, options, guildId) {
118
+ let commands;
119
+ if (guildId) {
120
+ commands = this._client.guilds.cache.get(guildId)?.commands;
121
+ }
122
+ else {
123
+ commands = this._client.application?.commands;
124
+ }
125
+ if (!commands) {
126
+ return;
127
+ }
128
+ // @ts-ignore
129
+ await commands.fetch();
130
+ const cmd = commands.cache.find((cmd) => cmd.name === name);
131
+ if (cmd) {
132
+ const optionsChanged = this.didOptionsChange(cmd, options);
133
+ if (cmd.description !== description ||
134
+ cmd.options.length !== options.length ||
135
+ optionsChanged) {
136
+ console.log(`SpaceCommands > Updating${guildId ? ' guild' : ''} slash command "${name}"`);
137
+ return commands?.edit(cmd.id, {
138
+ name,
139
+ description,
140
+ options,
141
+ });
142
+ }
143
+ return Promise.resolve(cmd);
144
+ }
145
+ if (commands) {
146
+ console.log(`SpaceCommands > Creating${guildId ? ' guild' : ''} slash command "${name}"`);
147
+ const newCommand = await commands.create({
148
+ name,
149
+ description,
150
+ options,
151
+ });
152
+ return newCommand;
153
+ }
154
+ return Promise.resolve(undefined);
155
+ }
156
+ async delete(commandId, guildId) {
157
+ const commands = this.getCommands(guildId);
158
+ if (commands) {
159
+ const cmd = commands.cache.get(commandId);
160
+ if (cmd) {
161
+ console.log(`SpaceCommands > Deleting${guildId ? ' guild' : ''} slash command "${cmd.name}"`);
162
+ cmd.delete();
163
+ }
164
+ }
165
+ return Promise.resolve(undefined);
166
+ }
167
+ async invokeCommand(interaction, commandName, options, args) {
168
+ const command = this._instance.commandHandler.getCommand(commandName);
169
+ if (!command || !command.callback) {
170
+ return;
171
+ }
172
+ const reply = await command.callback({
173
+ member: interaction.member,
174
+ guild: interaction.guild,
175
+ channel: interaction.channel,
176
+ args,
177
+ text: args.join(' '),
178
+ client: this._client,
179
+ instance: this._instance,
180
+ interaction,
181
+ options,
182
+ user: interaction.user,
183
+ });
184
+ if (reply) {
185
+ if (typeof reply === 'string') {
186
+ interaction.reply({
187
+ content: reply,
188
+ });
189
+ }
190
+ else if (typeof reply === 'object') {
191
+ if (reply.custom) {
192
+ interaction.reply(reply);
193
+ }
194
+ else {
195
+ let embeds = [];
196
+ if (Array.isArray(reply)) {
197
+ embeds = reply;
198
+ }
199
+ else {
200
+ embeds.push(reply);
201
+ }
202
+ interaction.reply({ embeds });
203
+ }
204
+ }
205
+ }
206
+ }
207
+ /**
208
+ * Register an autocomplete handler for a command
209
+ */
210
+ registerAutocomplete(commandName, handler) {
211
+ this._autocompleteHandlers.set(commandName, handler);
212
+ }
213
+ /**
214
+ * Get all registered autocomplete handlers
215
+ */
216
+ get autocompleteHandlers() {
217
+ return this._autocompleteHandlers;
218
+ }
219
+ }
220
+ module.exports = SlashCommands;
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ module.exports = (guild, command, instance, member, user, reply, args, name, channel) => {
3
+ if (!guild || !command || !command.names) {
4
+ return true;
5
+ }
6
+ const key = `${guild.id}-${command.names[0]}`;
7
+ const channels = command.requiredChannels.get(key);
8
+ if (channels && channels.length && !channels.includes(channel.id)) {
9
+ let channelList = '';
10
+ for (const channel of channels) {
11
+ channelList += `<#${channel}>, `;
12
+ }
13
+ channelList = channelList.substring(0, channelList.length - 2);
14
+ reply(instance.messageHandler.get(guild, 'ALLOWED_CHANNELS', {
15
+ CHANNELS: channelList,
16
+ })).then((message) => {
17
+ if (!message) {
18
+ return;
19
+ }
20
+ if (instance.delErrMsgCooldown === -1 || !message.deletable) {
21
+ return;
22
+ }
23
+ setTimeout(() => {
24
+ message.delete();
25
+ }, 1000 * instance.delErrMsgCooldown);
26
+ });
27
+ return false;
28
+ }
29
+ return true;
30
+ };