spacecommands 3.4.0 → 3.4.2

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
@@ -55,7 +55,6 @@ client.on('ready', () => {
55
55
  featuresDir: path.join(__dirname, 'features'),
56
56
  testServers: ['YOUR_TEST_SERVER_ID'],
57
57
  botOwners: ['YOUR_USER_ID'],
58
- premiumServers: ['YOUR_PREMIUM_SERVER_ID'],
59
58
  // Database (optional - Supabase recommended)
60
59
  supabaseUrl: process.env.SUPABASE_URL,
61
60
  supabaseKey: process.env.SUPABASE_KEY,
@@ -67,7 +66,7 @@ client.login(process.env.BOT_TOKEN);
67
66
 
68
67
  ## Supabase Setup (Recommended)
69
68
 
70
- SpaceCommands uses Supabase for data persistence. Here's how to set it up:
69
+ SpaceCommands now uses Supabase for data persistence instead of MongoDB. Here's how to set it up:
71
70
 
72
71
  ### 1. Create a Supabase Project
73
72
 
@@ -52,9 +52,6 @@ class CommandHandler {
52
52
  if (disabledDefaultCommands.includes(fileName)) {
53
53
  continue;
54
54
  }
55
- if (instance.debug) {
56
- console.log(`SpaceCommands > Registering internal command: ${fileName}`);
57
- }
58
55
  await this.registerCommand(instance, client, file, fileName, true);
59
56
  }
60
57
  // Do not pass in TS here because this should always compiled to JS
@@ -69,9 +66,6 @@ class CommandHandler {
69
66
  const amount = files.length;
70
67
  console.log(`SpaceCommands > Loaded ${amount} command${amount === 1 ? '' : 's'}.`);
71
68
  for (const [file, fileName] of files) {
72
- if (instance.debug) {
73
- console.log(`SpaceCommands > Registering command file: ${fileName}`);
74
- }
75
69
  await this.registerCommand(instance, client, file, fileName);
76
70
  }
77
71
  if (instance.isDBConnected()) {
@@ -143,7 +137,7 @@ class CommandHandler {
143
137
  });
144
138
  }
145
139
  else {
146
- message.reply(instance.messageHandler.get(guild, 'EXCEPTION', {}, message.author));
140
+ message.reply(instance.messageHandler.get(guild, 'EXCEPTION'));
147
141
  console.error(e);
148
142
  }
149
143
  instance.emit(Events_1.default.COMMAND_EXCEPTION, command, message, e);
@@ -166,6 +160,15 @@ class CommandHandler {
166
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
162
  const { testOnly } = configuration;
163
+ // Extract options from SlashCommandBuilder if using data property
164
+ // This allows commands to use SlashCommandBuilder pattern while still working with SpaceCommands
165
+ let finalOptions = options;
166
+ if (configuration.data && configuration.data.options) {
167
+ finalOptions = configuration.data.options;
168
+ if (instance.debug) {
169
+ console.log(`SpaceCommands > Command "${name || fileName}" using SlashCommandBuilder with ${finalOptions.length} options`);
170
+ }
171
+ }
169
172
  if (run || execute) {
170
173
  throw new Error(`Command located at "${file}" has either a "run" or "execute" function. Please rename that function to "callback".`);
171
174
  }
@@ -207,7 +210,7 @@ class CommandHandler {
207
210
  if (slash !== undefined && typeof slash !== 'boolean' && slash !== 'both') {
208
211
  throw new Error(`SpaceCommands > Command "${names[0]}" has a "slash" property that is not boolean "true" or string "both".`);
209
212
  }
210
- if (!slash && options.length) {
213
+ if (!slash && finalOptions.length) {
211
214
  throw new Error(`SpaceCommands > Command "${names[0]}" has an "options" property but is not a slash command.`);
212
215
  }
213
216
  if (slash && !(builtIn && !instance.isDBConnected())) {
@@ -217,9 +220,9 @@ class CommandHandler {
217
220
  if (minArgs !== undefined && !expectedArgs) {
218
221
  throw new Error(`SpaceCommands > Command "${names[0]}" has "minArgs" property defined without "expectedArgs" property as a slash command.`);
219
222
  }
220
- if (options.length) {
221
- for (const key in options) {
222
- const name = options[key].name;
223
+ if (finalOptions.length) {
224
+ for (const key in finalOptions) {
225
+ const name = finalOptions[key].name;
223
226
  let lowerCase = name.toLowerCase();
224
227
  if (name !== lowerCase && instance.showWarns) {
225
228
  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.`);
@@ -228,7 +231,7 @@ class CommandHandler {
228
231
  lowerCase = lowerCase.replace(/\s/g, '_');
229
232
  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.`);
230
233
  }
231
- options[key].name = lowerCase;
234
+ finalOptions[key].name = lowerCase;
232
235
  }
233
236
  }
234
237
  else if (expectedArgs) {
@@ -237,12 +240,12 @@ class CommandHandler {
237
240
  .split(/[>\]] [<\[]/);
238
241
  for (let a = 0; a < split.length; ++a) {
239
242
  const item = split[a];
240
- options.push({
243
+ finalOptions.push({
241
244
  name: item.replace(/ /g, '-').toLowerCase(),
242
245
  description: item,
243
246
  type: expectedArgsTypes && expectedArgsTypes.length >= a
244
247
  ? expectedArgsTypes[a]
245
- : 3,
248
+ : 'STRING',
246
249
  required: a < minArgs,
247
250
  });
248
251
  }
@@ -250,11 +253,11 @@ class CommandHandler {
250
253
  const slashCommands = instance.slashCommands;
251
254
  if (testOnly) {
252
255
  for (const id of instance.testServers) {
253
- await slashCommands.create(names[0], description, options, id);
256
+ await slashCommands.create(names[0], description, finalOptions, id);
254
257
  }
255
258
  }
256
259
  else {
257
- await slashCommands.create(names[0], description, options);
260
+ await slashCommands.create(names[0], description, finalOptions);
258
261
  }
259
262
  if (autocomplete) {
260
263
  const slashCommands = instance.slashCommands;
@@ -72,7 +72,7 @@ class SlashCommands {
72
72
  const command = this._instance.commandHandler.getCommand(commandName);
73
73
  if (!command) {
74
74
  interaction.reply({
75
- content: this._instance.messageHandler.get(guild, 'INVALID_SLASH_COMMAND', {}, interaction.user),
75
+ content: this._instance.messageHandler.get(guild, 'INVALID_SLASH_COMMAND'),
76
76
  ephemeral: this._instance.ephemeral,
77
77
  });
78
78
  return;
@@ -5,12 +5,6 @@ module.exports = async (guild, command, instance, member, user, reply) => {
5
5
  if (!requiredEntitlements.length && !premiumOnly) {
6
6
  return true;
7
7
  }
8
- if (guild && instance.premiumServers.includes(guild.id)) {
9
- return true;
10
- }
11
- else if (guild) {
12
- console.log(`[EntitlementCheck] Guild ${guild.id} NOT in premium list:`, instance.premiumServers);
13
- }
14
8
  const entitlementHandler = instance.entitlementHandler;
15
9
  if (!entitlementHandler) {
16
10
  console.warn('SpaceCommands > Command requires entitlements but EntitlementHandler is not initialized.');
@@ -19,12 +13,7 @@ module.exports = async (guild, command, instance, member, user, reply) => {
19
13
  // Check if user has required entitlements
20
14
  if (requiredEntitlements.length > 0) {
21
15
  const { hasEntitlement } = await entitlementHandler.hasAnyEntitlement(user.id, requiredEntitlements);
22
- let hasAccess = hasEntitlement;
23
- if (!hasAccess && guild) {
24
- const { hasEntitlement: hasGuildEntitlement } = await entitlementHandler.hasAnyGuildEntitlement(guild.id, requiredEntitlements);
25
- hasAccess = hasGuildEntitlement;
26
- }
27
- if (!hasAccess) {
16
+ if (!hasEntitlement) {
28
17
  reply(instance.messageHandler.get(guild, 'MISSING_ENTITLEMENT') ||
29
18
  'You need a premium subscription to use this command.').then((message) => {
30
19
  if (!message) {
@@ -43,12 +32,7 @@ module.exports = async (guild, command, instance, member, user, reply) => {
43
32
  // If premiumOnly is set, check for any active entitlement
44
33
  if (premiumOnly) {
45
34
  const allEntitlements = await entitlementHandler.getUserEntitlements(user.id);
46
- let hasPremium = allEntitlements.length > 0;
47
- if (!hasPremium && guild) {
48
- const guildEntitlements = await entitlementHandler.getGuildEntitlements(guild.id);
49
- hasPremium = guildEntitlements.length > 0;
50
- }
51
- if (!hasPremium) {
35
+ if (allEntitlements.length === 0) {
52
36
  reply(instance.messageHandler.get(guild, 'PREMIUM_ONLY') ||
53
37
  'This command is only available to premium subscribers.').then((message) => {
54
38
  if (!message) {
@@ -20,7 +20,7 @@ module.exports = (guild, command, instance, member, user, reply) => {
20
20
  else {
21
21
  reply(instance.messageHandler.get(guild, 'MISSING_PERMISSION', {
22
22
  PERM: perm,
23
- }, user)).then((message) => {
23
+ })).then((message) => {
24
24
  if (!message) {
25
25
  return;
26
26
  }
@@ -12,7 +12,6 @@ module.exports = {
12
12
  maxArgs: 2,
13
13
  expectedArgs: '<"enable" or "disable"> <Command Name>',
14
14
  cooldown: '2s',
15
- guildOnly: true,
16
15
  slash: 'both',
17
16
  options: [
18
17
  {
@@ -10,7 +10,6 @@ module.exports = {
10
10
  maxArgs: 1,
11
11
  expectedArgs: '[prefix]',
12
12
  cooldown: '2s',
13
- guildOnly: true,
14
13
  slash: 'both',
15
14
  options: [
16
15
  {
@@ -11,7 +11,6 @@ module.exports = {
11
11
  minArgs: 2,
12
12
  maxArgs: 2,
13
13
  expectedArgs: '<command> <none-or-roleid>',
14
- guildOnly: true,
15
14
  cooldown: '2s',
16
15
  slash: 'both',
17
16
  callback: async (options) => {
@@ -6,7 +6,6 @@ module.exports = {
6
6
  permissions: ['Administrator'],
7
7
  maxArgs: 1,
8
8
  expectedArgs: '[command-id]',
9
- guildOnly: true,
10
9
  ownerOnly: true,
11
10
  hidden: true,
12
11
  slash: 'both',
@@ -79,17 +79,6 @@ class EntitlementHandler {
79
79
  entitlement,
80
80
  };
81
81
  }
82
- /**
83
- * Check if a guild has any of the specified entitlements
84
- */
85
- async hasAnyGuildEntitlement(guildId, skuIds) {
86
- const entitlements = await this.getGuildEntitlements(guildId);
87
- const entitlement = entitlements.find((e) => skuIds.includes(e.skuId));
88
- return {
89
- hasEntitlement: !!entitlement,
90
- entitlement,
91
- };
92
- }
93
82
  /**
94
83
  * Check if a user has all of the specified entitlements
95
84
  */
@@ -32,7 +32,7 @@ class ModalHandler {
32
32
  for (const [file, fileName] of files) {
33
33
  const handler = require(file);
34
34
  const config = handler.default || handler;
35
- if (config.type === 'modal' || (config.customId && config.callback)) {
35
+ if (config.type === 'modal') {
36
36
  this.registerModalHandler(config);
37
37
  }
38
38
  }
package/dist/index.js CHANGED
@@ -36,7 +36,6 @@ class SpaceCommands extends events_1.EventEmitter {
36
36
  _ignoreBots = true;
37
37
  _botOwner = [];
38
38
  _testServers = [];
39
- _premiumServers = [];
40
39
  _defaultLanguage = 'english';
41
40
  _ephemeral = true;
42
41
  _debug = false;
@@ -99,12 +98,6 @@ class SpaceCommands extends events_1.EventEmitter {
99
98
  }
100
99
  this._botOwner = botOwners;
101
100
  }
102
- if (options.premiumServers) {
103
- if (typeof options.premiumServers === 'string') {
104
- options.premiumServers = [options.premiumServers];
105
- }
106
- this._premiumServers = options.premiumServers;
107
- }
108
101
  this._showWarns = showWarns;
109
102
  this._delErrMsgCooldown = delErrMsgCooldown;
110
103
  this._defaultLanguage = defaultLanguage.toLowerCase();
@@ -276,9 +269,6 @@ class SpaceCommands extends events_1.EventEmitter {
276
269
  get testServers() {
277
270
  return this._testServers;
278
271
  }
279
- get premiumServers() {
280
- return this._premiumServers;
281
- }
282
272
  get defaultLanguage() {
283
273
  return this._defaultLanguage;
284
274
  }
@@ -37,12 +37,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  const languages_1 = __importDefault(require("./models/languages"));
40
- const user_languages_1 = __importDefault(require("./models/user-languages"));
41
40
  const defualtMessages = require('../messages.json');
42
41
  class MessageHandler {
43
42
  _instance;
44
43
  _guildLanguages = new Map(); // <Guild ID, Language>
45
- _userLanguages = new Map(); // <User ID, Language>
46
44
  _languages = [];
47
45
  _messages = {};
48
46
  constructor(instance, messagePath) {
@@ -63,11 +61,6 @@ class MessageHandler {
63
61
  for (const { _id: guildId, language } of results) {
64
62
  this._guildLanguages.set(guildId, language);
65
63
  }
66
- const userResults = await user_languages_1.default.find();
67
- // @ts-ignore
68
- for (const { _id: userId, language } of userResults) {
69
- this._userLanguages.set(userId, language);
70
- }
71
64
  }
72
65
  })();
73
66
  }
@@ -79,26 +72,7 @@ class MessageHandler {
79
72
  this._guildLanguages.set(guild.id, language);
80
73
  }
81
74
  }
82
- async setUserLanguage(user, language) {
83
- this._userLanguages.set(user.id, language);
84
- if (this._instance.isDBConnected()) {
85
- await user_languages_1.default.findOneAndUpdate({
86
- _id: user.id,
87
- }, {
88
- _id: user.id,
89
- language,
90
- }, {
91
- upsert: true,
92
- });
93
- }
94
- }
95
- getLanguage(guild, user) {
96
- if (user) {
97
- const userLang = this._userLanguages.get(user.id);
98
- if (userLang) {
99
- return userLang;
100
- }
101
- }
75
+ getLanguage(guild) {
102
76
  if (guild) {
103
77
  const result = this._guildLanguages.get(guild.id);
104
78
  if (result) {
@@ -107,8 +81,8 @@ class MessageHandler {
107
81
  }
108
82
  return this._instance.defaultLanguage;
109
83
  }
110
- get(guild, messageId, args = {}, user) {
111
- const language = this.getLanguage(guild, user);
84
+ get(guild, messageId, args = {}) {
85
+ const language = this.getLanguage(guild);
112
86
  const translations = this._messages[messageId];
113
87
  if (!translations) {
114
88
  console.error(`SpaceCommands > Could not find the correct message to send for "${messageId}"`);
@@ -117,12 +91,12 @@ class MessageHandler {
117
91
  let result = translations[language];
118
92
  for (const key of Object.keys(args)) {
119
93
  const expression = new RegExp(`{${key}}`, 'g');
120
- result = result?.replace(expression, args[key]);
94
+ result = result.replace(expression, args[key]);
121
95
  }
122
96
  return result;
123
97
  }
124
- getEmbed(guild, embedId, itemId, args = {}, user) {
125
- const language = this.getLanguage(guild, user);
98
+ getEmbed(guild, embedId, itemId, args = {}) {
99
+ const language = this.getLanguage(guild);
126
100
  const items = this._messages[embedId];
127
101
  if (!items) {
128
102
  console.error(`SpaceCommands > Could not find the correct item to send for "${embedId}" -> "${itemId}"`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spacecommands",
3
- "version": "3.4.0",
3
+ "version": "3.4.2",
4
4
  "main": "dist/index.js",
5
5
  "types": "./typings.d.ts",
6
6
  "typings": "./typings.d.ts",
@@ -1,74 +0,0 @@
1
- "use strict";
2
- module.exports = {
3
- description: 'Deletes a slash command by name from Discord (Development Tool)',
4
- category: 'Development',
5
- minArgs: 1,
6
- expectedArgs: '<command_name> [guild_id]',
7
- ownerOnly: true,
8
- testOnly: true, // Visible immediately
9
- slash: 'both',
10
- options: [
11
- {
12
- name: 'command_name',
13
- description: 'The name of the command to delete (case insensitive)',
14
- type: 3, // STRING
15
- required: true,
16
- },
17
- {
18
- name: 'scope',
19
- description: 'Where to delete from',
20
- type: 3, // STRING
21
- required: false,
22
- choices: [
23
- { name: 'Global', value: 'global' },
24
- { name: 'Current Server', value: 'guild' },
25
- { name: 'All Servers (Dangerous)', value: 'all' }
26
- ]
27
- }
28
- ],
29
- callback: async (options) => {
30
- const { instance, args, interaction, guild, client } = options;
31
- const commandName = args[0].toLowerCase();
32
- const scope = args[1] || 'global';
33
- let deletedCount = 0;
34
- let status = [];
35
- // Helper to delete from a command manager
36
- const deleteFromManager = async (manager, sourceName) => {
37
- try {
38
- const commands = await manager.fetch();
39
- const cmd = commands.find((c) => c.name.toLowerCase() === commandName);
40
- if (cmd) {
41
- await manager.delete(cmd.id);
42
- status.push(`✅ Deleted **${cmd.name}** from ${sourceName}`);
43
- deletedCount++;
44
- }
45
- else {
46
- // status.push(`⚠️ Could not find **${commandName}** in ${sourceName}`)
47
- }
48
- }
49
- catch (err) {
50
- status.push(`❌ Error handling ${sourceName}: ${err.message}`);
51
- }
52
- };
53
- if (scope === 'global') {
54
- await deleteFromManager(client.application?.commands, 'Global');
55
- }
56
- if (scope === 'guild' || scope === 'all') {
57
- if (guild && scope === 'guild') {
58
- await deleteFromManager(guild.commands, `Guild: ${guild.name}`);
59
- }
60
- else if (scope === 'all') {
61
- // Iterate all guilds the bot is in
62
- const guilds = client.guilds.cache;
63
- status.push(`🔄 Scanning ${guilds.size} guilds...`);
64
- for (const [id, g] of guilds) {
65
- await deleteFromManager(g.commands, `Guild: ${g.name}`);
66
- }
67
- }
68
- }
69
- if (deletedCount === 0 && status.length === 0) {
70
- return `Could not find any command named "${commandName}" to delete in scope "${scope}".`;
71
- }
72
- return status.join('\n') || `Operation complete. Deleted ${deletedCount} instances.`;
73
- }
74
- };
@@ -1,51 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- const Events_1 = __importDefault(require("../enums/Events"));
6
- module.exports = {
7
- description: 'Displays or sets your personal language preference',
8
- category: 'Configuration',
9
- aliases: ['mylang'],
10
- maxArgs: 1,
11
- expectedArgs: '[language]',
12
- testOnly: true,
13
- cooldown: '2s',
14
- options: [
15
- {
16
- name: 'language',
17
- description: 'The language to set for yourself',
18
- type: 3, // STRING
19
- required: false,
20
- },
21
- ],
22
- slash: 'both',
23
- callback: async (options) => {
24
- const { channel, text, instance, user } = options;
25
- const { guild } = channel;
26
- // Allow in DMs
27
- // if (!guild) {
28
- // return
29
- // }
30
- const { messageHandler } = instance;
31
- if (!instance.isDBConnected()) {
32
- return instance.messageHandler.get(guild, 'NO_DATABASE_FOUND', {}, user);
33
- }
34
- const lang = text.toLowerCase();
35
- if (!lang) {
36
- return instance.messageHandler.get(guild, 'CURRENT_LANGUAGE', {
37
- LANGUAGE: instance.messageHandler.getLanguage(guild, user),
38
- }, user);
39
- }
40
- if (!messageHandler.languages().includes(lang)) {
41
- instance.emit(Events_1.default.LANGUAGE_NOT_SUPPORTED, guild, lang);
42
- return messageHandler.get(guild, 'LANGUAGE_NOT_SUPPORTED', {
43
- LANGUAGE: lang,
44
- }, user);
45
- }
46
- await instance.messageHandler.setUserLanguage(user, lang);
47
- return instance.messageHandler.get(guild, 'NEW_LANGUAGE', {
48
- LANGUAGE: lang,
49
- }, user);
50
- },
51
- };
@@ -1,65 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- // @ts-nocheck
4
- const supabase_1 = require("../supabase");
5
- const TABLE_NAME = 'spacecommands_user_languages';
6
- exports.default = {
7
- async find(filter = {}) {
8
- const client = (0, supabase_1.getSupabaseClient)();
9
- if (!client)
10
- return [];
11
- let query = client.from(TABLE_NAME).select('*');
12
- if (filter._id) {
13
- query = query.eq('user_id', filter._id);
14
- }
15
- const { data, error } = await query;
16
- if (error) {
17
- console.error('SpaceCommands > Error fetching user languages:', error);
18
- return [];
19
- }
20
- return (data || []).map((row) => ({
21
- _id: row.user_id,
22
- language: row.language,
23
- }));
24
- },
25
- async findOne(filter) {
26
- const client = (0, supabase_1.getSupabaseClient)();
27
- if (!client)
28
- return null;
29
- const { data, error } = await client
30
- .from(TABLE_NAME)
31
- .select('*')
32
- .eq('user_id', filter._id)
33
- .single();
34
- if (error) {
35
- if (error.code === 'PGRST116')
36
- return null;
37
- console.error('SpaceCommands > Error finding user language:', error);
38
- return null;
39
- }
40
- return {
41
- _id: data.user_id,
42
- language: data.language,
43
- };
44
- },
45
- async findOneAndUpdate(filter, update, options = {}) {
46
- const client = (0, supabase_1.getSupabaseClient)();
47
- if (!client)
48
- return null;
49
- const userId = filter._id;
50
- const language = update.language || update.$set?.language;
51
- const { data, error } = await client
52
- .from(TABLE_NAME)
53
- .upsert({ user_id: userId, language }, { onConflict: 'user_id' })
54
- .select()
55
- .single();
56
- if (error) {
57
- console.error('SpaceCommands > Error upserting user language:', error);
58
- return null;
59
- }
60
- return {
61
- _id: data.user_id,
62
- language: data.language,
63
- };
64
- },
65
- };