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.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Client, Message } from 'stoat.js';
|
|
1
|
+
import { Client as Client$1, Message } from 'stoat.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Permission types for commands
|
|
@@ -44,7 +44,7 @@ interface CommandMetadata {
|
|
|
44
44
|
*/
|
|
45
45
|
interface CommandContext {
|
|
46
46
|
/** The client instance */
|
|
47
|
-
client: Client;
|
|
47
|
+
client: Client$1;
|
|
48
48
|
/** The raw message content */
|
|
49
49
|
content: string;
|
|
50
50
|
/** The author ID */
|
|
@@ -60,7 +60,7 @@ interface CommandContext {
|
|
|
60
60
|
/** The command name used (could be an alias) */
|
|
61
61
|
commandName: string;
|
|
62
62
|
/** Reply to the message */
|
|
63
|
-
reply: (content: string) => Promise<
|
|
63
|
+
reply: (content: string) => Promise<Message>;
|
|
64
64
|
/** The original message object (platform-specific) */
|
|
65
65
|
message: Message;
|
|
66
66
|
}
|
|
@@ -73,14 +73,14 @@ interface StoatLifecycle {
|
|
|
73
73
|
/** Optional: Called when a cooldown is active */
|
|
74
74
|
onCooldown?(ctx: CommandContext, remaining: number): Promise<void>;
|
|
75
75
|
}
|
|
76
|
-
interface
|
|
76
|
+
interface StoatxGuard {
|
|
77
77
|
run(ctx: CommandContext): Promise<boolean> | boolean;
|
|
78
78
|
guardFail?(ctx: CommandContext): Promise<void> | void;
|
|
79
79
|
}
|
|
80
80
|
/**
|
|
81
81
|
* Discovery options for automatic command module loading
|
|
82
82
|
*/
|
|
83
|
-
interface
|
|
83
|
+
interface StoatxDiscoveryOptions {
|
|
84
84
|
/** Root directories to scan (default: [process.cwd()]) */
|
|
85
85
|
roots?: string[];
|
|
86
86
|
/** Glob patterns relative to each root */
|
|
@@ -91,13 +91,13 @@ interface MallyDiscoveryOptions {
|
|
|
91
91
|
/**
|
|
92
92
|
* Handler options
|
|
93
93
|
*/
|
|
94
|
-
interface
|
|
94
|
+
interface StoatxHandlerOptions {
|
|
95
95
|
/** The client instance */
|
|
96
|
-
client: Client;
|
|
96
|
+
client: Client$1;
|
|
97
97
|
/** Directory to scan for command modules (absolute path) */
|
|
98
98
|
commandsDir?: string;
|
|
99
99
|
/** Auto-discovery options used when commandsDir is not provided */
|
|
100
|
-
discovery?:
|
|
100
|
+
discovery?: StoatxDiscoveryOptions;
|
|
101
101
|
/** Command prefix or prefix resolver function */
|
|
102
102
|
prefix: string | ((ctx: {
|
|
103
103
|
serverId?: string;
|
|
@@ -183,7 +183,7 @@ declare function getSimpleCommands(target: Function): SimpleCommandDefinition[];
|
|
|
183
183
|
* import { Guard, Stoat, SimpleCommand, CommandContext } from 'stoatx';
|
|
184
184
|
*
|
|
185
185
|
* // Define a guard
|
|
186
|
-
* class NotBot implements
|
|
186
|
+
* class NotBot implements StoatxGuard {
|
|
187
187
|
* run(ctx: CommandContext): boolean {
|
|
188
188
|
* return !ctx.message.author.bot;
|
|
189
189
|
* }
|
|
@@ -209,6 +209,60 @@ declare function Guard(guardClass: Function): ClassDecorator;
|
|
|
209
209
|
*/
|
|
210
210
|
declare function getGuards(target: Function): Function[];
|
|
211
211
|
|
|
212
|
+
interface EventDefinition {
|
|
213
|
+
methodName: string;
|
|
214
|
+
event: string;
|
|
215
|
+
type: "on" | "once";
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* @On
|
|
219
|
+
* Triggered on every occurrence of the event.
|
|
220
|
+
* Marks a method to be executed whenever the specified client event is emitted.
|
|
221
|
+
*
|
|
222
|
+
* @example
|
|
223
|
+
* ```ts
|
|
224
|
+
* import { Stoat, On } from 'stoatx';
|
|
225
|
+
* import { Message, Client } from 'stoat.js';
|
|
226
|
+
*
|
|
227
|
+
* @Stoat()
|
|
228
|
+
* class BotEvents {
|
|
229
|
+
* @On('messageCreate')
|
|
230
|
+
* async onMessage(message: Message, client: Client) {
|
|
231
|
+
* console.log('New message received:', message.content);
|
|
232
|
+
* }
|
|
233
|
+
* }
|
|
234
|
+
* ```
|
|
235
|
+
*
|
|
236
|
+
* @param event The name of the client event to listen to
|
|
237
|
+
*/
|
|
238
|
+
declare function On(event: string): MethodDecorator;
|
|
239
|
+
/**
|
|
240
|
+
* @Once
|
|
241
|
+
* Triggered only fully once.
|
|
242
|
+
* Marks a method to be executed only the FIRST time the specified client event is emitted.
|
|
243
|
+
*
|
|
244
|
+
* @example
|
|
245
|
+
* ```ts
|
|
246
|
+
* import { Stoat, Once } from 'stoatx';
|
|
247
|
+
* import { Client } from 'stoat.js';
|
|
248
|
+
*
|
|
249
|
+
* @Stoat()
|
|
250
|
+
* class BotEvents {
|
|
251
|
+
* @Once('ready')
|
|
252
|
+
* async onReady(client: Client) {
|
|
253
|
+
* console.log('Bot successfully started and logged in!');
|
|
254
|
+
* }
|
|
255
|
+
* }
|
|
256
|
+
* ```
|
|
257
|
+
*
|
|
258
|
+
* @param event The name of the client event to listen to
|
|
259
|
+
*/
|
|
260
|
+
declare function Once(event: string): MethodDecorator;
|
|
261
|
+
/**
|
|
262
|
+
* Get all event definitions from a @Stoat class
|
|
263
|
+
*/
|
|
264
|
+
declare function getEventsMetadata(target: Function): EventDefinition[];
|
|
265
|
+
|
|
212
266
|
/**
|
|
213
267
|
* Build CommandMetadata from SimpleCommandOptions
|
|
214
268
|
*/
|
|
@@ -220,7 +274,8 @@ declare function buildSimpleCommandMetadata(options: SimpleCommandOptions, metho
|
|
|
220
274
|
declare const METADATA_KEYS: {
|
|
221
275
|
readonly IS_STOAT_CLASS: symbol;
|
|
222
276
|
readonly SIMPLE_COMMANDS: symbol;
|
|
223
|
-
readonly GUARDS: "
|
|
277
|
+
readonly GUARDS: "stoatx:command:guards";
|
|
278
|
+
readonly EVENTS: symbol;
|
|
224
279
|
};
|
|
225
280
|
|
|
226
281
|
interface AutoDiscoveryOptions {
|
|
@@ -241,6 +296,15 @@ interface RegisteredCommand {
|
|
|
241
296
|
/** The original class constructor (for guard validation) */
|
|
242
297
|
classConstructor: Function;
|
|
243
298
|
}
|
|
299
|
+
/**
|
|
300
|
+
* Stored event entry from @On/@Once registration.
|
|
301
|
+
*/
|
|
302
|
+
interface RegisteredEvent {
|
|
303
|
+
instance: object;
|
|
304
|
+
methodName: string;
|
|
305
|
+
event: string;
|
|
306
|
+
type: "on" | "once";
|
|
307
|
+
}
|
|
244
308
|
/**
|
|
245
309
|
* CommandRegistry - Scans directories and stores commands in a Map
|
|
246
310
|
*
|
|
@@ -257,6 +321,7 @@ declare class CommandRegistry {
|
|
|
257
321
|
private static readonly DEFAULT_AUTO_DISCOVERY_IGNORES;
|
|
258
322
|
private readonly commands;
|
|
259
323
|
private readonly aliases;
|
|
324
|
+
private readonly registeredEvents;
|
|
260
325
|
private readonly extensions;
|
|
261
326
|
private readonly processedStoatClasses;
|
|
262
327
|
constructor(extensions?: string[]);
|
|
@@ -294,6 +359,10 @@ declare class CommandRegistry {
|
|
|
294
359
|
* Get all command metadata
|
|
295
360
|
*/
|
|
296
361
|
getAllMetadata(): CommandMetadata[];
|
|
362
|
+
/**
|
|
363
|
+
* Get all registered events
|
|
364
|
+
*/
|
|
365
|
+
getEvents(): RegisteredEvent[];
|
|
297
366
|
/**
|
|
298
367
|
* Get commands grouped by category
|
|
299
368
|
*/
|
|
@@ -333,44 +402,53 @@ declare class CommandRegistry {
|
|
|
333
402
|
}
|
|
334
403
|
|
|
335
404
|
/**
|
|
336
|
-
*
|
|
337
|
-
*
|
|
338
|
-
* Handles message parsing, middleware execution, and command dispatching
|
|
405
|
+
* Client - An extended Client that integrates StoatxHandler directly
|
|
339
406
|
*
|
|
340
407
|
* @example
|
|
341
408
|
* ```ts
|
|
342
|
-
* import {
|
|
343
|
-
* import { Client } from 'stoat.js';
|
|
344
|
-
*
|
|
345
|
-
* const client = new Client();
|
|
409
|
+
* import { Client } from 'stoatx';
|
|
346
410
|
*
|
|
347
|
-
* const
|
|
348
|
-
* client,
|
|
411
|
+
* const client = new Client({
|
|
349
412
|
* prefix: '!',
|
|
350
413
|
* owners: ['owner-user-id'],
|
|
351
414
|
* });
|
|
352
415
|
*
|
|
353
|
-
* await
|
|
354
|
-
*
|
|
355
|
-
* client.on('message', (message) => {
|
|
356
|
-
* handler.handleMessage(message);
|
|
357
|
-
* });
|
|
416
|
+
* await client.initCommands();
|
|
358
417
|
* ```
|
|
359
418
|
*/
|
|
360
|
-
declare class
|
|
361
|
-
|
|
362
|
-
|
|
419
|
+
declare class Client extends Client$1 {
|
|
420
|
+
readonly handler: StoatxHandler;
|
|
421
|
+
constructor(options: Omit<StoatxHandlerOptions, "client">);
|
|
422
|
+
/**
|
|
423
|
+
* Initialize the StoatxHandler commands
|
|
424
|
+
*/
|
|
425
|
+
initCommands(): Promise<void>;
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* StoatxHandler - The execution engine for commands
|
|
429
|
+
*
|
|
430
|
+
* Handles message parsing, middleware execution, and command dispatching
|
|
431
|
+
*
|
|
432
|
+
* @internal This class is not intended to be instantiated directly. Use the `Client` from `stoatx` instead.
|
|
433
|
+
*/
|
|
434
|
+
declare class StoatxHandler {
|
|
435
|
+
private readonly commandsDir;
|
|
436
|
+
private readonly discoveryOptions;
|
|
363
437
|
private readonly prefixResolver;
|
|
364
438
|
private readonly owners;
|
|
365
439
|
private readonly registry;
|
|
366
440
|
private readonly cooldowns;
|
|
367
441
|
private readonly disableMentionPrefix;
|
|
368
442
|
private readonly client;
|
|
369
|
-
constructor(options:
|
|
443
|
+
constructor(options: StoatxHandlerOptions);
|
|
370
444
|
/**
|
|
371
445
|
* Initialize the handler - load all commands
|
|
372
446
|
*/
|
|
373
447
|
init(): Promise<void>;
|
|
448
|
+
/**
|
|
449
|
+
* Attach registered events to the client
|
|
450
|
+
*/
|
|
451
|
+
private attachEvents;
|
|
374
452
|
/**
|
|
375
453
|
* Parse a raw message into command context
|
|
376
454
|
*/
|
|
@@ -378,7 +456,7 @@ declare class MallyHandler {
|
|
|
378
456
|
authorId: string;
|
|
379
457
|
channelId: string;
|
|
380
458
|
serverId?: string;
|
|
381
|
-
reply: (content: string) => Promise<
|
|
459
|
+
reply: (content: string) => Promise<Message>;
|
|
382
460
|
}): Promise<CommandContext | null>;
|
|
383
461
|
/**
|
|
384
462
|
* Handle a message object using the configured message adapter
|
|
@@ -412,7 +490,7 @@ declare class MallyHandler {
|
|
|
412
490
|
authorId: string;
|
|
413
491
|
channelId: string;
|
|
414
492
|
serverId?: string;
|
|
415
|
-
reply: (content: string) => Promise<
|
|
493
|
+
reply: (content: string) => Promise<Message>;
|
|
416
494
|
}): Promise<boolean>;
|
|
417
495
|
/**
|
|
418
496
|
* Execute a command with the given context
|
|
@@ -464,4 +542,4 @@ declare class MallyHandler {
|
|
|
464
542
|
private setCooldown;
|
|
465
543
|
}
|
|
466
544
|
|
|
467
|
-
export { type CommandContext, type CommandMetadata, CommandRegistry, type
|
|
545
|
+
export { Client, type CommandContext, type CommandMetadata, CommandRegistry, type EventDefinition, Guard, METADATA_KEYS, On, Once, type Permission, type RegisteredCommand, type RegisteredEvent, SimpleCommand, type SimpleCommandDefinition, type SimpleCommandOptions, Stoat, type StoatLifecycle, type StoatxDiscoveryOptions, type StoatxGuard, StoatxHandler, type StoatxHandlerOptions, buildSimpleCommandMetadata, getEventsMetadata, getGuards, getSimpleCommands, isStoatClass };
|
package/dist/index.js
CHANGED
|
@@ -30,13 +30,16 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
Client: () => Client,
|
|
33
34
|
CommandRegistry: () => CommandRegistry,
|
|
34
35
|
Guard: () => Guard,
|
|
35
36
|
METADATA_KEYS: () => METADATA_KEYS,
|
|
36
|
-
|
|
37
|
+
On: () => On,
|
|
38
|
+
Once: () => Once,
|
|
37
39
|
SimpleCommand: () => SimpleCommand,
|
|
38
40
|
Stoat: () => Stoat,
|
|
39
41
|
buildSimpleCommandMetadata: () => buildSimpleCommandMetadata,
|
|
42
|
+
getEventsMetadata: () => getEventsMetadata,
|
|
40
43
|
getGuards: () => getGuards,
|
|
41
44
|
getSimpleCommands: () => getSimpleCommands,
|
|
42
45
|
isStoatClass: () => isStoatClass
|
|
@@ -48,9 +51,10 @@ var import_reflect_metadata = require("reflect-metadata");
|
|
|
48
51
|
|
|
49
52
|
// src/decorators/keys.ts
|
|
50
53
|
var METADATA_KEYS = {
|
|
51
|
-
IS_STOAT_CLASS: /* @__PURE__ */ Symbol("
|
|
52
|
-
SIMPLE_COMMANDS: /* @__PURE__ */ Symbol("
|
|
53
|
-
GUARDS: "
|
|
54
|
+
IS_STOAT_CLASS: /* @__PURE__ */ Symbol("stoatx:stoat:isClass"),
|
|
55
|
+
SIMPLE_COMMANDS: /* @__PURE__ */ Symbol("stoatx:stoat:simpleCommands"),
|
|
56
|
+
GUARDS: "stoatx:command:guards",
|
|
57
|
+
EVENTS: /* @__PURE__ */ Symbol("stoatx:stoat:events")
|
|
54
58
|
};
|
|
55
59
|
|
|
56
60
|
// src/decorators/store.ts
|
|
@@ -161,6 +165,31 @@ function getGuards(target) {
|
|
|
161
165
|
return Reflect.getMetadata(METADATA_KEYS.GUARDS, target) || [];
|
|
162
166
|
}
|
|
163
167
|
|
|
168
|
+
// src/decorators/Events.ts
|
|
169
|
+
var import_reflect_metadata4 = require("reflect-metadata");
|
|
170
|
+
function createEventDecorator(event, type) {
|
|
171
|
+
return (target, propertyKey, descriptor) => {
|
|
172
|
+
const constructor = target.constructor;
|
|
173
|
+
const existingEvents = Reflect.getMetadata(METADATA_KEYS.EVENTS, constructor) || [];
|
|
174
|
+
existingEvents.push({
|
|
175
|
+
methodName: String(propertyKey),
|
|
176
|
+
event,
|
|
177
|
+
type
|
|
178
|
+
});
|
|
179
|
+
Reflect.defineMetadata(METADATA_KEYS.EVENTS, existingEvents, constructor);
|
|
180
|
+
return descriptor;
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
function On(event) {
|
|
184
|
+
return createEventDecorator(event, "on");
|
|
185
|
+
}
|
|
186
|
+
function Once(event) {
|
|
187
|
+
return createEventDecorator(event, "once");
|
|
188
|
+
}
|
|
189
|
+
function getEventsMetadata(target) {
|
|
190
|
+
return Reflect.getMetadata(METADATA_KEYS.EVENTS, target) || [];
|
|
191
|
+
}
|
|
192
|
+
|
|
164
193
|
// src/decorators/utils.ts
|
|
165
194
|
function buildSimpleCommandMetadata(options, methodName, category) {
|
|
166
195
|
return {
|
|
@@ -184,6 +213,7 @@ var _CommandRegistry = class _CommandRegistry {
|
|
|
184
213
|
constructor(extensions = [".js", ".mjs", ".cjs"]) {
|
|
185
214
|
this.commands = /* @__PURE__ */ new Map();
|
|
186
215
|
this.aliases = /* @__PURE__ */ new Map();
|
|
216
|
+
this.registeredEvents = [];
|
|
187
217
|
this.processedStoatClasses = /* @__PURE__ */ new Set();
|
|
188
218
|
this.extensions = extensions;
|
|
189
219
|
}
|
|
@@ -207,7 +237,7 @@ var _CommandRegistry = class _CommandRegistry {
|
|
|
207
237
|
await this.loadFile(file, directory);
|
|
208
238
|
}
|
|
209
239
|
}
|
|
210
|
-
console.log(`[
|
|
240
|
+
console.log(`[Stoatx] Loaded ${this.commands.size} command(s) and ${this.registeredEvents.length} event(s)`);
|
|
211
241
|
}
|
|
212
242
|
/**
|
|
213
243
|
* Auto-discover command files across one or more roots.
|
|
@@ -235,7 +265,7 @@ var _CommandRegistry = class _CommandRegistry {
|
|
|
235
265
|
}) ?? roots[0];
|
|
236
266
|
await this.loadFile(file, baseDir);
|
|
237
267
|
}
|
|
238
|
-
console.log(`[
|
|
268
|
+
console.log(`[Stoatx] Loaded ${this.commands.size} command(s) and ${this.registeredEvents.length} event(s)`);
|
|
239
269
|
}
|
|
240
270
|
getDefaultAutoDiscoveryPatterns() {
|
|
241
271
|
return this.extensions.map((ext) => `**/*${ext}`);
|
|
@@ -243,7 +273,7 @@ var _CommandRegistry = class _CommandRegistry {
|
|
|
243
273
|
async isLikelyCommandModule(filePath) {
|
|
244
274
|
try {
|
|
245
275
|
const source = await fs.readFile(filePath, "utf8");
|
|
246
|
-
return source.includes("Stoat") || source.includes("SimpleCommand") || source.includes("Command") || source.includes("
|
|
276
|
+
return source.includes("Stoat") || source.includes("SimpleCommand") || source.includes("Command") || source.includes("stoatx:command");
|
|
247
277
|
} catch {
|
|
248
278
|
return true;
|
|
249
279
|
}
|
|
@@ -254,7 +284,7 @@ var _CommandRegistry = class _CommandRegistry {
|
|
|
254
284
|
register(instance, metadata, classConstructor, methodName) {
|
|
255
285
|
const name = metadata.name.toLowerCase();
|
|
256
286
|
if (this.commands.has(name)) {
|
|
257
|
-
console.warn(`[
|
|
287
|
+
console.warn(`[Stoatx] Duplicate command name: ${name}. Skipping...`);
|
|
258
288
|
return;
|
|
259
289
|
}
|
|
260
290
|
this.validateGuards(classConstructor, metadata.name);
|
|
@@ -262,7 +292,7 @@ var _CommandRegistry = class _CommandRegistry {
|
|
|
262
292
|
for (const alias of metadata.aliases) {
|
|
263
293
|
const aliasLower = alias.toLowerCase();
|
|
264
294
|
if (this.aliases.has(aliasLower) || this.commands.has(aliasLower)) {
|
|
265
|
-
console.warn(`[
|
|
295
|
+
console.warn(`[Stoatx] Duplicate alias: ${aliasLower}. Skipping...`);
|
|
266
296
|
continue;
|
|
267
297
|
}
|
|
268
298
|
this.aliases.set(aliasLower, name);
|
|
@@ -295,6 +325,12 @@ var _CommandRegistry = class _CommandRegistry {
|
|
|
295
325
|
getAllMetadata() {
|
|
296
326
|
return this.getAll().map((c) => c.metadata);
|
|
297
327
|
}
|
|
328
|
+
/**
|
|
329
|
+
* Get all registered events
|
|
330
|
+
*/
|
|
331
|
+
getEvents() {
|
|
332
|
+
return this.registeredEvents;
|
|
333
|
+
}
|
|
298
334
|
/**
|
|
299
335
|
* Get commands grouped by category
|
|
300
336
|
*/
|
|
@@ -314,6 +350,7 @@ var _CommandRegistry = class _CommandRegistry {
|
|
|
314
350
|
clear() {
|
|
315
351
|
this.commands.clear();
|
|
316
352
|
this.aliases.clear();
|
|
353
|
+
this.registeredEvents.length = 0;
|
|
317
354
|
this.processedStoatClasses.clear();
|
|
318
355
|
}
|
|
319
356
|
/**
|
|
@@ -341,20 +378,20 @@ var _CommandRegistry = class _CommandRegistry {
|
|
|
341
378
|
* @private
|
|
342
379
|
*/
|
|
343
380
|
validateGuards(commandClass, commandName) {
|
|
344
|
-
const guards = Reflect.getMetadata("
|
|
381
|
+
const guards = Reflect.getMetadata("stoatx:command:guards", commandClass) || [];
|
|
345
382
|
for (const GuardClass of guards) {
|
|
346
383
|
const guardInstance = new GuardClass();
|
|
347
384
|
if (typeof guardInstance.run !== "function") {
|
|
348
385
|
console.error(
|
|
349
|
-
`[
|
|
386
|
+
`[Stoatx] FATAL: Guard "${GuardClass.name}" on command "${commandName}" does not have a run() method.`
|
|
350
387
|
);
|
|
351
388
|
process.exit(1);
|
|
352
389
|
}
|
|
353
390
|
if (typeof guardInstance.guardFail !== "function") {
|
|
354
391
|
console.error(
|
|
355
|
-
`[
|
|
392
|
+
`[Stoatx] FATAL: Guard "${GuardClass.name}" on command "${commandName}" does not have a guardFail() method.`
|
|
356
393
|
);
|
|
357
|
-
console.error(`[
|
|
394
|
+
console.error(`[Stoatx] All guards must implement guardFail() to handle failed checks.`);
|
|
358
395
|
process.exit(1);
|
|
359
396
|
}
|
|
360
397
|
}
|
|
@@ -375,15 +412,16 @@ var _CommandRegistry = class _CommandRegistry {
|
|
|
375
412
|
this.registerStoatClassCommands(stoatClass, stoatInstance, filePath, baseDir);
|
|
376
413
|
}
|
|
377
414
|
} catch (error) {
|
|
378
|
-
console.error(`[
|
|
415
|
+
console.error(`[Stoatx] Failed to load command file: ${filePath}`, error);
|
|
379
416
|
}
|
|
380
417
|
}
|
|
381
418
|
registerStoatClassCommands(stoatClass, instance, filePath, baseDir) {
|
|
382
419
|
const simpleCommands = getSimpleCommands(stoatClass);
|
|
420
|
+
const events = getEventsMetadata(stoatClass);
|
|
383
421
|
const category = this.getCategoryFromPath(filePath, baseDir);
|
|
384
|
-
if (simpleCommands.length === 0) {
|
|
422
|
+
if (simpleCommands.length === 0 && events.length === 0) {
|
|
385
423
|
console.warn(
|
|
386
|
-
`[
|
|
424
|
+
`[Stoatx] Class ${stoatClass.name} is decorated with @Stoat but has no @SimpleCommand, @On or @Once methods. Skipping...`
|
|
387
425
|
);
|
|
388
426
|
this.processedStoatClasses.add(stoatClass);
|
|
389
427
|
return;
|
|
@@ -391,12 +429,25 @@ var _CommandRegistry = class _CommandRegistry {
|
|
|
391
429
|
for (const cmdDef of simpleCommands) {
|
|
392
430
|
const method = instance[cmdDef.methodName];
|
|
393
431
|
if (typeof method !== "function") {
|
|
394
|
-
console.warn(`[
|
|
432
|
+
console.warn(`[Stoatx] Method ${cmdDef.methodName} not found on ${stoatClass.name}. Skipping...`);
|
|
395
433
|
continue;
|
|
396
434
|
}
|
|
397
435
|
const metadata = buildSimpleCommandMetadata(cmdDef.options, cmdDef.methodName, category);
|
|
398
436
|
this.register(instance, metadata, stoatClass, cmdDef.methodName);
|
|
399
437
|
}
|
|
438
|
+
for (const eventDef of events) {
|
|
439
|
+
const method = instance[eventDef.methodName];
|
|
440
|
+
if (typeof method !== "function") {
|
|
441
|
+
console.warn(`[Stoatx] Method ${eventDef.methodName} not found on ${stoatClass.name}. Skipping...`);
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
444
|
+
this.registeredEvents.push({
|
|
445
|
+
instance,
|
|
446
|
+
methodName: eventDef.methodName,
|
|
447
|
+
event: eventDef.event,
|
|
448
|
+
type: eventDef.type
|
|
449
|
+
});
|
|
450
|
+
}
|
|
400
451
|
this.processedStoatClasses.add(stoatClass);
|
|
401
452
|
}
|
|
402
453
|
/**
|
|
@@ -421,8 +472,24 @@ _CommandRegistry.DEFAULT_AUTO_DISCOVERY_IGNORES = [
|
|
|
421
472
|
var CommandRegistry = _CommandRegistry;
|
|
422
473
|
|
|
423
474
|
// src/handler.ts
|
|
424
|
-
var
|
|
425
|
-
var
|
|
475
|
+
var import_reflect_metadata5 = require("reflect-metadata");
|
|
476
|
+
var import_stoat = require("stoat.js");
|
|
477
|
+
var Client = class extends import_stoat.Client {
|
|
478
|
+
constructor(options) {
|
|
479
|
+
super();
|
|
480
|
+
this.handler = new StoatxHandler({ ...options, client: this });
|
|
481
|
+
this.on("messageCreate", async (message) => {
|
|
482
|
+
await this.handler.handle(message);
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Initialize the StoatxHandler commands
|
|
487
|
+
*/
|
|
488
|
+
async initCommands() {
|
|
489
|
+
await this.handler.init();
|
|
490
|
+
}
|
|
491
|
+
};
|
|
492
|
+
var StoatxHandler = class {
|
|
426
493
|
constructor(options) {
|
|
427
494
|
this.cooldowns = /* @__PURE__ */ new Map();
|
|
428
495
|
this.client = options.client;
|
|
@@ -439,9 +506,33 @@ var MallyHandler = class {
|
|
|
439
506
|
async init() {
|
|
440
507
|
if (this.commandsDir) {
|
|
441
508
|
await this.registry.loadFromDirectory(this.commandsDir);
|
|
442
|
-
|
|
509
|
+
} else {
|
|
510
|
+
await this.registry.autoDiscover(this.discoveryOptions);
|
|
511
|
+
}
|
|
512
|
+
this.attachEvents();
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* Attach registered events to the client
|
|
516
|
+
*/
|
|
517
|
+
attachEvents() {
|
|
518
|
+
const events = this.registry.getEvents();
|
|
519
|
+
for (const eventDef of events) {
|
|
520
|
+
const handler = async (...args) => {
|
|
521
|
+
try {
|
|
522
|
+
await eventDef.instance[eventDef.methodName](...args, this.client);
|
|
523
|
+
} catch (error) {
|
|
524
|
+
console.error(
|
|
525
|
+
`[Stoatx] Event Handler Error in @${eventDef.type === "on" ? "On" : "Once"}('${eventDef.event}'):`,
|
|
526
|
+
error
|
|
527
|
+
);
|
|
528
|
+
}
|
|
529
|
+
};
|
|
530
|
+
if (eventDef.type === "once") {
|
|
531
|
+
this.client.once(eventDef.event, handler);
|
|
532
|
+
} else {
|
|
533
|
+
this.client.on(eventDef.event, handler);
|
|
534
|
+
}
|
|
443
535
|
}
|
|
444
|
-
await this.registry.autoDiscover(this.discoveryOptions);
|
|
445
536
|
}
|
|
446
537
|
/**
|
|
447
538
|
* Parse a raw message into command context
|
|
@@ -508,7 +599,7 @@ var MallyHandler = class {
|
|
|
508
599
|
const channelId = message.channel.id;
|
|
509
600
|
const serverId = message.server?.id;
|
|
510
601
|
const reply = async (content) => {
|
|
511
|
-
await message.channel.sendMessage(content);
|
|
602
|
+
return await message.channel.sendMessage(content);
|
|
512
603
|
};
|
|
513
604
|
return this.handleMessage(rawContent, message, {
|
|
514
605
|
authorId,
|
|
@@ -553,7 +644,7 @@ var MallyHandler = class {
|
|
|
553
644
|
await ctx.reply("This command is owner-only.");
|
|
554
645
|
return false;
|
|
555
646
|
}
|
|
556
|
-
const guards = Reflect.getMetadata("
|
|
647
|
+
const guards = Reflect.getMetadata("stoatx:command:guards", classConstructor) || [];
|
|
557
648
|
for (const guardClass of guards) {
|
|
558
649
|
const guardInstance = new guardClass();
|
|
559
650
|
if (typeof guardInstance.run === "function") {
|
|
@@ -562,7 +653,7 @@ var MallyHandler = class {
|
|
|
562
653
|
if (typeof guardInstance.guardFail === "function") {
|
|
563
654
|
await guardInstance.guardFail(ctx);
|
|
564
655
|
} else {
|
|
565
|
-
console.error("[
|
|
656
|
+
console.error("[Stoatx] Guard check failed but no guardFail method defined on", guardClass.name);
|
|
566
657
|
}
|
|
567
658
|
return false;
|
|
568
659
|
}
|
|
@@ -587,7 +678,7 @@ var MallyHandler = class {
|
|
|
587
678
|
if (typeof instance.onError === "function") {
|
|
588
679
|
await instance.onError(ctx, error);
|
|
589
680
|
} else {
|
|
590
|
-
console.error(`[
|
|
681
|
+
console.error(`[Stoatx] Error in command ${metadata.name}:`, error);
|
|
591
682
|
await ctx.reply(`An error occurred: ${error.message}`);
|
|
592
683
|
}
|
|
593
684
|
return false;
|
|
@@ -684,13 +775,16 @@ var MallyHandler = class {
|
|
|
684
775
|
};
|
|
685
776
|
// Annotate the CommonJS export names for ESM import in node:
|
|
686
777
|
0 && (module.exports = {
|
|
778
|
+
Client,
|
|
687
779
|
CommandRegistry,
|
|
688
780
|
Guard,
|
|
689
781
|
METADATA_KEYS,
|
|
690
|
-
|
|
782
|
+
On,
|
|
783
|
+
Once,
|
|
691
784
|
SimpleCommand,
|
|
692
785
|
Stoat,
|
|
693
786
|
buildSimpleCommandMetadata,
|
|
787
|
+
getEventsMetadata,
|
|
694
788
|
getGuards,
|
|
695
789
|
getSimpleCommands,
|
|
696
790
|
isStoatClass
|