stoatx 0.2.1 → 0.4.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/LICENSE +21 -0
- package/README.md +42 -56
- package/dist/index.d.mts +109 -31
- package/dist/index.d.ts +109 -31
- package/dist/index.js +120 -26
- package/dist/index.mjs +115 -24
- package/package.json +8 -9
package/dist/index.mjs
CHANGED
|
@@ -3,9 +3,10 @@ import "reflect-metadata";
|
|
|
3
3
|
|
|
4
4
|
// src/decorators/keys.ts
|
|
5
5
|
var METADATA_KEYS = {
|
|
6
|
-
IS_STOAT_CLASS: /* @__PURE__ */ Symbol("
|
|
7
|
-
SIMPLE_COMMANDS: /* @__PURE__ */ Symbol("
|
|
8
|
-
GUARDS: "
|
|
6
|
+
IS_STOAT_CLASS: /* @__PURE__ */ Symbol("stoatx:stoat:isClass"),
|
|
7
|
+
SIMPLE_COMMANDS: /* @__PURE__ */ Symbol("stoatx:stoat:simpleCommands"),
|
|
8
|
+
GUARDS: "stoatx:command:guards",
|
|
9
|
+
EVENTS: /* @__PURE__ */ Symbol("stoatx:stoat:events")
|
|
9
10
|
};
|
|
10
11
|
|
|
11
12
|
// src/decorators/store.ts
|
|
@@ -116,6 +117,31 @@ function getGuards(target) {
|
|
|
116
117
|
return Reflect.getMetadata(METADATA_KEYS.GUARDS, target) || [];
|
|
117
118
|
}
|
|
118
119
|
|
|
120
|
+
// src/decorators/Events.ts
|
|
121
|
+
import "reflect-metadata";
|
|
122
|
+
function createEventDecorator(event, type) {
|
|
123
|
+
return (target, propertyKey, descriptor) => {
|
|
124
|
+
const constructor = target.constructor;
|
|
125
|
+
const existingEvents = Reflect.getMetadata(METADATA_KEYS.EVENTS, constructor) || [];
|
|
126
|
+
existingEvents.push({
|
|
127
|
+
methodName: String(propertyKey),
|
|
128
|
+
event,
|
|
129
|
+
type
|
|
130
|
+
});
|
|
131
|
+
Reflect.defineMetadata(METADATA_KEYS.EVENTS, existingEvents, constructor);
|
|
132
|
+
return descriptor;
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
function On(event) {
|
|
136
|
+
return createEventDecorator(event, "on");
|
|
137
|
+
}
|
|
138
|
+
function Once(event) {
|
|
139
|
+
return createEventDecorator(event, "once");
|
|
140
|
+
}
|
|
141
|
+
function getEventsMetadata(target) {
|
|
142
|
+
return Reflect.getMetadata(METADATA_KEYS.EVENTS, target) || [];
|
|
143
|
+
}
|
|
144
|
+
|
|
119
145
|
// src/decorators/utils.ts
|
|
120
146
|
function buildSimpleCommandMetadata(options, methodName, category) {
|
|
121
147
|
return {
|
|
@@ -139,6 +165,7 @@ var _CommandRegistry = class _CommandRegistry {
|
|
|
139
165
|
constructor(extensions = [".js", ".mjs", ".cjs"]) {
|
|
140
166
|
this.commands = /* @__PURE__ */ new Map();
|
|
141
167
|
this.aliases = /* @__PURE__ */ new Map();
|
|
168
|
+
this.registeredEvents = [];
|
|
142
169
|
this.processedStoatClasses = /* @__PURE__ */ new Set();
|
|
143
170
|
this.extensions = extensions;
|
|
144
171
|
}
|
|
@@ -162,7 +189,7 @@ var _CommandRegistry = class _CommandRegistry {
|
|
|
162
189
|
await this.loadFile(file, directory);
|
|
163
190
|
}
|
|
164
191
|
}
|
|
165
|
-
console.log(`[
|
|
192
|
+
console.log(`[Stoatx] Loaded ${this.commands.size} command(s) and ${this.registeredEvents.length} event(s)`);
|
|
166
193
|
}
|
|
167
194
|
/**
|
|
168
195
|
* Auto-discover command files across one or more roots.
|
|
@@ -190,7 +217,7 @@ var _CommandRegistry = class _CommandRegistry {
|
|
|
190
217
|
}) ?? roots[0];
|
|
191
218
|
await this.loadFile(file, baseDir);
|
|
192
219
|
}
|
|
193
|
-
console.log(`[
|
|
220
|
+
console.log(`[Stoatx] Loaded ${this.commands.size} command(s) and ${this.registeredEvents.length} event(s)`);
|
|
194
221
|
}
|
|
195
222
|
getDefaultAutoDiscoveryPatterns() {
|
|
196
223
|
return this.extensions.map((ext) => `**/*${ext}`);
|
|
@@ -198,7 +225,7 @@ var _CommandRegistry = class _CommandRegistry {
|
|
|
198
225
|
async isLikelyCommandModule(filePath) {
|
|
199
226
|
try {
|
|
200
227
|
const source = await fs.readFile(filePath, "utf8");
|
|
201
|
-
return source.includes("Stoat") || source.includes("SimpleCommand") || source.includes("Command") || source.includes("
|
|
228
|
+
return source.includes("Stoat") || source.includes("SimpleCommand") || source.includes("Command") || source.includes("stoatx:command");
|
|
202
229
|
} catch {
|
|
203
230
|
return true;
|
|
204
231
|
}
|
|
@@ -209,7 +236,7 @@ var _CommandRegistry = class _CommandRegistry {
|
|
|
209
236
|
register(instance, metadata, classConstructor, methodName) {
|
|
210
237
|
const name = metadata.name.toLowerCase();
|
|
211
238
|
if (this.commands.has(name)) {
|
|
212
|
-
console.warn(`[
|
|
239
|
+
console.warn(`[Stoatx] Duplicate command name: ${name}. Skipping...`);
|
|
213
240
|
return;
|
|
214
241
|
}
|
|
215
242
|
this.validateGuards(classConstructor, metadata.name);
|
|
@@ -217,7 +244,7 @@ var _CommandRegistry = class _CommandRegistry {
|
|
|
217
244
|
for (const alias of metadata.aliases) {
|
|
218
245
|
const aliasLower = alias.toLowerCase();
|
|
219
246
|
if (this.aliases.has(aliasLower) || this.commands.has(aliasLower)) {
|
|
220
|
-
console.warn(`[
|
|
247
|
+
console.warn(`[Stoatx] Duplicate alias: ${aliasLower}. Skipping...`);
|
|
221
248
|
continue;
|
|
222
249
|
}
|
|
223
250
|
this.aliases.set(aliasLower, name);
|
|
@@ -250,6 +277,12 @@ var _CommandRegistry = class _CommandRegistry {
|
|
|
250
277
|
getAllMetadata() {
|
|
251
278
|
return this.getAll().map((c) => c.metadata);
|
|
252
279
|
}
|
|
280
|
+
/**
|
|
281
|
+
* Get all registered events
|
|
282
|
+
*/
|
|
283
|
+
getEvents() {
|
|
284
|
+
return this.registeredEvents;
|
|
285
|
+
}
|
|
253
286
|
/**
|
|
254
287
|
* Get commands grouped by category
|
|
255
288
|
*/
|
|
@@ -269,6 +302,7 @@ var _CommandRegistry = class _CommandRegistry {
|
|
|
269
302
|
clear() {
|
|
270
303
|
this.commands.clear();
|
|
271
304
|
this.aliases.clear();
|
|
305
|
+
this.registeredEvents.length = 0;
|
|
272
306
|
this.processedStoatClasses.clear();
|
|
273
307
|
}
|
|
274
308
|
/**
|
|
@@ -296,20 +330,20 @@ var _CommandRegistry = class _CommandRegistry {
|
|
|
296
330
|
* @private
|
|
297
331
|
*/
|
|
298
332
|
validateGuards(commandClass, commandName) {
|
|
299
|
-
const guards = Reflect.getMetadata("
|
|
333
|
+
const guards = Reflect.getMetadata("stoatx:command:guards", commandClass) || [];
|
|
300
334
|
for (const GuardClass of guards) {
|
|
301
335
|
const guardInstance = new GuardClass();
|
|
302
336
|
if (typeof guardInstance.run !== "function") {
|
|
303
337
|
console.error(
|
|
304
|
-
`[
|
|
338
|
+
`[Stoatx] FATAL: Guard "${GuardClass.name}" on command "${commandName}" does not have a run() method.`
|
|
305
339
|
);
|
|
306
340
|
process.exit(1);
|
|
307
341
|
}
|
|
308
342
|
if (typeof guardInstance.guardFail !== "function") {
|
|
309
343
|
console.error(
|
|
310
|
-
`[
|
|
344
|
+
`[Stoatx] FATAL: Guard "${GuardClass.name}" on command "${commandName}" does not have a guardFail() method.`
|
|
311
345
|
);
|
|
312
|
-
console.error(`[
|
|
346
|
+
console.error(`[Stoatx] All guards must implement guardFail() to handle failed checks.`);
|
|
313
347
|
process.exit(1);
|
|
314
348
|
}
|
|
315
349
|
}
|
|
@@ -330,15 +364,16 @@ var _CommandRegistry = class _CommandRegistry {
|
|
|
330
364
|
this.registerStoatClassCommands(stoatClass, stoatInstance, filePath, baseDir);
|
|
331
365
|
}
|
|
332
366
|
} catch (error) {
|
|
333
|
-
console.error(`[
|
|
367
|
+
console.error(`[Stoatx] Failed to load command file: ${filePath}`, error);
|
|
334
368
|
}
|
|
335
369
|
}
|
|
336
370
|
registerStoatClassCommands(stoatClass, instance, filePath, baseDir) {
|
|
337
371
|
const simpleCommands = getSimpleCommands(stoatClass);
|
|
372
|
+
const events = getEventsMetadata(stoatClass);
|
|
338
373
|
const category = this.getCategoryFromPath(filePath, baseDir);
|
|
339
|
-
if (simpleCommands.length === 0) {
|
|
374
|
+
if (simpleCommands.length === 0 && events.length === 0) {
|
|
340
375
|
console.warn(
|
|
341
|
-
`[
|
|
376
|
+
`[Stoatx] Class ${stoatClass.name} is decorated with @Stoat but has no @SimpleCommand, @On or @Once methods. Skipping...`
|
|
342
377
|
);
|
|
343
378
|
this.processedStoatClasses.add(stoatClass);
|
|
344
379
|
return;
|
|
@@ -346,12 +381,25 @@ var _CommandRegistry = class _CommandRegistry {
|
|
|
346
381
|
for (const cmdDef of simpleCommands) {
|
|
347
382
|
const method = instance[cmdDef.methodName];
|
|
348
383
|
if (typeof method !== "function") {
|
|
349
|
-
console.warn(`[
|
|
384
|
+
console.warn(`[Stoatx] Method ${cmdDef.methodName} not found on ${stoatClass.name}. Skipping...`);
|
|
350
385
|
continue;
|
|
351
386
|
}
|
|
352
387
|
const metadata = buildSimpleCommandMetadata(cmdDef.options, cmdDef.methodName, category);
|
|
353
388
|
this.register(instance, metadata, stoatClass, cmdDef.methodName);
|
|
354
389
|
}
|
|
390
|
+
for (const eventDef of events) {
|
|
391
|
+
const method = instance[eventDef.methodName];
|
|
392
|
+
if (typeof method !== "function") {
|
|
393
|
+
console.warn(`[Stoatx] Method ${eventDef.methodName} not found on ${stoatClass.name}. Skipping...`);
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
this.registeredEvents.push({
|
|
397
|
+
instance,
|
|
398
|
+
methodName: eventDef.methodName,
|
|
399
|
+
event: eventDef.event,
|
|
400
|
+
type: eventDef.type
|
|
401
|
+
});
|
|
402
|
+
}
|
|
355
403
|
this.processedStoatClasses.add(stoatClass);
|
|
356
404
|
}
|
|
357
405
|
/**
|
|
@@ -377,7 +425,23 @@ var CommandRegistry = _CommandRegistry;
|
|
|
377
425
|
|
|
378
426
|
// src/handler.ts
|
|
379
427
|
import "reflect-metadata";
|
|
380
|
-
|
|
428
|
+
import { Client as StoatClient } from "stoat.js";
|
|
429
|
+
var Client = class extends StoatClient {
|
|
430
|
+
constructor(options) {
|
|
431
|
+
super();
|
|
432
|
+
this.handler = new StoatxHandler({ ...options, client: this });
|
|
433
|
+
this.on("messageCreate", async (message) => {
|
|
434
|
+
await this.handler.handle(message);
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Initialize the StoatxHandler commands
|
|
439
|
+
*/
|
|
440
|
+
async initCommands() {
|
|
441
|
+
await this.handler.init();
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
var StoatxHandler = class {
|
|
381
445
|
constructor(options) {
|
|
382
446
|
this.cooldowns = /* @__PURE__ */ new Map();
|
|
383
447
|
this.client = options.client;
|
|
@@ -394,9 +458,33 @@ var MallyHandler = class {
|
|
|
394
458
|
async init() {
|
|
395
459
|
if (this.commandsDir) {
|
|
396
460
|
await this.registry.loadFromDirectory(this.commandsDir);
|
|
397
|
-
|
|
461
|
+
} else {
|
|
462
|
+
await this.registry.autoDiscover(this.discoveryOptions);
|
|
463
|
+
}
|
|
464
|
+
this.attachEvents();
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Attach registered events to the client
|
|
468
|
+
*/
|
|
469
|
+
attachEvents() {
|
|
470
|
+
const events = this.registry.getEvents();
|
|
471
|
+
for (const eventDef of events) {
|
|
472
|
+
const handler = async (...args) => {
|
|
473
|
+
try {
|
|
474
|
+
await eventDef.instance[eventDef.methodName](...args, this.client);
|
|
475
|
+
} catch (error) {
|
|
476
|
+
console.error(
|
|
477
|
+
`[Stoatx] Event Handler Error in @${eventDef.type === "on" ? "On" : "Once"}('${eventDef.event}'):`,
|
|
478
|
+
error
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
};
|
|
482
|
+
if (eventDef.type === "once") {
|
|
483
|
+
this.client.once(eventDef.event, handler);
|
|
484
|
+
} else {
|
|
485
|
+
this.client.on(eventDef.event, handler);
|
|
486
|
+
}
|
|
398
487
|
}
|
|
399
|
-
await this.registry.autoDiscover(this.discoveryOptions);
|
|
400
488
|
}
|
|
401
489
|
/**
|
|
402
490
|
* Parse a raw message into command context
|
|
@@ -463,7 +551,7 @@ var MallyHandler = class {
|
|
|
463
551
|
const channelId = message.channel.id;
|
|
464
552
|
const serverId = message.server?.id;
|
|
465
553
|
const reply = async (content) => {
|
|
466
|
-
await message.channel.sendMessage(content);
|
|
554
|
+
return await message.channel.sendMessage(content);
|
|
467
555
|
};
|
|
468
556
|
return this.handleMessage(rawContent, message, {
|
|
469
557
|
authorId,
|
|
@@ -508,7 +596,7 @@ var MallyHandler = class {
|
|
|
508
596
|
await ctx.reply("This command is owner-only.");
|
|
509
597
|
return false;
|
|
510
598
|
}
|
|
511
|
-
const guards = Reflect.getMetadata("
|
|
599
|
+
const guards = Reflect.getMetadata("stoatx:command:guards", classConstructor) || [];
|
|
512
600
|
for (const guardClass of guards) {
|
|
513
601
|
const guardInstance = new guardClass();
|
|
514
602
|
if (typeof guardInstance.run === "function") {
|
|
@@ -517,7 +605,7 @@ var MallyHandler = class {
|
|
|
517
605
|
if (typeof guardInstance.guardFail === "function") {
|
|
518
606
|
await guardInstance.guardFail(ctx);
|
|
519
607
|
} else {
|
|
520
|
-
console.error("[
|
|
608
|
+
console.error("[Stoatx] Guard check failed but no guardFail method defined on", guardClass.name);
|
|
521
609
|
}
|
|
522
610
|
return false;
|
|
523
611
|
}
|
|
@@ -542,7 +630,7 @@ var MallyHandler = class {
|
|
|
542
630
|
if (typeof instance.onError === "function") {
|
|
543
631
|
await instance.onError(ctx, error);
|
|
544
632
|
} else {
|
|
545
|
-
console.error(`[
|
|
633
|
+
console.error(`[Stoatx] Error in command ${metadata.name}:`, error);
|
|
546
634
|
await ctx.reply(`An error occurred: ${error.message}`);
|
|
547
635
|
}
|
|
548
636
|
return false;
|
|
@@ -638,13 +726,16 @@ var MallyHandler = class {
|
|
|
638
726
|
}
|
|
639
727
|
};
|
|
640
728
|
export {
|
|
729
|
+
Client,
|
|
641
730
|
CommandRegistry,
|
|
642
731
|
Guard,
|
|
643
732
|
METADATA_KEYS,
|
|
644
|
-
|
|
733
|
+
On,
|
|
734
|
+
Once,
|
|
645
735
|
SimpleCommand,
|
|
646
736
|
Stoat,
|
|
647
737
|
buildSimpleCommandMetadata,
|
|
738
|
+
getEventsMetadata,
|
|
648
739
|
getGuards,
|
|
649
740
|
getSimpleCommands,
|
|
650
741
|
isStoatClass
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stoatx",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "A high-performance, decorator-based command handler for the Stoat ecosystem.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -23,16 +23,10 @@
|
|
|
23
23
|
"files": [
|
|
24
24
|
"dist"
|
|
25
25
|
],
|
|
26
|
-
"license": "
|
|
26
|
+
"license": "MIT",
|
|
27
27
|
"publishConfig": {
|
|
28
28
|
"access": "public"
|
|
29
29
|
},
|
|
30
|
-
"scripts": {
|
|
31
|
-
"build": "tsup src/index.ts --format cjs,esm --dts",
|
|
32
|
-
"dev": "tsup src/index.ts --format cjs,esm --watch --dts",
|
|
33
|
-
"lint": "eslint src/**/*.ts",
|
|
34
|
-
"format": "prettier --write src/**/*.ts"
|
|
35
|
-
},
|
|
36
30
|
"dependencies": {
|
|
37
31
|
"reflect-metadata": "^0.2.2",
|
|
38
32
|
"stoat.js": "^7.3.6",
|
|
@@ -46,5 +40,10 @@
|
|
|
46
40
|
},
|
|
47
41
|
"peerDependencies": {
|
|
48
42
|
"reflect-metadata": "^0.2.0"
|
|
43
|
+
},
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "tsup src/index.ts --format cjs,esm --dts --tsconfig tsconfig.build.json",
|
|
46
|
+
"dev": "tsup src/index.ts --format cjs,esm --watch --dts --tsconfig tsconfig.build.json",
|
|
47
|
+
"lint": "eslint src/**/*.ts"
|
|
49
48
|
}
|
|
50
|
-
}
|
|
49
|
+
}
|