stoatx 0.7.6 → 0.8.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.
- package/README.md +3 -264
- package/dist/index.d.mts +367 -335
- package/dist/index.d.ts +367 -335
- package/dist/index.js +427 -147
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +428 -147
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
8
9
|
var __export = (target, all) => {
|
|
9
10
|
for (var name in all)
|
|
10
11
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -33,6 +34,7 @@ var index_exports = {};
|
|
|
33
34
|
__export(index_exports, {
|
|
34
35
|
Client: () => Client,
|
|
35
36
|
CommandRegistry: () => CommandRegistry,
|
|
37
|
+
CommandValidationError: () => CommandValidationError,
|
|
36
38
|
DefaultCooldownManager: () => DefaultCooldownManager,
|
|
37
39
|
Guard: () => Guard,
|
|
38
40
|
METADATA_KEYS: () => METADATA_KEYS,
|
|
@@ -61,6 +63,9 @@ var METADATA_KEYS = {
|
|
|
61
63
|
|
|
62
64
|
// src/decorators/store.ts
|
|
63
65
|
var DecoratorStore = class _DecoratorStore {
|
|
66
|
+
static {
|
|
67
|
+
__name(this, "DecoratorStore");
|
|
68
|
+
}
|
|
64
69
|
static instance;
|
|
65
70
|
/** Stoat classes with their SimpleCommand methods */
|
|
66
71
|
stoatClasses = /* @__PURE__ */ new Map();
|
|
@@ -77,8 +82,8 @@ var DecoratorStore = class _DecoratorStore {
|
|
|
77
82
|
return _DecoratorStore.instance;
|
|
78
83
|
}
|
|
79
84
|
/**
|
|
80
|
-
|
|
81
|
-
|
|
85
|
+
* Register a @Stoat decorated class
|
|
86
|
+
*/
|
|
82
87
|
registerStoatClass(classConstructor) {
|
|
83
88
|
if (!this.stoatClasses.has(classConstructor)) {
|
|
84
89
|
const instance = new classConstructor();
|
|
@@ -86,40 +91,40 @@ var DecoratorStore = class _DecoratorStore {
|
|
|
86
91
|
}
|
|
87
92
|
}
|
|
88
93
|
/**
|
|
89
|
-
|
|
90
|
-
|
|
94
|
+
* Get all registered Stoat classes with their instances
|
|
95
|
+
*/
|
|
91
96
|
getStoatClasses() {
|
|
92
97
|
return this.stoatClasses;
|
|
93
98
|
}
|
|
94
99
|
/**
|
|
95
|
-
|
|
96
|
-
|
|
100
|
+
* Add a registered command
|
|
101
|
+
*/
|
|
97
102
|
addCommand(command) {
|
|
98
103
|
this.commands.push(command);
|
|
99
104
|
}
|
|
100
105
|
/**
|
|
101
|
-
|
|
102
|
-
|
|
106
|
+
* Get all registered commands
|
|
107
|
+
*/
|
|
103
108
|
getCommands() {
|
|
104
109
|
return this.commands;
|
|
105
110
|
}
|
|
106
111
|
/**
|
|
107
|
-
|
|
108
|
-
|
|
112
|
+
* Clear all registered classes (useful for testing)
|
|
113
|
+
*/
|
|
109
114
|
clear() {
|
|
110
115
|
this.stoatClasses.clear();
|
|
111
116
|
this.commands = [];
|
|
112
117
|
this.initialized = false;
|
|
113
118
|
}
|
|
114
119
|
/**
|
|
115
|
-
|
|
116
|
-
|
|
120
|
+
* Mark as initialized
|
|
121
|
+
*/
|
|
117
122
|
markInitialized() {
|
|
118
123
|
this.initialized = true;
|
|
119
124
|
}
|
|
120
125
|
/**
|
|
121
|
-
|
|
122
|
-
|
|
126
|
+
* Check if initialized
|
|
127
|
+
*/
|
|
123
128
|
isInitialized() {
|
|
124
129
|
return this.initialized;
|
|
125
130
|
}
|
|
@@ -133,9 +138,11 @@ function Stoat() {
|
|
|
133
138
|
decoratorStore.registerStoatClass(target);
|
|
134
139
|
};
|
|
135
140
|
}
|
|
141
|
+
__name(Stoat, "Stoat");
|
|
136
142
|
function isStoatClass(target) {
|
|
137
143
|
return Reflect.getMetadata(METADATA_KEYS.IS_STOAT_CLASS, target) === true;
|
|
138
144
|
}
|
|
145
|
+
__name(isStoatClass, "isStoatClass");
|
|
139
146
|
|
|
140
147
|
// src/decorators/SimpleCommand.ts
|
|
141
148
|
var import_reflect_metadata2 = require("reflect-metadata");
|
|
@@ -151,9 +158,11 @@ function SimpleCommand(options = {}) {
|
|
|
151
158
|
return descriptor;
|
|
152
159
|
};
|
|
153
160
|
}
|
|
161
|
+
__name(SimpleCommand, "SimpleCommand");
|
|
154
162
|
function getSimpleCommands(target) {
|
|
155
163
|
return Reflect.getMetadata(METADATA_KEYS.SIMPLE_COMMANDS, target) || [];
|
|
156
164
|
}
|
|
165
|
+
__name(getSimpleCommands, "getSimpleCommands");
|
|
157
166
|
|
|
158
167
|
// src/decorators/Guard.ts
|
|
159
168
|
var import_reflect_metadata3 = require("reflect-metadata");
|
|
@@ -164,9 +173,11 @@ function Guard(guardClass) {
|
|
|
164
173
|
Reflect.defineMetadata(METADATA_KEYS.GUARDS, existingGuards, target);
|
|
165
174
|
};
|
|
166
175
|
}
|
|
176
|
+
__name(Guard, "Guard");
|
|
167
177
|
function getGuards(target) {
|
|
168
178
|
return Reflect.getMetadata(METADATA_KEYS.GUARDS, target) || [];
|
|
169
179
|
}
|
|
180
|
+
__name(getGuards, "getGuards");
|
|
170
181
|
|
|
171
182
|
// src/decorators/Events.ts
|
|
172
183
|
var import_reflect_metadata4 = require("reflect-metadata");
|
|
@@ -183,15 +194,19 @@ function createEventDecorator(event, type) {
|
|
|
183
194
|
return descriptor;
|
|
184
195
|
};
|
|
185
196
|
}
|
|
197
|
+
__name(createEventDecorator, "createEventDecorator");
|
|
186
198
|
function On(event) {
|
|
187
199
|
return createEventDecorator(event, "on");
|
|
188
200
|
}
|
|
201
|
+
__name(On, "On");
|
|
189
202
|
function Once(event) {
|
|
190
203
|
return createEventDecorator(event, "once");
|
|
191
204
|
}
|
|
205
|
+
__name(Once, "Once");
|
|
192
206
|
function getEventsMetadata(target) {
|
|
193
207
|
return Reflect.getMetadata(METADATA_KEYS.EVENTS, target) || [];
|
|
194
208
|
}
|
|
209
|
+
__name(getEventsMetadata, "getEventsMetadata");
|
|
195
210
|
|
|
196
211
|
// src/decorators/utils.ts
|
|
197
212
|
function buildSimpleCommandMetadata(options, methodName, category) {
|
|
@@ -202,11 +217,16 @@ function buildSimpleCommandMetadata(options, methodName, category) {
|
|
|
202
217
|
permissions: options.permissions ?? [],
|
|
203
218
|
category: options.category ?? category ?? "uncategorized",
|
|
204
219
|
cooldown: options.cooldown ?? 0,
|
|
205
|
-
...options.cooldownStorage !== void 0 ? {
|
|
220
|
+
...options.cooldownStorage !== void 0 ? {
|
|
221
|
+
cooldownStorage: options.cooldownStorage
|
|
222
|
+
} : {},
|
|
206
223
|
nsfw: options.nsfw ?? false,
|
|
207
|
-
ownerOnly: options.ownerOnly ?? false
|
|
224
|
+
ownerOnly: options.ownerOnly ?? false,
|
|
225
|
+
options: options.options ?? [],
|
|
226
|
+
args: options.args ?? []
|
|
208
227
|
};
|
|
209
228
|
}
|
|
229
|
+
__name(buildSimpleCommandMetadata, "buildSimpleCommandMetadata");
|
|
210
230
|
|
|
211
231
|
// src/registry.ts
|
|
212
232
|
var path = __toESM(require("path"));
|
|
@@ -214,6 +234,9 @@ var fs = __toESM(require("fs/promises"));
|
|
|
214
234
|
var import_node_url = require("url");
|
|
215
235
|
var import_tinyglobby = require("tinyglobby");
|
|
216
236
|
var CommandRegistry = class _CommandRegistry {
|
|
237
|
+
static {
|
|
238
|
+
__name(this, "CommandRegistry");
|
|
239
|
+
}
|
|
217
240
|
static DEFAULT_AUTO_DISCOVERY_IGNORES = [
|
|
218
241
|
"**/node_modules/**",
|
|
219
242
|
"**/.git/**",
|
|
@@ -226,23 +249,31 @@ var CommandRegistry = class _CommandRegistry {
|
|
|
226
249
|
registeredEvents = [];
|
|
227
250
|
extensions;
|
|
228
251
|
processedStoatClasses = /* @__PURE__ */ new Set();
|
|
229
|
-
constructor(extensions = [
|
|
252
|
+
constructor(extensions = [
|
|
253
|
+
".js",
|
|
254
|
+
".mjs",
|
|
255
|
+
".cjs"
|
|
256
|
+
]) {
|
|
230
257
|
this.extensions = extensions;
|
|
231
258
|
}
|
|
232
259
|
/**
|
|
233
|
-
|
|
234
|
-
|
|
260
|
+
* Get the number of registered commands
|
|
261
|
+
*/
|
|
235
262
|
get size() {
|
|
236
263
|
return this.commands.size;
|
|
237
264
|
}
|
|
238
265
|
/**
|
|
239
|
-
|
|
240
|
-
|
|
266
|
+
* Load commands from a directory using glob pattern matching
|
|
267
|
+
*/
|
|
241
268
|
async loadFromDirectory(directory) {
|
|
242
269
|
const patterns = this.extensions.map((ext) => path.join(directory, "**", `*${ext}`).replace(/\\/g, "/"));
|
|
243
270
|
for (const pattern of patterns) {
|
|
244
271
|
const files = await (0, import_tinyglobby.glob)(pattern, {
|
|
245
|
-
ignore: [
|
|
272
|
+
ignore: [
|
|
273
|
+
"**/*.d.ts",
|
|
274
|
+
"**/*.test.ts",
|
|
275
|
+
"**/*.spec.ts"
|
|
276
|
+
],
|
|
246
277
|
absolute: true
|
|
247
278
|
});
|
|
248
279
|
for (const file of files) {
|
|
@@ -252,19 +283,24 @@ var CommandRegistry = class _CommandRegistry {
|
|
|
252
283
|
console.log(`[Stoatx] Loaded ${this.commands.size} command(s) and ${this.registeredEvents.length} event(s)`);
|
|
253
284
|
}
|
|
254
285
|
/**
|
|
255
|
-
|
|
256
|
-
|
|
286
|
+
* Auto-discover command files across one or more roots.
|
|
287
|
+
*/
|
|
257
288
|
async autoDiscover(options = {}) {
|
|
258
|
-
const roots = options.roots?.length ? options.roots : [
|
|
289
|
+
const roots = options.roots?.length ? options.roots : [
|
|
290
|
+
process.cwd()
|
|
291
|
+
];
|
|
259
292
|
const includePatterns = options.include?.length ? options.include : this.getDefaultAutoDiscoveryPatterns();
|
|
260
|
-
const patterns = roots.flatMap(
|
|
261
|
-
(root) => includePatterns.map((pattern) => path.join(root, pattern).replace(/\\/g, "/"))
|
|
262
|
-
);
|
|
293
|
+
const patterns = roots.flatMap((root) => includePatterns.map((pattern) => path.join(root, pattern).replace(/\\/g, "/")));
|
|
263
294
|
const files = await (0, import_tinyglobby.glob)(patterns, {
|
|
264
|
-
ignore: [
|
|
295
|
+
ignore: [
|
|
296
|
+
..._CommandRegistry.DEFAULT_AUTO_DISCOVERY_IGNORES,
|
|
297
|
+
...options.ignore ?? []
|
|
298
|
+
],
|
|
265
299
|
absolute: true
|
|
266
300
|
});
|
|
267
|
-
const uniqueFiles = [
|
|
301
|
+
const uniqueFiles = [
|
|
302
|
+
...new Set(files)
|
|
303
|
+
];
|
|
268
304
|
let candidateFiles = 0;
|
|
269
305
|
for (const file of uniqueFiles) {
|
|
270
306
|
if (!await this.isLikelyCommandModule(file)) {
|
|
@@ -291,8 +327,8 @@ var CommandRegistry = class _CommandRegistry {
|
|
|
291
327
|
}
|
|
292
328
|
}
|
|
293
329
|
/**
|
|
294
|
-
|
|
295
|
-
|
|
330
|
+
* Register a command instance
|
|
331
|
+
*/
|
|
296
332
|
register(instance, metadata, classConstructor, methodName) {
|
|
297
333
|
const name = metadata.name.toLowerCase();
|
|
298
334
|
if (this.commands.has(name)) {
|
|
@@ -300,7 +336,12 @@ var CommandRegistry = class _CommandRegistry {
|
|
|
300
336
|
return;
|
|
301
337
|
}
|
|
302
338
|
this.validateGuards(classConstructor, metadata.name);
|
|
303
|
-
this.commands.set(name, {
|
|
339
|
+
this.commands.set(name, {
|
|
340
|
+
instance,
|
|
341
|
+
metadata,
|
|
342
|
+
methodName,
|
|
343
|
+
classConstructor
|
|
344
|
+
});
|
|
304
345
|
for (const alias of metadata.aliases) {
|
|
305
346
|
const aliasLower = alias.toLowerCase();
|
|
306
347
|
if (this.aliases.has(aliasLower) || this.commands.has(aliasLower)) {
|
|
@@ -311,41 +352,41 @@ var CommandRegistry = class _CommandRegistry {
|
|
|
311
352
|
}
|
|
312
353
|
}
|
|
313
354
|
/**
|
|
314
|
-
|
|
315
|
-
|
|
355
|
+
* Get a command by name or alias
|
|
356
|
+
*/
|
|
316
357
|
get(name) {
|
|
317
358
|
const lowerName = name.toLowerCase();
|
|
318
359
|
const resolvedName = this.aliases.get(lowerName) ?? lowerName;
|
|
319
360
|
return this.commands.get(resolvedName);
|
|
320
361
|
}
|
|
321
362
|
/**
|
|
322
|
-
|
|
323
|
-
|
|
363
|
+
* Check if a command exists
|
|
364
|
+
*/
|
|
324
365
|
has(name) {
|
|
325
366
|
const lowerName = name.toLowerCase();
|
|
326
367
|
return this.commands.has(lowerName) || this.aliases.has(lowerName);
|
|
327
368
|
}
|
|
328
369
|
/**
|
|
329
|
-
|
|
330
|
-
|
|
370
|
+
* Get all registered commands
|
|
371
|
+
*/
|
|
331
372
|
getAll() {
|
|
332
373
|
return Array.from(this.commands.values());
|
|
333
374
|
}
|
|
334
375
|
/**
|
|
335
|
-
|
|
336
|
-
|
|
376
|
+
* Get all command metadata
|
|
377
|
+
*/
|
|
337
378
|
getAllMetadata() {
|
|
338
379
|
return this.getAll().map((c) => c.metadata);
|
|
339
380
|
}
|
|
340
381
|
/**
|
|
341
|
-
|
|
342
|
-
|
|
382
|
+
* Get all registered events
|
|
383
|
+
*/
|
|
343
384
|
getEvents() {
|
|
344
385
|
return this.registeredEvents;
|
|
345
386
|
}
|
|
346
387
|
/**
|
|
347
|
-
|
|
348
|
-
|
|
388
|
+
* Get commands grouped by category
|
|
389
|
+
*/
|
|
349
390
|
getByCategory() {
|
|
350
391
|
const categories = /* @__PURE__ */ new Map();
|
|
351
392
|
for (const cmd of this.commands.values()) {
|
|
@@ -357,8 +398,8 @@ var CommandRegistry = class _CommandRegistry {
|
|
|
357
398
|
return categories;
|
|
358
399
|
}
|
|
359
400
|
/**
|
|
360
|
-
|
|
361
|
-
|
|
401
|
+
* Clear all commands
|
|
402
|
+
*/
|
|
362
403
|
clear() {
|
|
363
404
|
this.commands.clear();
|
|
364
405
|
this.aliases.clear();
|
|
@@ -366,51 +407,47 @@ var CommandRegistry = class _CommandRegistry {
|
|
|
366
407
|
this.processedStoatClasses.clear();
|
|
367
408
|
}
|
|
368
409
|
/**
|
|
369
|
-
|
|
370
|
-
|
|
410
|
+
* Iterate over commands
|
|
411
|
+
*/
|
|
371
412
|
[Symbol.iterator]() {
|
|
372
413
|
return this.commands.entries();
|
|
373
414
|
}
|
|
374
415
|
/**
|
|
375
|
-
|
|
376
|
-
|
|
416
|
+
* Iterate over command values
|
|
417
|
+
*/
|
|
377
418
|
values() {
|
|
378
419
|
return this.commands.values();
|
|
379
420
|
}
|
|
380
421
|
/**
|
|
381
|
-
|
|
382
|
-
|
|
422
|
+
* Iterate over command names
|
|
423
|
+
*/
|
|
383
424
|
keys() {
|
|
384
425
|
return this.commands.keys();
|
|
385
426
|
}
|
|
386
427
|
/**
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
428
|
+
* Validate that all guards on a command implement the required methods
|
|
429
|
+
* @param commandClass
|
|
430
|
+
* @param commandName
|
|
431
|
+
* @private
|
|
432
|
+
*/
|
|
392
433
|
validateGuards(commandClass, commandName) {
|
|
393
434
|
const guards = Reflect.getMetadata("stoatx:command:guards", commandClass) || [];
|
|
394
435
|
for (const GuardClass of guards) {
|
|
395
436
|
const guardInstance = new GuardClass();
|
|
396
437
|
if (typeof guardInstance.run !== "function") {
|
|
397
|
-
console.error(
|
|
398
|
-
`[Stoatx] FATAL: Guard "${GuardClass.name}" on command "${commandName}" does not have a run() method.`
|
|
399
|
-
);
|
|
438
|
+
console.error(`[Stoatx] FATAL: Guard "${GuardClass.name}" on command "${commandName}" does not have a run() method.`);
|
|
400
439
|
process.exit(1);
|
|
401
440
|
}
|
|
402
441
|
if (typeof guardInstance.guardFail !== "function") {
|
|
403
|
-
console.error(
|
|
404
|
-
`[Stoatx] FATAL: Guard "${GuardClass.name}" on command "${commandName}" does not have a guardFail() method.`
|
|
405
|
-
);
|
|
442
|
+
console.error(`[Stoatx] FATAL: Guard "${GuardClass.name}" on command "${commandName}" does not have a guardFail() method.`);
|
|
406
443
|
console.error(`[Stoatx] All guards must implement guardFail() to handle failed checks.`);
|
|
407
444
|
process.exit(1);
|
|
408
445
|
}
|
|
409
446
|
}
|
|
410
447
|
}
|
|
411
448
|
/**
|
|
412
|
-
|
|
413
|
-
|
|
449
|
+
* Load commands from a single file
|
|
450
|
+
*/
|
|
414
451
|
async loadFile(filePath, baseDir) {
|
|
415
452
|
try {
|
|
416
453
|
const knownStoatClasses = new Set(decoratorStore.getStoatClasses().keys());
|
|
@@ -432,9 +469,7 @@ var CommandRegistry = class _CommandRegistry {
|
|
|
432
469
|
const events = getEventsMetadata(stoatClass);
|
|
433
470
|
const category = this.getCategoryFromPath(filePath, baseDir);
|
|
434
471
|
if (simpleCommands.length === 0 && events.length === 0) {
|
|
435
|
-
console.warn(
|
|
436
|
-
`[Stoatx] Class ${stoatClass.name} is decorated with @Stoat but has no @SimpleCommand, @On or @Once methods. Skipping...`
|
|
437
|
-
);
|
|
472
|
+
console.warn(`[Stoatx] Class ${stoatClass.name} is decorated with @Stoat but has no @SimpleCommand, @On or @Once methods. Skipping...`);
|
|
438
473
|
this.processedStoatClasses.add(stoatClass);
|
|
439
474
|
return;
|
|
440
475
|
}
|
|
@@ -463,8 +498,8 @@ var CommandRegistry = class _CommandRegistry {
|
|
|
463
498
|
this.processedStoatClasses.add(stoatClass);
|
|
464
499
|
}
|
|
465
500
|
/**
|
|
466
|
-
|
|
467
|
-
|
|
501
|
+
* Derive category from file path relative to base directory
|
|
502
|
+
*/
|
|
468
503
|
getCategoryFromPath(filePath, baseDir) {
|
|
469
504
|
const relative2 = path.relative(baseDir, filePath);
|
|
470
505
|
const parts = relative2.split(path.sep);
|
|
@@ -477,8 +512,24 @@ var CommandRegistry = class _CommandRegistry {
|
|
|
477
512
|
|
|
478
513
|
// src/handler.ts
|
|
479
514
|
var import_reflect_metadata5 = require("reflect-metadata");
|
|
480
|
-
|
|
515
|
+
|
|
516
|
+
// src/error.ts
|
|
517
|
+
var CommandValidationError = class extends Error {
|
|
518
|
+
static {
|
|
519
|
+
__name(this, "CommandValidationError");
|
|
520
|
+
}
|
|
521
|
+
optionName;
|
|
522
|
+
constructor(optionName, message) {
|
|
523
|
+
super(message), this.optionName = optionName;
|
|
524
|
+
this.name = "CommandValidationError";
|
|
525
|
+
}
|
|
526
|
+
};
|
|
527
|
+
|
|
528
|
+
// src/handler.ts
|
|
481
529
|
var DefaultCooldownManager = class {
|
|
530
|
+
static {
|
|
531
|
+
__name(this, "DefaultCooldownManager");
|
|
532
|
+
}
|
|
482
533
|
cooldowns = /* @__PURE__ */ new Map();
|
|
483
534
|
check(ctx, metadata) {
|
|
484
535
|
if (metadata.cooldown <= 0) return true;
|
|
@@ -510,23 +561,10 @@ var DefaultCooldownManager = class {
|
|
|
510
561
|
this.cooldowns.clear();
|
|
511
562
|
}
|
|
512
563
|
};
|
|
513
|
-
var Client = class extends import_client.Client {
|
|
514
|
-
handler;
|
|
515
|
-
constructor(options) {
|
|
516
|
-
super();
|
|
517
|
-
this.handler = new StoatxHandler({ ...options, client: this });
|
|
518
|
-
this.on("messageCreate", async (message) => {
|
|
519
|
-
await this.handler.handle(message);
|
|
520
|
-
});
|
|
521
|
-
}
|
|
522
|
-
/**
|
|
523
|
-
* Initialize the StoatxHandler commands
|
|
524
|
-
*/
|
|
525
|
-
async initCommands() {
|
|
526
|
-
await this.handler.init();
|
|
527
|
-
}
|
|
528
|
-
};
|
|
529
564
|
var StoatxHandler = class {
|
|
565
|
+
static {
|
|
566
|
+
__name(this, "StoatxHandler");
|
|
567
|
+
}
|
|
530
568
|
commandsDir;
|
|
531
569
|
discoveryOptions;
|
|
532
570
|
prefixResolver;
|
|
@@ -535,6 +573,7 @@ var StoatxHandler = class {
|
|
|
535
573
|
cooldownManager;
|
|
536
574
|
disableMentionPrefix;
|
|
537
575
|
client;
|
|
576
|
+
flagPrefix;
|
|
538
577
|
constructor(options) {
|
|
539
578
|
this.client = options.client;
|
|
540
579
|
this.commandsDir = options.commandsDir;
|
|
@@ -544,10 +583,11 @@ var StoatxHandler = class {
|
|
|
544
583
|
this.registry = new CommandRegistry(options.extensions);
|
|
545
584
|
this.disableMentionPrefix = options.disableMentionPrefix ?? false;
|
|
546
585
|
this.cooldownManager = options.cooldownManager ?? new DefaultCooldownManager();
|
|
586
|
+
this.flagPrefix = options.flagPrefix || "-";
|
|
547
587
|
}
|
|
548
588
|
/**
|
|
549
|
-
|
|
550
|
-
|
|
589
|
+
* Initialize the handler - load all commands
|
|
590
|
+
*/
|
|
551
591
|
async init() {
|
|
552
592
|
if (this.commandsDir) {
|
|
553
593
|
await this.registry.loadFromDirectory(this.commandsDir);
|
|
@@ -557,21 +597,18 @@ var StoatxHandler = class {
|
|
|
557
597
|
this.attachEvents();
|
|
558
598
|
}
|
|
559
599
|
/**
|
|
560
|
-
|
|
561
|
-
|
|
600
|
+
* Attach registered events to the client
|
|
601
|
+
*/
|
|
562
602
|
attachEvents() {
|
|
563
603
|
const events = this.registry.getEvents();
|
|
564
604
|
for (const eventDef of events) {
|
|
565
|
-
const handler = async (...args) => {
|
|
605
|
+
const handler = /* @__PURE__ */ __name(async (...args) => {
|
|
566
606
|
try {
|
|
567
607
|
await eventDef.instance[eventDef.methodName](...args, this.client);
|
|
568
608
|
} catch (error) {
|
|
569
|
-
console.error(
|
|
570
|
-
`[Stoatx] Event Handler Error in @${eventDef.type === "on" ? "On" : "Once"}('${eventDef.event}'):`,
|
|
571
|
-
error
|
|
572
|
-
);
|
|
609
|
+
console.error(`[Stoatx] Event Handler Error in @${eventDef.type === "on" ? "On" : "Once"}('${eventDef.event}'):`, error);
|
|
573
610
|
}
|
|
574
|
-
};
|
|
611
|
+
}, "handler");
|
|
575
612
|
const eventName = eventDef.event;
|
|
576
613
|
if (eventDef.type === "once") {
|
|
577
614
|
this.client.once(eventName, handler);
|
|
@@ -581,8 +618,38 @@ var StoatxHandler = class {
|
|
|
581
618
|
}
|
|
582
619
|
}
|
|
583
620
|
/**
|
|
584
|
-
|
|
585
|
-
|
|
621
|
+
* Parses raw string arguments into positional args and key-value options
|
|
622
|
+
*/
|
|
623
|
+
parseCommandOptions(rawArgs) {
|
|
624
|
+
const args = [];
|
|
625
|
+
const options = {};
|
|
626
|
+
for (let i = 0; i < rawArgs.length; i++) {
|
|
627
|
+
const arg = rawArgs[i];
|
|
628
|
+
if (arg === void 0) continue;
|
|
629
|
+
if (arg.startsWith(this.flagPrefix)) {
|
|
630
|
+
let key = arg;
|
|
631
|
+
while (key.startsWith(this.flagPrefix)) {
|
|
632
|
+
key = key.slice(this.flagPrefix.length);
|
|
633
|
+
}
|
|
634
|
+
const nextArg = rawArgs[i + 1];
|
|
635
|
+
if (nextArg !== void 0 && !nextArg.startsWith(this.flagPrefix)) {
|
|
636
|
+
options[key] = nextArg;
|
|
637
|
+
i++;
|
|
638
|
+
} else {
|
|
639
|
+
options[key] = true;
|
|
640
|
+
}
|
|
641
|
+
} else {
|
|
642
|
+
args.push(arg);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
return {
|
|
646
|
+
args,
|
|
647
|
+
options
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* Parse a raw message into command context
|
|
652
|
+
*/
|
|
586
653
|
async parseMessage(rawContent, message, meta) {
|
|
587
654
|
const prefix = await this.resolvePrefix(meta.serverId);
|
|
588
655
|
let usedPrefix = prefix;
|
|
@@ -605,10 +672,11 @@ var StoatxHandler = class {
|
|
|
605
672
|
if (!withoutPrefix) {
|
|
606
673
|
return null;
|
|
607
674
|
}
|
|
608
|
-
const [commandName, ...
|
|
675
|
+
const [commandName, ...rawArgs] = withoutPrefix.split(/\s+/);
|
|
609
676
|
if (!commandName) {
|
|
610
677
|
return null;
|
|
611
678
|
}
|
|
679
|
+
const { args, options } = this.parseCommandOptions(rawArgs);
|
|
612
680
|
return {
|
|
613
681
|
client: this.client,
|
|
614
682
|
content: rawContent,
|
|
@@ -616,6 +684,7 @@ var StoatxHandler = class {
|
|
|
616
684
|
channelId: meta.channelId,
|
|
617
685
|
serverId: meta.serverId,
|
|
618
686
|
args,
|
|
687
|
+
options,
|
|
619
688
|
prefix: usedPrefix,
|
|
620
689
|
commandName: commandName.toLowerCase(),
|
|
621
690
|
reply: meta.reply,
|
|
@@ -623,16 +692,16 @@ var StoatxHandler = class {
|
|
|
623
692
|
};
|
|
624
693
|
}
|
|
625
694
|
/**
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
695
|
+
* Handle a message object using the configured message adapter
|
|
696
|
+
*
|
|
697
|
+
* @example
|
|
698
|
+
* ```ts
|
|
699
|
+
* // With message adapter configured
|
|
700
|
+
* client.on('messageCreate', (message) => {
|
|
701
|
+
* handler.handle(message);
|
|
702
|
+
* });
|
|
703
|
+
* ```
|
|
704
|
+
*/
|
|
636
705
|
async handle(message) {
|
|
637
706
|
if (!message.channel || !message.author || !message.content) {
|
|
638
707
|
return false;
|
|
@@ -644,9 +713,9 @@ var StoatxHandler = class {
|
|
|
644
713
|
const authorId = message.author.id;
|
|
645
714
|
const channelId = message.channel.id;
|
|
646
715
|
const serverId = message.server?.id;
|
|
647
|
-
const reply = async (content) => {
|
|
716
|
+
const reply = /* @__PURE__ */ __name(async (content) => {
|
|
648
717
|
return await message.channel.send(content);
|
|
649
|
-
};
|
|
718
|
+
}, "reply");
|
|
650
719
|
await this.handleMessage(rawContent, message, {
|
|
651
720
|
authorId,
|
|
652
721
|
channelId,
|
|
@@ -656,21 +725,21 @@ var StoatxHandler = class {
|
|
|
656
725
|
return true;
|
|
657
726
|
}
|
|
658
727
|
/**
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
728
|
+
* Handle a raw message string with metadata
|
|
729
|
+
*
|
|
730
|
+
* @example
|
|
731
|
+
* ```ts
|
|
732
|
+
* // Manual usage without message adapter
|
|
733
|
+
* client.on('messageCreate', (message) => {
|
|
734
|
+
* handler.handleMessage(message.content, message, {
|
|
735
|
+
* authorId: message.author.id,
|
|
736
|
+
* channelId: message.channel.id,
|
|
737
|
+
* serverId: message.server?.id,
|
|
738
|
+
* reply: (content) => message.channel.sendMessage(content),
|
|
739
|
+
* });
|
|
740
|
+
* });
|
|
741
|
+
* ```
|
|
742
|
+
*/
|
|
674
743
|
async handleMessage(rawContent, message, meta) {
|
|
675
744
|
const ctx = await this.parseMessage(rawContent, message, meta);
|
|
676
745
|
if (!ctx) {
|
|
@@ -679,14 +748,15 @@ var StoatxHandler = class {
|
|
|
679
748
|
await this.execute(ctx);
|
|
680
749
|
}
|
|
681
750
|
/**
|
|
682
|
-
|
|
683
|
-
|
|
751
|
+
* Execute a command with the given context
|
|
752
|
+
*/
|
|
684
753
|
async execute(ctx) {
|
|
685
754
|
const registered = this.registry.get(ctx.commandName);
|
|
686
755
|
if (!registered) {
|
|
687
756
|
return false;
|
|
688
757
|
}
|
|
689
758
|
const { instance, metadata, methodName, classConstructor } = registered;
|
|
759
|
+
console.log(`[Debug] Metadata options for ${ctx.commandName}:`, metadata.options);
|
|
690
760
|
if (metadata.ownerOnly && !this.owners.has(ctx.authorId)) {
|
|
691
761
|
await ctx.reply("This command is owner-only.");
|
|
692
762
|
return false;
|
|
@@ -719,6 +789,190 @@ var StoatxHandler = class {
|
|
|
719
789
|
}
|
|
720
790
|
}
|
|
721
791
|
}
|
|
792
|
+
if (metadata.args) {
|
|
793
|
+
const finalArgs = [];
|
|
794
|
+
for (let i = 0; i < metadata.args.length; i++) {
|
|
795
|
+
const def = metadata.args[i];
|
|
796
|
+
const rawValue = ctx.args[i];
|
|
797
|
+
if (def === void 0) continue;
|
|
798
|
+
if (rawValue === void 0) {
|
|
799
|
+
if (def.required) {
|
|
800
|
+
try {
|
|
801
|
+
throw new CommandValidationError(def.name, `Missing required argument: \`<${def.name}>\``);
|
|
802
|
+
} catch (error) {
|
|
803
|
+
if (typeof instance.onError === "function") {
|
|
804
|
+
await instance.onError(ctx, error);
|
|
805
|
+
} else {
|
|
806
|
+
await ctx.reply(`Missing required argument: \`<${def.name}>\``);
|
|
807
|
+
}
|
|
808
|
+
return false;
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
break;
|
|
812
|
+
}
|
|
813
|
+
if (def.type === "number") {
|
|
814
|
+
const numValue = Number(rawValue);
|
|
815
|
+
if (isNaN(numValue)) {
|
|
816
|
+
try {
|
|
817
|
+
throw new CommandValidationError(def.name, `Invalid value for \`<${def.name}>\`. Expected a number.`);
|
|
818
|
+
} catch (error) {
|
|
819
|
+
if (typeof instance.onError === "function") {
|
|
820
|
+
await instance.onError(ctx, error);
|
|
821
|
+
} else {
|
|
822
|
+
await ctx.reply(`Invalid value for \`<${def.name}>\`. Expected a number.`);
|
|
823
|
+
}
|
|
824
|
+
return false;
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
finalArgs[i] = numValue;
|
|
828
|
+
} else if (def.type === "boolean") {
|
|
829
|
+
finalArgs[i] = rawValue === "false" ? false : Boolean(rawValue);
|
|
830
|
+
} else if (def.type === "user") {
|
|
831
|
+
const match = String(rawValue).match(/^(?:<@)?([0-7][0-9A-HJKMNP-TV-Z]{25})>?$/i);
|
|
832
|
+
if (!match) {
|
|
833
|
+
try {
|
|
834
|
+
throw new CommandValidationError(def.name, `Invalid user mention for \`<${def.name}>\`.`);
|
|
835
|
+
} catch (error) {
|
|
836
|
+
if (typeof instance.onError === "function") {
|
|
837
|
+
await instance.onError(ctx, error);
|
|
838
|
+
} else {
|
|
839
|
+
await ctx.reply(`Invalid user mention for \`<${def.name}>\`.`);
|
|
840
|
+
}
|
|
841
|
+
return false;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
finalArgs[i] = match[1];
|
|
845
|
+
} else if (def.type === "channel") {
|
|
846
|
+
const match = String(rawValue).match(/^(?:<#)?([0-7][0-9A-HJKMNP-TV-Z]{25})>?$/i);
|
|
847
|
+
if (!match) {
|
|
848
|
+
try {
|
|
849
|
+
throw new CommandValidationError(def.name, `Invalid channel mention for \`<${def.name}>\`.`);
|
|
850
|
+
} catch (error) {
|
|
851
|
+
if (typeof instance.onError === "function") {
|
|
852
|
+
await instance.onError(ctx, error);
|
|
853
|
+
} else {
|
|
854
|
+
await ctx.reply(`Invalid channel mention for \`<${def.name}>\`.`);
|
|
855
|
+
}
|
|
856
|
+
return false;
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
finalArgs[i] = match[1];
|
|
860
|
+
} else if (def.type === "role") {
|
|
861
|
+
const match = String(rawValue).match(/^(?:<%)?([0-7][0-9A-HJKMNP-TV-Z]{25})>?$/i);
|
|
862
|
+
if (!match) {
|
|
863
|
+
try {
|
|
864
|
+
throw new CommandValidationError(def.name, `Invalid role mention for \`<${def.name}>\`.`);
|
|
865
|
+
} catch (error) {
|
|
866
|
+
if (typeof instance.onError === "function") {
|
|
867
|
+
await instance.onError(ctx, error);
|
|
868
|
+
} else {
|
|
869
|
+
await ctx.reply(`Invalid role mention for \`<${def.name}>\`.`);
|
|
870
|
+
}
|
|
871
|
+
return false;
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
finalArgs[i] = match[1];
|
|
875
|
+
} else {
|
|
876
|
+
finalArgs[i] = String(rawValue);
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
if (ctx.args.length > metadata.args.length) {
|
|
880
|
+
for (let i = metadata.args.length; i < ctx.args.length; i++) {
|
|
881
|
+
if (ctx.args[i] !== void 0) {
|
|
882
|
+
finalArgs.push(ctx.args[i]);
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
ctx.args = finalArgs;
|
|
887
|
+
}
|
|
888
|
+
const finalOptions = {};
|
|
889
|
+
const currentOptions = ctx.options || {};
|
|
890
|
+
if (metadata.options) {
|
|
891
|
+
for (const def of metadata.options) {
|
|
892
|
+
const rawValue = currentOptions[def.name];
|
|
893
|
+
if (rawValue === void 0) {
|
|
894
|
+
if (def.required) {
|
|
895
|
+
try {
|
|
896
|
+
throw new CommandValidationError(def.name, `Missing required option: \`--${def.name}\``);
|
|
897
|
+
} catch (error) {
|
|
898
|
+
if (typeof instance.onError === "function") {
|
|
899
|
+
await instance.onError(ctx, error);
|
|
900
|
+
} else {
|
|
901
|
+
await ctx.reply(`Missing required option: \`--${def.name}\``);
|
|
902
|
+
}
|
|
903
|
+
return false;
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
continue;
|
|
907
|
+
}
|
|
908
|
+
if (def.type === "number") {
|
|
909
|
+
const numValue = Number(rawValue);
|
|
910
|
+
if (isNaN(numValue)) {
|
|
911
|
+
try {
|
|
912
|
+
throw new CommandValidationError(def.name, `Invalid value for \`--${def.name}\`. Expected a number.`);
|
|
913
|
+
} catch (error) {
|
|
914
|
+
if (typeof instance.onError === "function") {
|
|
915
|
+
await instance.onError(ctx, error);
|
|
916
|
+
} else {
|
|
917
|
+
await ctx.reply(`Invalid value for \`--${def.name}\`. Expected a number.`);
|
|
918
|
+
}
|
|
919
|
+
return false;
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
finalOptions[def.name] = numValue;
|
|
923
|
+
} else if (def.type === "boolean") {
|
|
924
|
+
finalOptions[def.name] = rawValue === "false" ? false : Boolean(rawValue);
|
|
925
|
+
} else if (def.type === "user") {
|
|
926
|
+
const match = String(rawValue).match(/^(?:<@)?([0-7][0-9A-HJKMNP-TV-Z]{25})>?$/i);
|
|
927
|
+
if (!match) {
|
|
928
|
+
try {
|
|
929
|
+
throw new CommandValidationError(def.name, `Invalid user mention for \`--${def.name}\`.`);
|
|
930
|
+
} catch (error) {
|
|
931
|
+
if (typeof instance.onError === "function") {
|
|
932
|
+
await instance.onError(ctx, error);
|
|
933
|
+
} else {
|
|
934
|
+
await ctx.reply(`Invalid user mention for \`--${def.name}\`.`);
|
|
935
|
+
}
|
|
936
|
+
return false;
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
finalOptions[def.name] = match[1];
|
|
940
|
+
} else if (def.type === "channel") {
|
|
941
|
+
const match = String(rawValue).match(/^(?:<#)?([0-7][0-9A-HJKMNP-TV-Z]{25})>?$/i);
|
|
942
|
+
if (!match) {
|
|
943
|
+
try {
|
|
944
|
+
throw new CommandValidationError(def.name, `Invalid channel mention for \`--${def.name}\`.`);
|
|
945
|
+
} catch (error) {
|
|
946
|
+
if (typeof instance.onError === "function") {
|
|
947
|
+
await instance.onError(ctx, error);
|
|
948
|
+
} else {
|
|
949
|
+
await ctx.reply(`Invalid channel mention for \`--${def.name}\`.`);
|
|
950
|
+
}
|
|
951
|
+
return false;
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
finalOptions[def.name] = match[1];
|
|
955
|
+
} else if (def.type === "role") {
|
|
956
|
+
const match = String(rawValue).match(/^(?:<%)?([0-7][0-9A-HJKMNP-TV-Z]{25})>?$/i);
|
|
957
|
+
if (!match) {
|
|
958
|
+
try {
|
|
959
|
+
throw new CommandValidationError(def.name, `Invalid role mention for \`--${def.name}\`.`);
|
|
960
|
+
} catch (error) {
|
|
961
|
+
if (typeof instance.onError === "function") {
|
|
962
|
+
await instance.onError(ctx, error);
|
|
963
|
+
} else {
|
|
964
|
+
await ctx.reply(`Invalid role mention for \`--${def.name}\`.`);
|
|
965
|
+
}
|
|
966
|
+
return false;
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
finalOptions[def.name] = match[1];
|
|
970
|
+
} else {
|
|
971
|
+
finalOptions[def.name] = String(rawValue);
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
ctx.options = finalOptions;
|
|
975
|
+
}
|
|
722
976
|
if (!await this.cooldownManager.check(ctx, metadata)) {
|
|
723
977
|
const remaining = await this.cooldownManager.getRemaining(ctx, metadata);
|
|
724
978
|
if (typeof instance.onCooldown === "function") {
|
|
@@ -744,26 +998,26 @@ var StoatxHandler = class {
|
|
|
744
998
|
}
|
|
745
999
|
}
|
|
746
1000
|
/**
|
|
747
|
-
|
|
748
|
-
|
|
1001
|
+
* Get the command registry
|
|
1002
|
+
*/
|
|
749
1003
|
getRegistry() {
|
|
750
1004
|
return this.registry;
|
|
751
1005
|
}
|
|
752
1006
|
/**
|
|
753
|
-
|
|
754
|
-
|
|
1007
|
+
* Get a command by name or alias
|
|
1008
|
+
*/
|
|
755
1009
|
getCommand(name) {
|
|
756
1010
|
return this.registry.get(name);
|
|
757
1011
|
}
|
|
758
1012
|
/**
|
|
759
|
-
|
|
760
|
-
|
|
1013
|
+
* Get all commands
|
|
1014
|
+
*/
|
|
761
1015
|
getCommands() {
|
|
762
1016
|
return this.registry.getAll();
|
|
763
1017
|
}
|
|
764
1018
|
/**
|
|
765
|
-
|
|
766
|
-
|
|
1019
|
+
* Reload all commands
|
|
1020
|
+
*/
|
|
767
1021
|
async reload() {
|
|
768
1022
|
this.registry.clear();
|
|
769
1023
|
if (this.cooldownManager.clear) {
|
|
@@ -776,40 +1030,66 @@ var StoatxHandler = class {
|
|
|
776
1030
|
await this.registry.autoDiscover(this.discoveryOptions);
|
|
777
1031
|
}
|
|
778
1032
|
/**
|
|
779
|
-
|
|
780
|
-
|
|
1033
|
+
* Check if a user is an owner
|
|
1034
|
+
*/
|
|
781
1035
|
isOwner(userId) {
|
|
782
1036
|
return this.owners.has(userId);
|
|
783
1037
|
}
|
|
784
1038
|
/**
|
|
785
|
-
|
|
786
|
-
|
|
1039
|
+
* Add an owner
|
|
1040
|
+
*/
|
|
787
1041
|
addOwner(userId) {
|
|
788
1042
|
this.owners.add(userId);
|
|
789
1043
|
}
|
|
790
1044
|
/**
|
|
791
|
-
|
|
792
|
-
|
|
1045
|
+
* Remove an owner
|
|
1046
|
+
*/
|
|
793
1047
|
removeOwner(userId) {
|
|
794
1048
|
this.owners.delete(userId);
|
|
795
1049
|
}
|
|
796
1050
|
/**
|
|
797
|
-
|
|
798
|
-
|
|
1051
|
+
* Resolve the prefix for a context
|
|
1052
|
+
*/
|
|
799
1053
|
async resolvePrefix(serverId) {
|
|
800
1054
|
if (typeof this.prefixResolver === "function") {
|
|
801
|
-
return this.prefixResolver({
|
|
1055
|
+
return this.prefixResolver({
|
|
1056
|
+
serverId
|
|
1057
|
+
});
|
|
802
1058
|
}
|
|
803
1059
|
return this.prefixResolver;
|
|
804
1060
|
}
|
|
805
1061
|
};
|
|
806
1062
|
|
|
1063
|
+
// src/client.ts
|
|
1064
|
+
var import_client = require("@stoatx/client");
|
|
1065
|
+
var Client = class extends import_client.Client {
|
|
1066
|
+
static {
|
|
1067
|
+
__name(this, "Client");
|
|
1068
|
+
}
|
|
1069
|
+
handler;
|
|
1070
|
+
constructor(options) {
|
|
1071
|
+
super(options);
|
|
1072
|
+
this.handler = new StoatxHandler({
|
|
1073
|
+
...options,
|
|
1074
|
+
client: this
|
|
1075
|
+
});
|
|
1076
|
+
}
|
|
1077
|
+
async login(token) {
|
|
1078
|
+
await this.handler.init();
|
|
1079
|
+
return super.login(token);
|
|
1080
|
+
}
|
|
1081
|
+
async executeCommand(message) {
|
|
1082
|
+
await this.handler.handle(message);
|
|
1083
|
+
}
|
|
1084
|
+
};
|
|
1085
|
+
|
|
807
1086
|
// src/index.ts
|
|
808
1087
|
__reExport(index_exports, require("@stoatx/client"), module.exports);
|
|
809
1088
|
// Annotate the CommonJS export names for ESM import in node:
|
|
810
1089
|
0 && (module.exports = {
|
|
811
1090
|
Client,
|
|
812
1091
|
CommandRegistry,
|
|
1092
|
+
CommandValidationError,
|
|
813
1093
|
DefaultCooldownManager,
|
|
814
1094
|
Guard,
|
|
815
1095
|
METADATA_KEYS,
|