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/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("mally:stoat:isClass"),
7
- SIMPLE_COMMANDS: /* @__PURE__ */ Symbol("mally:stoat:simpleCommands"),
8
- GUARDS: "mally:command: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(`[Mally] Loaded ${this.commands.size} command(s)`);
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(`[Mally] Auto-discovered ${candidateFiles} candidate file(s), loaded ${this.commands.size} command(s)`);
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("mally:command");
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(`[Mally] Duplicate command name: ${name}. Skipping...`);
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(`[Mally] Duplicate alias: ${aliasLower}. Skipping...`);
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("mally:command:guards", commandClass) || [];
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
- `[Mally] FATAL: Guard "${GuardClass.name}" on command "${commandName}" does not have a run() method.`
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
- `[Mally] FATAL: Guard "${GuardClass.name}" on command "${commandName}" does not have a guardFail() method.`
344
+ `[Stoatx] FATAL: Guard "${GuardClass.name}" on command "${commandName}" does not have a guardFail() method.`
311
345
  );
312
- console.error(`[Mally] All guards must implement guardFail() to handle failed checks.`);
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(`[Mally] Failed to load command file: ${filePath}`, 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
- `[Mally] Class ${stoatClass.name} is decorated with @Stoat but has no @SimpleCommand methods. Skipping...`
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(`[Mally] Method ${cmdDef.methodName} not found on ${stoatClass.name}. Skipping...`);
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
- var MallyHandler = class {
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
- return;
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("mally:command:guards", classConstructor) || [];
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("[Mally] Guard check failed but no guardFail method defined on", guardClass.name);
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(`[Mally] Error in command ${metadata.name}:`, 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
- MallyHandler,
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.2.1",
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": "AGPL-3.0-or-later",
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
+ }