spacecommands 3.4.2 → 3.4.6

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.
package/README.md CHANGED
@@ -20,6 +20,16 @@ SpaceCommands is a modern, feature-rich Discord.js command handler library. Buil
20
20
  - 📊 **Poll Support** - Native Discord polls with result tracking
21
21
  - 🛡️ **AutoMod Integration** - Full AutoMod rule creation and management
22
22
 
23
+
24
+ ## ⚠️ Developer Notes (Common Pitfalls)
25
+
26
+ **Critical information for maintainers:**
27
+
28
+ 1. **SlashCommandBuilder Support**: The library MUST explicitly extract options from `configuration.data.toJSON().options`. Native `SlashCommandBuilder` objects hide the `type` field, which causes `DiscordAPIError[50035]: Invalid Form Body` if passed directly. **DO NOT** revert the `.toJSON()` extraction logic in `CommandHandler.ts`.
29
+ 2. **Command Registration Timing**: Global commands fail silently if registered before the client is ready. The `CommandHandler` constructor now waits for `client.once('ready')`. **DO NOT** remove this check.
30
+ 3. **Update Logic**: The `didOptionsChange` logic in `SlashCommands.ts` must use `||` (OR) operators to correctly detect changes. **DO NOT** change this back to `&&`.
31
+
32
+
23
33
  ## Installation
24
34
 
25
35
  **NPM**
@@ -569,4 +579,4 @@ If you need help or have questions, please open an issue on our [GitHub reposito
569
579
 
570
580
  ## License
571
581
 
572
- MIT License - see LICENSE file for details.
582
+ MIT License - see LICENSE file for details.
package/dist/Command.js CHANGED
@@ -34,7 +34,8 @@ class Command {
34
34
  _requiredChannels = new Map(); // <GuildID-Command, Channel IDs>
35
35
  _requiredEntitlements = [];
36
36
  _premiumOnly = false;
37
- constructor(instance, client, names, callback, error, { category, minArgs, maxArgs, syntaxError, expectedArgs, description, requiredPermissions, permissions, cooldown, globalCooldown, ownerOnly = false, hidden = false, guildOnly = false, testOnly = false, slash = false, requireRoles = false, requiredEntitlements, premiumOnly = false, }) {
37
+ _delete = false;
38
+ constructor(instance, client, names, callback, error, { category, minArgs, maxArgs, syntaxError, expectedArgs, description, requiredPermissions, permissions, cooldown, globalCooldown, ownerOnly = false, hidden = false, guildOnly = false, testOnly = false, slash = false, requireRoles = false, requiredEntitlements, premiumOnly = false, delete: del = false, }) {
38
39
  this.instance = instance;
39
40
  this.client = client;
40
41
  this._names = typeof names === 'string' ? [names] : names;
@@ -60,6 +61,7 @@ class Command {
60
61
  ? [requiredEntitlements]
61
62
  : requiredEntitlements || [];
62
63
  this._premiumOnly = premiumOnly;
64
+ this._delete = del;
63
65
  if (this.cooldown && this.globalCooldown) {
64
66
  throw new Error(`Command "${names[0]}" has both a global and per-user cooldown. Commands can only have up to one of these properties.`);
65
67
  }
@@ -380,5 +382,8 @@ class Command {
380
382
  get premiumOnly() {
381
383
  return this._premiumOnly;
382
384
  }
385
+ get delete() {
386
+ return this._delete;
387
+ }
383
388
  }
384
389
  module.exports = Command;
@@ -44,7 +44,14 @@ class CommandHandler {
44
44
  _commandChecks = new Map();
45
45
  constructor(instance, client, dir, disabledDefaultCommands, typeScript = false) {
46
46
  this._client = client;
47
- this.setUp(instance, client, dir, disabledDefaultCommands, typeScript);
47
+ if (client.isReady()) {
48
+ this.setUp(instance, client, dir, disabledDefaultCommands, typeScript);
49
+ }
50
+ else {
51
+ client.once('ready', () => {
52
+ this.setUp(instance, client, dir, disabledDefaultCommands, typeScript);
53
+ });
54
+ }
48
55
  }
49
56
  async setUp(instance, client, dir, disabledDefaultCommands, typeScript = false) {
50
57
  // Do not pass in TS here because this should always compiled to JS
@@ -158,12 +165,21 @@ class CommandHandler {
158
165
  if (configuration.default && Object.keys(configuration).length === 1) {
159
166
  configuration = configuration.default;
160
167
  }
161
- const { name = fileName, category, commands, aliases, init, callback, run, execute, error, description, requiredPermissions, permissions, slash, expectedArgs, expectedArgsTypes, minArgs, options = [], autocomplete, } = configuration;
168
+ const { name = fileName, category, commands, aliases, init, callback, run, execute, error, description, requiredPermissions, permissions, slash, expectedArgs, expectedArgsTypes, minArgs, options = [], autocomplete, delete: del, } = configuration;
162
169
  const { testOnly } = configuration;
163
170
  // Extract options from SlashCommandBuilder if using data property
164
171
  // This allows commands to use SlashCommandBuilder pattern while still working with SpaceCommands
165
172
  let finalOptions = options;
166
- if (configuration.data && configuration.data.options) {
173
+ if (configuration.data && typeof configuration.data.toJSON === 'function') {
174
+ const jsonData = configuration.data.toJSON();
175
+ if (jsonData.options) {
176
+ finalOptions = jsonData.options;
177
+ }
178
+ if (instance.debug && finalOptions && finalOptions.length) {
179
+ console.log(`SpaceCommands > Command "${name || fileName}" using SlashCommandBuilder with ${finalOptions.length} options`);
180
+ }
181
+ }
182
+ else if (configuration.data && configuration.data.options) {
167
183
  finalOptions = configuration.data.options;
168
184
  if (instance.debug) {
169
185
  console.log(`SpaceCommands > Command "${name || fileName}" using SlashCommandBuilder with ${finalOptions.length} options`);
@@ -185,6 +201,20 @@ class CommandHandler {
185
201
  if (name && !names.includes(name.toLowerCase())) {
186
202
  names.unshift(name.toLowerCase());
187
203
  }
204
+ if (del) {
205
+ if (slash !== false) {
206
+ const slashCommands = instance.slashCommands;
207
+ if (testOnly) {
208
+ for (const id of instance.testServers) {
209
+ await slashCommands.deleteByName(names[0], id);
210
+ }
211
+ }
212
+ else {
213
+ await slashCommands.deleteByName(names[0]);
214
+ }
215
+ }
216
+ return;
217
+ }
188
218
  if (requiredPermissions || permissions) {
189
219
  for (const perm of requiredPermissions || permissions) {
190
220
  if (!permissions_1.permissionList.includes(perm)) {
@@ -109,9 +109,9 @@ class SlashCommands {
109
109
  }
110
110
  didOptionsChange(command, options) {
111
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);
112
+ return (opt?.required !== options[index]?.required ||
113
+ opt?.name !== options[index]?.name ||
114
+ (opt?.options && opt.options.length !== options[index]?.options?.length));
115
115
  }).length !== 0);
116
116
  }
117
117
  async create(name, description, options, guildId) {
@@ -164,6 +164,16 @@ class SlashCommands {
164
164
  }
165
165
  return Promise.resolve(undefined);
166
166
  }
167
+ async deleteByName(name, guildId) {
168
+ const commands = this.getCommands(guildId);
169
+ if (commands) {
170
+ await commands.fetch();
171
+ const cmd = commands.cache.find((c) => c.name === name);
172
+ if (cmd) {
173
+ await this.delete(cmd.id, guildId);
174
+ }
175
+ }
176
+ }
167
177
  async invokeCommand(interaction, commandName, options, args) {
168
178
  const command = this._instance.commandHandler.getCommand(commandName);
169
179
  if (!command || !command.callback) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spacecommands",
3
- "version": "3.4.2",
3
+ "version": "3.4.6",
4
4
  "main": "dist/index.js",
5
5
  "types": "./typings.d.ts",
6
6
  "typings": "./typings.d.ts",
package/typings.d.ts CHANGED
@@ -193,6 +193,7 @@ export interface ICommand {
193
193
  requireRoles?: boolean
194
194
  requiredEntitlements?: string | string[]
195
195
  premiumOnly?: boolean
196
+ delete?: boolean
196
197
  }
197
198
 
198
199
  export interface ISlashCommand {