stoatx 0.5.4 → 0.6.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 +72 -1
- package/dist/index.d.mts +30 -16
- package/dist/index.d.ts +30 -16
- package/dist/index.js +44 -37
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +43 -37
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -94,14 +94,85 @@ Marks a method as a command.
|
|
|
94
94
|
aliases: ['b'], // Alternative names
|
|
95
95
|
permissions: ['BanMembers'], // This is currently not implemented, but will be in the future
|
|
96
96
|
cooldown: 5000, // 5 seconds
|
|
97
|
+
cooldownStorage: "database", // A custom flag you can use in your CustomCooldownManager
|
|
97
98
|
ownerOnly: false,
|
|
98
99
|
nsfw: false,
|
|
99
100
|
})
|
|
100
101
|
async ban(ctx: Context) {
|
|
101
|
-
//
|
|
102
|
+
// logic
|
|
102
103
|
}
|
|
103
104
|
```
|
|
104
105
|
|
|
106
|
+
### StoatLifecycle Interface
|
|
107
|
+
|
|
108
|
+
Provides intellisense for lifecycle hooks like `onError` and `onCooldown` on your `@Stoat()` classes.
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
import { Stoat, SimpleCommand, CommandContext, StoatLifecycle } from "stoatx";
|
|
112
|
+
|
|
113
|
+
@Stoat()
|
|
114
|
+
export class MyCommands implements StoatLifecycle {
|
|
115
|
+
@SimpleCommand({ cooldown: 5000 })
|
|
116
|
+
async ping(ctx: CommandContext) {
|
|
117
|
+
await ctx.reply(`Pong! 🏓`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Called when the command is placed on cooldown
|
|
121
|
+
async onCooldown(ctx: CommandContext, remaining: number) {
|
|
122
|
+
await ctx.reply(`You are in cooldown, wait ${(remaining / 1000).toFixed(1)} seconds!`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Called when an error occurs during execution
|
|
126
|
+
async onError(ctx: CommandContext, error: Error) {
|
|
127
|
+
console.error(error);
|
|
128
|
+
await ctx.reply("An error occurred");
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Custom Cooldowns
|
|
134
|
+
|
|
135
|
+
By default, cooldowns are stored in memory. For persistent or distributed setups, you can implement the `CooldownManager` interface to store cooldowns in a database such as Redis, PostgreSQL or MongoDB.
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
import { Client, CooldownManager, CommandContext, CommandMetadata, DefaultCooldownManager } from "stoatx";
|
|
139
|
+
|
|
140
|
+
class MixedCooldownManager implements CooldownManager {
|
|
141
|
+
private memory = new DefaultCooldownManager();
|
|
142
|
+
|
|
143
|
+
async check(ctx: CommandContext, metadata: CommandMetadata): Promise<boolean> {
|
|
144
|
+
if (metadata.cooldownStorage === "database") {
|
|
145
|
+
// Return true if action is allowed in DB
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
return this.memory.check(ctx, metadata);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async getRemaining(ctx: CommandContext, metadata: CommandMetadata): Promise<number> {
|
|
152
|
+
if (metadata.cooldownStorage === "database") {
|
|
153
|
+
// Query remaining from DB
|
|
154
|
+
return 0;
|
|
155
|
+
}
|
|
156
|
+
return this.memory.getRemaining(ctx, metadata);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async set(ctx: CommandContext, metadata: CommandMetadata): Promise<void> {
|
|
160
|
+
if (metadata.cooldownStorage === "database") {
|
|
161
|
+
// Store cooldown expiration in your database
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
this.memory.set(ctx, metadata);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const client = new Client({
|
|
169
|
+
prefix: "!",
|
|
170
|
+
cooldownManager: new MixedCooldownManager(),
|
|
171
|
+
});
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Guards
|
|
175
|
+
|
|
105
176
|
### @Guard(GuardClass)
|
|
106
177
|
|
|
107
178
|
Adds a guard check before command execution.
|
package/dist/index.d.mts
CHANGED
|
@@ -22,6 +22,8 @@ interface SimpleCommandOptions {
|
|
|
22
22
|
category?: string;
|
|
23
23
|
/** Cooldown in milliseconds */
|
|
24
24
|
cooldown?: number;
|
|
25
|
+
/** Storage strategy or identifier for cooldowns (e.g. "memory", "database") */
|
|
26
|
+
cooldownStorage?: string;
|
|
25
27
|
/** Whether the command is NSFW only */
|
|
26
28
|
nsfw?: boolean;
|
|
27
29
|
/** Whether the command is owner only */
|
|
@@ -37,6 +39,7 @@ interface CommandMetadata {
|
|
|
37
39
|
permissions: Permission[];
|
|
38
40
|
category: string;
|
|
39
41
|
cooldown: number;
|
|
42
|
+
cooldownStorage?: string;
|
|
40
43
|
nsfw: boolean;
|
|
41
44
|
ownerOnly: boolean;
|
|
42
45
|
}
|
|
@@ -70,9 +73,20 @@ interface CommandContext {
|
|
|
70
73
|
*/
|
|
71
74
|
interface StoatLifecycle {
|
|
72
75
|
/** Optional: Called when an error occurs during command execution */
|
|
73
|
-
onError?(ctx: CommandContext, error: Error): Promise<void
|
|
76
|
+
onError?(ctx: CommandContext, error: Error): Promise<void> | void;
|
|
74
77
|
/** Optional: Called when a cooldown is active */
|
|
75
|
-
onCooldown?(ctx: CommandContext, remaining: number): Promise<void
|
|
78
|
+
onCooldown?(ctx: CommandContext, remaining: number): Promise<void> | void;
|
|
79
|
+
/** Allows the class to contain other methods (such as your commands) */
|
|
80
|
+
[method: string]: any;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Cooldown manager interface for custom cooldown storage (e.g., database)
|
|
84
|
+
*/
|
|
85
|
+
interface CooldownManager {
|
|
86
|
+
check(ctx: CommandContext, metadata: CommandMetadata): boolean | Promise<boolean>;
|
|
87
|
+
getRemaining(ctx: CommandContext, metadata: CommandMetadata): number | Promise<number>;
|
|
88
|
+
set(ctx: CommandContext, metadata: CommandMetadata): void | Promise<void>;
|
|
89
|
+
clear?(): void | Promise<void>;
|
|
76
90
|
}
|
|
77
91
|
interface StoatxGuard {
|
|
78
92
|
run(ctx: CommandContext): Promise<boolean> | boolean;
|
|
@@ -109,6 +123,8 @@ interface StoatxHandlerOptions {
|
|
|
109
123
|
extensions?: string[];
|
|
110
124
|
/** Disable mention prefix support (default: false) */
|
|
111
125
|
disableMentionPrefix?: boolean;
|
|
126
|
+
/** Custom cooldown manager */
|
|
127
|
+
cooldownManager?: CooldownManager;
|
|
112
128
|
}
|
|
113
129
|
|
|
114
130
|
/**
|
|
@@ -402,6 +418,16 @@ declare class CommandRegistry {
|
|
|
402
418
|
private getCategoryFromPath;
|
|
403
419
|
}
|
|
404
420
|
|
|
421
|
+
/**
|
|
422
|
+
* Default in-memory cooldown manager
|
|
423
|
+
*/
|
|
424
|
+
declare class DefaultCooldownManager implements CooldownManager {
|
|
425
|
+
private readonly cooldowns;
|
|
426
|
+
check(ctx: CommandContext, metadata: CommandMetadata): boolean;
|
|
427
|
+
getRemaining(ctx: CommandContext, metadata: CommandMetadata): number;
|
|
428
|
+
set(ctx: CommandContext, metadata: CommandMetadata): void;
|
|
429
|
+
clear(): void;
|
|
430
|
+
}
|
|
405
431
|
/**
|
|
406
432
|
* Client - An extended Client that integrates StoatxHandler directly
|
|
407
433
|
*
|
|
@@ -438,7 +464,7 @@ declare class StoatxHandler {
|
|
|
438
464
|
private readonly prefixResolver;
|
|
439
465
|
private readonly owners;
|
|
440
466
|
private readonly registry;
|
|
441
|
-
private readonly
|
|
467
|
+
private readonly cooldownManager;
|
|
442
468
|
private readonly disableMentionPrefix;
|
|
443
469
|
private readonly client;
|
|
444
470
|
constructor(options: StoatxHandlerOptions);
|
|
@@ -529,18 +555,6 @@ declare class StoatxHandler {
|
|
|
529
555
|
* Resolve the prefix for a context
|
|
530
556
|
*/
|
|
531
557
|
private resolvePrefix;
|
|
532
|
-
/**
|
|
533
|
-
* Check if user is on cooldown
|
|
534
|
-
*/
|
|
535
|
-
private checkCooldown;
|
|
536
|
-
/**
|
|
537
|
-
* Get remaining cooldown time in ms
|
|
538
|
-
*/
|
|
539
|
-
private getRemainingCooldown;
|
|
540
|
-
/**
|
|
541
|
-
* Set cooldown for a user
|
|
542
|
-
*/
|
|
543
|
-
private setCooldown;
|
|
544
558
|
}
|
|
545
559
|
|
|
546
|
-
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 };
|
|
560
|
+
export { Client, type CommandContext, type CommandMetadata, CommandRegistry, type CooldownManager, DefaultCooldownManager, 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.d.ts
CHANGED
|
@@ -22,6 +22,8 @@ interface SimpleCommandOptions {
|
|
|
22
22
|
category?: string;
|
|
23
23
|
/** Cooldown in milliseconds */
|
|
24
24
|
cooldown?: number;
|
|
25
|
+
/** Storage strategy or identifier for cooldowns (e.g. "memory", "database") */
|
|
26
|
+
cooldownStorage?: string;
|
|
25
27
|
/** Whether the command is NSFW only */
|
|
26
28
|
nsfw?: boolean;
|
|
27
29
|
/** Whether the command is owner only */
|
|
@@ -37,6 +39,7 @@ interface CommandMetadata {
|
|
|
37
39
|
permissions: Permission[];
|
|
38
40
|
category: string;
|
|
39
41
|
cooldown: number;
|
|
42
|
+
cooldownStorage?: string;
|
|
40
43
|
nsfw: boolean;
|
|
41
44
|
ownerOnly: boolean;
|
|
42
45
|
}
|
|
@@ -70,9 +73,20 @@ interface CommandContext {
|
|
|
70
73
|
*/
|
|
71
74
|
interface StoatLifecycle {
|
|
72
75
|
/** Optional: Called when an error occurs during command execution */
|
|
73
|
-
onError?(ctx: CommandContext, error: Error): Promise<void
|
|
76
|
+
onError?(ctx: CommandContext, error: Error): Promise<void> | void;
|
|
74
77
|
/** Optional: Called when a cooldown is active */
|
|
75
|
-
onCooldown?(ctx: CommandContext, remaining: number): Promise<void
|
|
78
|
+
onCooldown?(ctx: CommandContext, remaining: number): Promise<void> | void;
|
|
79
|
+
/** Allows the class to contain other methods (such as your commands) */
|
|
80
|
+
[method: string]: any;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Cooldown manager interface for custom cooldown storage (e.g., database)
|
|
84
|
+
*/
|
|
85
|
+
interface CooldownManager {
|
|
86
|
+
check(ctx: CommandContext, metadata: CommandMetadata): boolean | Promise<boolean>;
|
|
87
|
+
getRemaining(ctx: CommandContext, metadata: CommandMetadata): number | Promise<number>;
|
|
88
|
+
set(ctx: CommandContext, metadata: CommandMetadata): void | Promise<void>;
|
|
89
|
+
clear?(): void | Promise<void>;
|
|
76
90
|
}
|
|
77
91
|
interface StoatxGuard {
|
|
78
92
|
run(ctx: CommandContext): Promise<boolean> | boolean;
|
|
@@ -109,6 +123,8 @@ interface StoatxHandlerOptions {
|
|
|
109
123
|
extensions?: string[];
|
|
110
124
|
/** Disable mention prefix support (default: false) */
|
|
111
125
|
disableMentionPrefix?: boolean;
|
|
126
|
+
/** Custom cooldown manager */
|
|
127
|
+
cooldownManager?: CooldownManager;
|
|
112
128
|
}
|
|
113
129
|
|
|
114
130
|
/**
|
|
@@ -402,6 +418,16 @@ declare class CommandRegistry {
|
|
|
402
418
|
private getCategoryFromPath;
|
|
403
419
|
}
|
|
404
420
|
|
|
421
|
+
/**
|
|
422
|
+
* Default in-memory cooldown manager
|
|
423
|
+
*/
|
|
424
|
+
declare class DefaultCooldownManager implements CooldownManager {
|
|
425
|
+
private readonly cooldowns;
|
|
426
|
+
check(ctx: CommandContext, metadata: CommandMetadata): boolean;
|
|
427
|
+
getRemaining(ctx: CommandContext, metadata: CommandMetadata): number;
|
|
428
|
+
set(ctx: CommandContext, metadata: CommandMetadata): void;
|
|
429
|
+
clear(): void;
|
|
430
|
+
}
|
|
405
431
|
/**
|
|
406
432
|
* Client - An extended Client that integrates StoatxHandler directly
|
|
407
433
|
*
|
|
@@ -438,7 +464,7 @@ declare class StoatxHandler {
|
|
|
438
464
|
private readonly prefixResolver;
|
|
439
465
|
private readonly owners;
|
|
440
466
|
private readonly registry;
|
|
441
|
-
private readonly
|
|
467
|
+
private readonly cooldownManager;
|
|
442
468
|
private readonly disableMentionPrefix;
|
|
443
469
|
private readonly client;
|
|
444
470
|
constructor(options: StoatxHandlerOptions);
|
|
@@ -529,18 +555,6 @@ declare class StoatxHandler {
|
|
|
529
555
|
* Resolve the prefix for a context
|
|
530
556
|
*/
|
|
531
557
|
private resolvePrefix;
|
|
532
|
-
/**
|
|
533
|
-
* Check if user is on cooldown
|
|
534
|
-
*/
|
|
535
|
-
private checkCooldown;
|
|
536
|
-
/**
|
|
537
|
-
* Get remaining cooldown time in ms
|
|
538
|
-
*/
|
|
539
|
-
private getRemainingCooldown;
|
|
540
|
-
/**
|
|
541
|
-
* Set cooldown for a user
|
|
542
|
-
*/
|
|
543
|
-
private setCooldown;
|
|
544
558
|
}
|
|
545
559
|
|
|
546
|
-
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 };
|
|
560
|
+
export { Client, type CommandContext, type CommandMetadata, CommandRegistry, type CooldownManager, DefaultCooldownManager, 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
|
@@ -33,6 +33,7 @@ var index_exports = {};
|
|
|
33
33
|
__export(index_exports, {
|
|
34
34
|
Client: () => Client,
|
|
35
35
|
CommandRegistry: () => CommandRegistry,
|
|
36
|
+
DefaultCooldownManager: () => DefaultCooldownManager,
|
|
36
37
|
Guard: () => Guard,
|
|
37
38
|
METADATA_KEYS: () => METADATA_KEYS,
|
|
38
39
|
On: () => On,
|
|
@@ -201,6 +202,7 @@ function buildSimpleCommandMetadata(options, methodName, category) {
|
|
|
201
202
|
permissions: options.permissions ?? [],
|
|
202
203
|
category: options.category ?? category ?? "uncategorized",
|
|
203
204
|
cooldown: options.cooldown ?? 0,
|
|
205
|
+
...options.cooldownStorage !== void 0 ? { cooldownStorage: options.cooldownStorage } : {},
|
|
204
206
|
nsfw: options.nsfw ?? false,
|
|
205
207
|
ownerOnly: options.ownerOnly ?? false
|
|
206
208
|
};
|
|
@@ -476,6 +478,38 @@ var CommandRegistry = class _CommandRegistry {
|
|
|
476
478
|
// src/handler.ts
|
|
477
479
|
var import_reflect_metadata5 = require("reflect-metadata");
|
|
478
480
|
var import_client = require("@stoatx/client");
|
|
481
|
+
var DefaultCooldownManager = class {
|
|
482
|
+
cooldowns = /* @__PURE__ */ new Map();
|
|
483
|
+
check(ctx, metadata) {
|
|
484
|
+
if (metadata.cooldown <= 0) return true;
|
|
485
|
+
const commandCooldowns = this.cooldowns.get(metadata.name);
|
|
486
|
+
if (!commandCooldowns) return true;
|
|
487
|
+
const expirationTime = commandCooldowns.get(ctx.authorId);
|
|
488
|
+
if (!expirationTime) return true;
|
|
489
|
+
if (Date.now() > expirationTime) {
|
|
490
|
+
commandCooldowns.delete(ctx.authorId);
|
|
491
|
+
return true;
|
|
492
|
+
}
|
|
493
|
+
return false;
|
|
494
|
+
}
|
|
495
|
+
getRemaining(ctx, metadata) {
|
|
496
|
+
const commandCooldowns = this.cooldowns.get(metadata.name);
|
|
497
|
+
if (!commandCooldowns) return 0;
|
|
498
|
+
const userCooldown = commandCooldowns.get(ctx.authorId);
|
|
499
|
+
if (!userCooldown) return 0;
|
|
500
|
+
return Math.max(0, userCooldown - Date.now());
|
|
501
|
+
}
|
|
502
|
+
set(ctx, metadata) {
|
|
503
|
+
if (!this.cooldowns.has(metadata.name)) {
|
|
504
|
+
this.cooldowns.set(metadata.name, /* @__PURE__ */ new Map());
|
|
505
|
+
}
|
|
506
|
+
const commandCooldowns = this.cooldowns.get(metadata.name);
|
|
507
|
+
commandCooldowns.set(ctx.authorId, Date.now() + metadata.cooldown);
|
|
508
|
+
}
|
|
509
|
+
clear() {
|
|
510
|
+
this.cooldowns.clear();
|
|
511
|
+
}
|
|
512
|
+
};
|
|
479
513
|
var Client = class extends import_client.Client {
|
|
480
514
|
handler;
|
|
481
515
|
constructor(options) {
|
|
@@ -498,7 +532,7 @@ var StoatxHandler = class {
|
|
|
498
532
|
prefixResolver;
|
|
499
533
|
owners;
|
|
500
534
|
registry;
|
|
501
|
-
|
|
535
|
+
cooldownManager;
|
|
502
536
|
disableMentionPrefix;
|
|
503
537
|
client;
|
|
504
538
|
constructor(options) {
|
|
@@ -509,6 +543,7 @@ var StoatxHandler = class {
|
|
|
509
543
|
this.owners = new Set(options.owners ?? []);
|
|
510
544
|
this.registry = new CommandRegistry(options.extensions);
|
|
511
545
|
this.disableMentionPrefix = options.disableMentionPrefix ?? false;
|
|
546
|
+
this.cooldownManager = options.cooldownManager ?? new DefaultCooldownManager();
|
|
512
547
|
}
|
|
513
548
|
/**
|
|
514
549
|
* Initialize the handler - load all commands
|
|
@@ -671,8 +706,8 @@ var StoatxHandler = class {
|
|
|
671
706
|
}
|
|
672
707
|
}
|
|
673
708
|
}
|
|
674
|
-
if (!this.
|
|
675
|
-
const remaining = this.
|
|
709
|
+
if (!await this.cooldownManager.check(ctx, metadata)) {
|
|
710
|
+
const remaining = await this.cooldownManager.getRemaining(ctx, metadata);
|
|
676
711
|
if (typeof instance.onCooldown === "function") {
|
|
677
712
|
await instance.onCooldown(ctx, remaining);
|
|
678
713
|
} else {
|
|
@@ -681,10 +716,10 @@ var StoatxHandler = class {
|
|
|
681
716
|
return false;
|
|
682
717
|
}
|
|
683
718
|
try {
|
|
684
|
-
await instance[methodName](ctx);
|
|
685
719
|
if (metadata.cooldown > 0) {
|
|
686
|
-
this.
|
|
720
|
+
await this.cooldownManager.set(ctx, metadata);
|
|
687
721
|
}
|
|
722
|
+
await instance[methodName](ctx);
|
|
688
723
|
return true;
|
|
689
724
|
} catch (error) {
|
|
690
725
|
if (typeof instance.onError === "function") {
|
|
@@ -718,7 +753,9 @@ var StoatxHandler = class {
|
|
|
718
753
|
*/
|
|
719
754
|
async reload() {
|
|
720
755
|
this.registry.clear();
|
|
721
|
-
this.
|
|
756
|
+
if (this.cooldownManager.clear) {
|
|
757
|
+
await this.cooldownManager.clear();
|
|
758
|
+
}
|
|
722
759
|
if (this.commandsDir) {
|
|
723
760
|
await this.registry.loadFromDirectory(this.commandsDir);
|
|
724
761
|
return;
|
|
@@ -752,37 +789,6 @@ var StoatxHandler = class {
|
|
|
752
789
|
}
|
|
753
790
|
return this.prefixResolver;
|
|
754
791
|
}
|
|
755
|
-
/**
|
|
756
|
-
* Check if user is on cooldown
|
|
757
|
-
*/
|
|
758
|
-
checkCooldown(userId, metadata) {
|
|
759
|
-
if (metadata.cooldown <= 0) return true;
|
|
760
|
-
const commandCooldowns = this.cooldowns.get(metadata.name);
|
|
761
|
-
if (!commandCooldowns) return true;
|
|
762
|
-
const userCooldown = commandCooldowns.get(userId);
|
|
763
|
-
if (!userCooldown) return true;
|
|
764
|
-
return Date.now() >= userCooldown;
|
|
765
|
-
}
|
|
766
|
-
/**
|
|
767
|
-
* Get remaining cooldown time in ms
|
|
768
|
-
*/
|
|
769
|
-
getRemainingCooldown(userId, metadata) {
|
|
770
|
-
const commandCooldowns = this.cooldowns.get(metadata.name);
|
|
771
|
-
if (!commandCooldowns) return 0;
|
|
772
|
-
const userCooldown = commandCooldowns.get(userId);
|
|
773
|
-
if (!userCooldown) return 0;
|
|
774
|
-
return Math.max(0, userCooldown - Date.now());
|
|
775
|
-
}
|
|
776
|
-
/**
|
|
777
|
-
* Set cooldown for a user
|
|
778
|
-
*/
|
|
779
|
-
setCooldown(userId, metadata) {
|
|
780
|
-
if (!this.cooldowns.has(metadata.name)) {
|
|
781
|
-
this.cooldowns.set(metadata.name, /* @__PURE__ */ new Map());
|
|
782
|
-
}
|
|
783
|
-
const commandCooldowns = this.cooldowns.get(metadata.name);
|
|
784
|
-
commandCooldowns.set(userId, Date.now() + metadata.cooldown);
|
|
785
|
-
}
|
|
786
792
|
};
|
|
787
793
|
|
|
788
794
|
// src/index.ts
|
|
@@ -791,6 +797,7 @@ __reExport(index_exports, require("@stoatx/client"), module.exports);
|
|
|
791
797
|
0 && (module.exports = {
|
|
792
798
|
Client,
|
|
793
799
|
CommandRegistry,
|
|
800
|
+
DefaultCooldownManager,
|
|
794
801
|
Guard,
|
|
795
802
|
METADATA_KEYS,
|
|
796
803
|
On,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/decorators/Stoat.ts","../src/decorators/keys.ts","../src/decorators/store.ts","../src/decorators/SimpleCommand.ts","../src/decorators/Guard.ts","../src/decorators/Events.ts","../src/decorators/utils.ts","../src/registry.ts","../src/handler.ts"],"sourcesContent":["// Types\nexport * from \"./types\";\n\n// Decorators\nexport * from \"./decorators\";\n\n// Registry\nexport * from \"./registry\";\n\n// Handler\nexport { Client } from \"./handler\";\nexport type { StoatxHandler } from \"./handler\";\nexport * from \"@stoatx/client\";\n","import \"reflect-metadata\";\nimport { METADATA_KEYS } from \"./keys\";\nimport { decoratorStore } from \"./store\";\n\n/**\n * @Stoat\n * Marks a class as a Stoat command container.\n * Use this decorator on classes that contain @SimpleCommand methods.\n *\n * @example\n * ```ts\n * import { Stoat, SimpleCommand, CommandContext } from 'stoatx';\n *\n * @Stoat()\n * class ModerationCommands {\n * @SimpleCommand({ name: 'ban', description: 'Ban a user' })\n * async ban(ctx: CommandContext) {\n * await ctx.reply('User banned!');\n * }\n *\n * @SimpleCommand({ name: 'kick', description: 'Kick a user' })\n * async kick(ctx: CommandContext) {\n * await ctx.reply('User kicked!');\n * }\n * }\n * ```\n */\nexport function Stoat(): ClassDecorator {\n return (target: Function) => {\n Reflect.defineMetadata(METADATA_KEYS.IS_STOAT_CLASS, true, target);\n decoratorStore.registerStoatClass(target);\n };\n}\n\n/**\n * Check if a class is decorated with @Stoat\n */\nexport function isStoatClass(target: Function): boolean {\n return Reflect.getMetadata(METADATA_KEYS.IS_STOAT_CLASS, target) === true;\n}\n","/**\n * Metadata keys used by decorators\n */\nexport const METADATA_KEYS = {\n IS_STOAT_CLASS: Symbol(\"stoatx:stoat:isClass\"),\n SIMPLE_COMMANDS: Symbol(\"stoatx:stoat:simpleCommands\"),\n GUARDS: \"stoatx:command:guards\",\n EVENTS: Symbol(\"stoatx:stoat:events\"),\n} as const;\n","import type { RegisteredCommand } from \"../registry\";\n\n/**\n * Global store for all decorated classes and commands\n * This allows automatic registration without directory scanning\n */\nexport class DecoratorStore {\n private static instance: DecoratorStore;\n\n /** Stoat classes with their SimpleCommand methods */\n private stoatClasses: Map<Function, object> = new Map();\n\n /** Registered commands from @Stoat/@SimpleCommand decorators */\n private commands: RegisteredCommand[] = [];\n\n /** Whether the store has been initialized */\n private initialized = false;\n\n private constructor() {}\n\n static getInstance(): DecoratorStore {\n if (!DecoratorStore.instance) {\n DecoratorStore.instance = new DecoratorStore();\n }\n return DecoratorStore.instance;\n }\n\n /**\n * Register a @Stoat decorated class\n */\n registerStoatClass(classConstructor: Function): void {\n if (!this.stoatClasses.has(classConstructor)) {\n // Create instance immediately when decorated\n const instance = new (classConstructor as new () => object)();\n this.stoatClasses.set(classConstructor, instance);\n }\n }\n\n /**\n * Get all registered Stoat classes with their instances\n */\n getStoatClasses(): Map<Function, object> {\n return this.stoatClasses;\n }\n\n /**\n * Add a registered command\n */\n addCommand(command: RegisteredCommand): void {\n this.commands.push(command);\n }\n\n /**\n * Get all registered commands\n */\n getCommands(): RegisteredCommand[] {\n return this.commands;\n }\n\n /**\n * Clear all registered classes (useful for testing)\n */\n clear(): void {\n this.stoatClasses.clear();\n this.commands = [];\n this.initialized = false;\n }\n\n /**\n * Mark as initialized\n */\n markInitialized(): void {\n this.initialized = true;\n }\n\n /**\n * Check if initialized\n */\n isInitialized(): boolean {\n return this.initialized;\n }\n}\n\nexport const decoratorStore = DecoratorStore.getInstance();\n","import \"reflect-metadata\";\nimport type { SimpleCommandOptions } from \"../types\";\nimport { METADATA_KEYS } from \"./keys\";\n\n/**\n * Stored simple command metadata from method decorator\n */\nexport interface SimpleCommandDefinition {\n methodName: string;\n options: SimpleCommandOptions;\n}\n\n/**\n * @SimpleCommand\n * Marks a method as a simple command within a @Stoat() decorated class.\n *\n * @example\n * ```ts\n * @Stoat()\n * class Example {\n * @SimpleCommand({ name: 'ping', description: 'Replies with Pong!' })\n * async ping(ctx: CommandContext) {\n * await ctx.reply('Pong!');\n * }\n *\n * @SimpleCommand({ aliases: ['perm'], name: 'permission' })\n * async permission(ctx: CommandContext) {\n * await ctx.reply('Access granted');\n * }\n * }\n * ```\n */\nexport function SimpleCommand(options: SimpleCommandOptions = {}): MethodDecorator {\n return (target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {\n const constructor = target.constructor;\n\n // Get existing simple commands or create new array\n const existingCommands: SimpleCommandDefinition[] =\n Reflect.getMetadata(METADATA_KEYS.SIMPLE_COMMANDS, constructor) || [];\n\n // Add this command definition\n existingCommands.push({\n methodName: String(propertyKey),\n options,\n });\n\n Reflect.defineMetadata(METADATA_KEYS.SIMPLE_COMMANDS, existingCommands, constructor);\n\n return descriptor;\n };\n}\n\n/**\n * Get all simple command definitions from a @Stoat class\n */\nexport function getSimpleCommands(target: Function): SimpleCommandDefinition[] {\n return Reflect.getMetadata(METADATA_KEYS.SIMPLE_COMMANDS, target) || [];\n}\n","import \"reflect-metadata\";\nimport { METADATA_KEYS } from \"./keys\";\n\n/**\n * @Guard\n * Runs before a command to check if it should execute.\n * Should return true to allow execution, false to block.\n * Applied on @Stoat classes to guard all contained @SimpleCommand methods.\n *\n * @example\n * ```ts\n * import { Guard, Stoat, SimpleCommand, CommandContext } from 'stoatx';\n *\n * // Define a guard\n * class NotBot implements StoatxGuard {\n * run(ctx: CommandContext): boolean {\n * return !ctx.message.author.bot;\n * }\n *\n * guardFail(ctx: CommandContext): void {\n * ctx.reply(\"Bots cannot use this command!\");\n * }\n * }\n *\n * @Stoat()\n * @Guard(NotBot)\n * class AdminCommands {\n * @SimpleCommand({ name: 'admin', description: 'Admin only command' })\n * async admin(ctx: CommandContext) {\n * ctx.reply(\"You passed the guard check!\");\n * }\n * }\n * ```\n */\nexport function Guard(guardClass: Function): ClassDecorator {\n return (target: Function) => {\n const existingGuards: Function[] = Reflect.getMetadata(METADATA_KEYS.GUARDS, target) || [];\n existingGuards.push(guardClass);\n Reflect.defineMetadata(METADATA_KEYS.GUARDS, existingGuards, target);\n };\n}\n\n/**\n * Get all guards from a decorated class\n */\nexport function getGuards(target: Function): Function[] {\n return Reflect.getMetadata(METADATA_KEYS.GUARDS, target) || [];\n}\n","import \"reflect-metadata\";\nimport { METADATA_KEYS } from \"./keys\";\n\nexport interface EventDefinition {\n methodName: string;\n event: string;\n type: \"on\" | \"once\";\n}\n\nfunction createEventDecorator(event: string, type: \"on\" | \"once\"): MethodDecorator {\n return (target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {\n const constructor = target.constructor;\n\n // Retrieve existing events or initialize a fresh array\n const existingEvents: EventDefinition[] = Reflect.getMetadata(METADATA_KEYS.EVENTS, constructor) || [];\n\n existingEvents.push({\n methodName: String(propertyKey),\n event,\n type,\n });\n\n Reflect.defineMetadata(METADATA_KEYS.EVENTS, existingEvents, constructor);\n\n return descriptor;\n };\n}\n\n/**\n * @On\n * Triggered on every occurrence of the event.\n * Marks a method to be executed whenever the specified client event is emitted.\n *\n * @example\n * ```ts\n * import { Stoat, On } from 'stoatx';\n * import { Message, Client } from 'stoat.js';\n *\n * @Stoat()\n * class BotEvents {\n * @On('messageCreate')\n * async onMessage(message: Message, client: Client) {\n * console.log('New message received:', message.content);\n * }\n * }\n * ```\n *\n * @param event The name of the client event to listen to\n */\nexport function On(event: string): MethodDecorator {\n return createEventDecorator(event, \"on\");\n}\n\n/**\n * @Once\n * Triggered only fully once.\n * Marks a method to be executed only the FIRST time the specified client event is emitted.\n *\n * @example\n * ```ts\n * import { Stoat, Once } from 'stoatx';\n * import { Client } from 'stoat.js';\n *\n * @Stoat()\n * class BotEvents {\n * @Once('ready')\n * async onReady(client: Client) {\n * console.log('Bot successfully started and logged in!');\n * }\n * }\n * ```\n *\n * @param event The name of the client event to listen to\n */\nexport function Once(event: string): MethodDecorator {\n return createEventDecorator(event, \"once\");\n}\n\n/**\n * Get all event definitions from a @Stoat class\n */\nexport function getEventsMetadata(target: Function): EventDefinition[] {\n return Reflect.getMetadata(METADATA_KEYS.EVENTS, target) || [];\n}\n","import type { CommandMetadata, SimpleCommandOptions } from \"../types\";\n\n/**\n * Build CommandMetadata from SimpleCommandOptions\n */\nexport function buildSimpleCommandMetadata(\n options: SimpleCommandOptions,\n methodName: string,\n category?: string,\n): CommandMetadata {\n return {\n name: options.name ?? methodName.toLowerCase(),\n description: options.description ?? \"No description provided\",\n aliases: options.aliases ?? [],\n permissions: options.permissions ?? [],\n category: options.category ?? category ?? \"uncategorized\",\n cooldown: options.cooldown ?? 0,\n nsfw: options.nsfw ?? false,\n ownerOnly: options.ownerOnly ?? false,\n };\n}\n","import * as path from \"node:path\";\nimport * as fs from \"node:fs/promises\";\nimport { pathToFileURL } from \"node:url\";\nimport { glob } from \"tinyglobby\";\nimport { buildSimpleCommandMetadata, getSimpleCommands, getEventsMetadata } from \"./decorators\";\nimport { decoratorStore } from \"./decorators/store\";\nimport type { CommandMetadata } from \"./types\";\n\ninterface AutoDiscoveryOptions {\n roots?: string[];\n include?: string[];\n ignore?: string[];\n}\n\n/**\n * Stored command entry from @Stoat/@SimpleCommand registration.\n */\nexport interface RegisteredCommand {\n /** Instance of the @Stoat class */\n instance: object;\n /** Command metadata */\n metadata: CommandMetadata;\n /** Method name to call */\n methodName: string;\n /** The original class constructor (for guard validation) */\n classConstructor: Function;\n}\n\n/**\n * Stored event entry from @On/@Once registration.\n */\nexport interface RegisteredEvent {\n instance: object;\n methodName: string;\n event: string;\n type: \"on\" | \"once\";\n}\n\n/**\n * CommandRegistry - Scans directories and stores commands in a Map\n *\n * @example\n * ```ts\n * const registry = new CommandRegistry();\n * await registry.loadFromDirectory('./src/commands');\n *\n * const ping = registry.get('ping');\n * const allCommands = registry.getAll();\n * ```\n */\nexport class CommandRegistry {\n private static readonly DEFAULT_AUTO_DISCOVERY_IGNORES = [\n \"**/node_modules/**\",\n \"**/.git/**\",\n \"**/*.d.ts\",\n \"**/*.test.*\",\n \"**/*.spec.*\",\n ];\n\n private readonly commands: Map<string, RegisteredCommand> = new Map();\n private readonly aliases: Map<string, string> = new Map();\n private readonly registeredEvents: RegisteredEvent[] = [];\n private readonly extensions: string[];\n private readonly processedStoatClasses: Set<Function> = new Set();\n\n constructor(extensions: string[] = [\".js\", \".mjs\", \".cjs\"]) {\n this.extensions = extensions;\n }\n\n /**\n * Get the number of registered commands\n */\n get size(): number {\n return this.commands.size;\n }\n\n /**\n * Load commands from a directory using glob pattern matching\n */\n async loadFromDirectory(directory: string): Promise<void> {\n const patterns = this.extensions.map((ext) => path.join(directory, \"**\", `*${ext}`).replace(/\\\\/g, \"/\"));\n\n for (const pattern of patterns) {\n const files = await glob(pattern, {\n ignore: [\"**/*.d.ts\", \"**/*.test.ts\", \"**/*.spec.ts\"],\n absolute: true,\n });\n\n for (const file of files) {\n await this.loadFile(file, directory);\n }\n }\n\n console.log(`[Stoatx] Loaded ${this.commands.size} command(s) and ${this.registeredEvents.length} event(s)`);\n }\n\n /**\n * Auto-discover command files across one or more roots.\n */\n async autoDiscover(options: AutoDiscoveryOptions = {}): Promise<void> {\n const roots = options.roots?.length ? options.roots : [process.cwd()];\n const includePatterns = options.include?.length ? options.include : this.getDefaultAutoDiscoveryPatterns();\n\n const patterns = roots.flatMap((root) =>\n includePatterns.map((pattern) => path.join(root, pattern).replace(/\\\\/g, \"/\")),\n );\n\n const files = await glob(patterns, {\n ignore: [...CommandRegistry.DEFAULT_AUTO_DISCOVERY_IGNORES, ...(options.ignore ?? [])],\n absolute: true,\n });\n\n const uniqueFiles = [...new Set(files)];\n let candidateFiles = 0;\n for (const file of uniqueFiles) {\n if (!(await this.isLikelyCommandModule(file))) {\n continue;\n }\n candidateFiles++;\n\n const baseDir =\n roots.find((root) => {\n const relative = path.relative(root, file);\n return relative && !relative.startsWith(\"..\") && !path.isAbsolute(relative);\n }) ?? roots[0]!;\n await this.loadFile(file, baseDir);\n }\n\n console.log(`[Stoatx] Loaded ${this.commands.size} command(s) and ${this.registeredEvents.length} event(s)`);\n }\n\n private getDefaultAutoDiscoveryPatterns(): string[] {\n // discordx-like default: scan broadly, then register only decorated classes\n return this.extensions.map((ext) => `**/*${ext}`);\n }\n\n private async isLikelyCommandModule(filePath: string): Promise<boolean> {\n try {\n const source = await fs.readFile(filePath, \"utf8\");\n return source.includes(\"Stoat\") || source.includes(\"SimpleCommand\") || source.includes(\"stoatx:command\");\n } catch {\n // If the file can't be pre-read, fall back to attempting import.\n return true;\n }\n }\n\n /**\n * Register a command instance\n */\n register(instance: object, metadata: CommandMetadata, classConstructor: Function, methodName: string): void {\n const name = metadata.name.toLowerCase();\n\n if (this.commands.has(name)) {\n console.warn(`[Stoatx] Duplicate command name: ${name}. Skipping...`);\n return;\n }\n\n this.validateGuards(classConstructor, metadata.name);\n\n this.commands.set(name, { instance, metadata, methodName, classConstructor });\n\n for (const alias of metadata.aliases) {\n const aliasLower = alias.toLowerCase();\n if (this.aliases.has(aliasLower) || this.commands.has(aliasLower)) {\n console.warn(`[Stoatx] Duplicate alias: ${aliasLower}. Skipping...`);\n continue;\n }\n this.aliases.set(aliasLower, name);\n }\n }\n\n /**\n * Get a command by name or alias\n */\n get(name: string): RegisteredCommand | undefined {\n const lowerName = name.toLowerCase();\n const resolvedName = this.aliases.get(lowerName) ?? lowerName;\n return this.commands.get(resolvedName);\n }\n\n /**\n * Check if a command exists\n */\n has(name: string): boolean {\n const lowerName = name.toLowerCase();\n return this.commands.has(lowerName) || this.aliases.has(lowerName);\n }\n\n /**\n * Get all registered commands\n */\n getAll(): RegisteredCommand[] {\n return Array.from(this.commands.values());\n }\n\n /**\n * Get all command metadata\n */\n getAllMetadata(): CommandMetadata[] {\n return this.getAll().map((c) => c.metadata);\n }\n\n /**\n * Get all registered events\n */\n getEvents(): RegisteredEvent[] {\n return this.registeredEvents;\n }\n\n /**\n * Get commands grouped by category\n */\n getByCategory(): Map<string, RegisteredCommand[]> {\n const categories = new Map<string, RegisteredCommand[]>();\n\n for (const cmd of this.commands.values()) {\n const category = cmd.metadata.category;\n const existing = categories.get(category) ?? [];\n existing.push(cmd);\n categories.set(category, existing);\n }\n\n return categories;\n }\n\n /**\n * Clear all commands\n */\n clear(): void {\n this.commands.clear();\n this.aliases.clear();\n this.registeredEvents.length = 0;\n this.processedStoatClasses.clear();\n }\n\n /**\n * Iterate over commands\n */\n [Symbol.iterator](): IterableIterator<[string, RegisteredCommand]> {\n return this.commands.entries();\n }\n\n /**\n * Iterate over command values\n */\n values(): IterableIterator<RegisteredCommand> {\n return this.commands.values();\n }\n\n /**\n * Iterate over command names\n */\n keys(): IterableIterator<string> {\n return this.commands.keys();\n }\n\n /**\n * Validate that all guards on a command implement the required methods\n * @param commandClass\n * @param commandName\n * @private\n */\n private validateGuards(commandClass: Function, commandName: string): void {\n const guards: Function[] = Reflect.getMetadata(\"stoatx:command:guards\", commandClass) || [];\n\n for (const GuardClass of guards) {\n const guardInstance = new (GuardClass as any)();\n\n if (typeof guardInstance.run !== \"function\") {\n console.error(\n `[Stoatx] FATAL: Guard \"${GuardClass.name}\" on command \"${commandName}\" does not have a run() method.`,\n );\n process.exit(1);\n }\n\n if (typeof guardInstance.guardFail !== \"function\") {\n console.error(\n `[Stoatx] FATAL: Guard \"${GuardClass.name}\" on command \"${commandName}\" does not have a guardFail() method.`,\n );\n console.error(`[Stoatx] All guards must implement guardFail() to handle failed checks.`);\n process.exit(1);\n }\n }\n }\n\n /**\n * Load commands from a single file\n */\n private async loadFile(filePath: string, baseDir: string): Promise<void> {\n try {\n const knownStoatClasses = new Set(decoratorStore.getStoatClasses().keys());\n const fileUrl = pathToFileURL(filePath).href;\n await import(fileUrl);\n\n const allStoatClasses = decoratorStore.getStoatClasses();\n for (const [stoatClass, stoatInstance] of allStoatClasses.entries()) {\n if (knownStoatClasses.has(stoatClass) || this.processedStoatClasses.has(stoatClass)) {\n continue;\n }\n this.registerStoatClassCommands(stoatClass, stoatInstance, filePath, baseDir);\n }\n } catch (error) {\n console.error(`[Stoatx] Failed to load command file: ${filePath}`, error);\n }\n }\n\n private registerStoatClassCommands(stoatClass: Function, instance: object, filePath: string, baseDir: string): void {\n const simpleCommands = getSimpleCommands(stoatClass);\n const events = getEventsMetadata(stoatClass);\n const category = this.getCategoryFromPath(filePath, baseDir);\n\n if (simpleCommands.length === 0 && events.length === 0) {\n console.warn(\n `[Stoatx] Class ${stoatClass.name} is decorated with @Stoat but has no @SimpleCommand, @On or @Once methods. Skipping...`,\n );\n this.processedStoatClasses.add(stoatClass);\n return;\n }\n\n for (const cmdDef of simpleCommands) {\n const method = (instance as any)[cmdDef.methodName];\n if (typeof method !== \"function\") {\n console.warn(`[Stoatx] Method ${cmdDef.methodName} not found on ${stoatClass.name}. Skipping...`);\n continue;\n }\n\n const metadata = buildSimpleCommandMetadata(cmdDef.options, cmdDef.methodName, category);\n this.register(instance, metadata, stoatClass, cmdDef.methodName);\n }\n\n for (const eventDef of events) {\n const method = (instance as any)[eventDef.methodName];\n if (typeof method !== \"function\") {\n console.warn(`[Stoatx] Method ${eventDef.methodName} not found on ${stoatClass.name}. Skipping...`);\n continue;\n }\n\n this.registeredEvents.push({\n instance,\n methodName: eventDef.methodName,\n event: eventDef.event,\n type: eventDef.type,\n });\n }\n\n this.processedStoatClasses.add(stoatClass);\n }\n\n /**\n * Derive category from file path relative to base directory\n */\n private getCategoryFromPath(filePath: string, baseDir: string): string | undefined {\n const relative = path.relative(baseDir, filePath);\n const parts = relative.split(path.sep);\n\n if (parts.length > 1) {\n return parts[0];\n }\n\n return undefined;\n }\n}\n","import \"reflect-metadata\";\nimport { CommandRegistry, RegisteredCommand } from \"./registry\";\nimport type { CommandContext, CommandMetadata, StoatxDiscoveryOptions, StoatxHandlerOptions } from \"./types\";\nimport { Client as StoatClient, ClientEvents, Message } from \"@stoatx/client\";\n\n/**\n * Client - An extended Client that integrates StoatxHandler directly\n *\n * @example\n * ```ts\n * import { Client } from 'stoatx';\n *\n * const client = new Client({\n * prefix: '!',\n * owners: ['owner-user-id'],\n * });\n *\n * await client.initCommands();\n * ```\n */\nexport class Client extends StoatClient {\n public readonly handler: StoatxHandler;\n\n constructor(options: Omit<StoatxHandlerOptions, \"client\">) {\n super();\n this.handler = new StoatxHandler({ ...options, client: this });\n\n // Automatically hook up the message handler\n this.on(\"messageCreate\", async (message) => {\n await this.handler.handle(message);\n });\n }\n\n /**\n * Initialize the StoatxHandler commands\n */\n async initCommands(): Promise<void> {\n await this.handler.init();\n }\n}\n\n/**\n * StoatxHandler - The execution engine for commands\n *\n * Handles message parsing, middleware execution, and command dispatching\n *\n * @internal This class is not intended to be instantiated directly. Use the `Client` from `stoatx` instead.\n */\nexport class StoatxHandler {\n private readonly commandsDir: string | undefined;\n private readonly discoveryOptions: StoatxDiscoveryOptions | undefined;\n private readonly prefixResolver: string | ((ctx: { serverId?: string | undefined }) => string | Promise<string>);\n private readonly owners: Set<string>;\n private readonly registry: CommandRegistry;\n private readonly cooldowns: Map<string, Map<string, number>> = new Map();\n private readonly disableMentionPrefix: boolean;\n private readonly client: StoatClient;\n constructor(options: StoatxHandlerOptions) {\n this.client = options.client;\n this.commandsDir = options.commandsDir;\n this.discoveryOptions = options.discovery;\n this.prefixResolver = options.prefix;\n this.owners = new Set(options.owners ?? []);\n this.registry = new CommandRegistry(options.extensions);\n this.disableMentionPrefix = options.disableMentionPrefix ?? false;\n }\n\n /**\n * Initialize the handler - load all commands\n */\n async init(): Promise<void> {\n if (this.commandsDir) {\n await this.registry.loadFromDirectory(this.commandsDir);\n } else {\n await this.registry.autoDiscover(this.discoveryOptions);\n }\n\n this.attachEvents();\n }\n\n /**\n * Attach registered events to the client\n */\n private attachEvents(): void {\n const events = this.registry.getEvents();\n\n for (const eventDef of events) {\n const handler = async (...args: any[]) => {\n try {\n await (eventDef.instance as any)[eventDef.methodName](...args, this.client);\n } catch (error) {\n console.error(\n `[Stoatx] Event Handler Error in @${eventDef.type === \"on\" ? \"On\" : \"Once\"}('${eventDef.event}'):`,\n error,\n );\n }\n };\n\n const eventName = eventDef.event as keyof ClientEvents;\n if (eventDef.type === \"once\") {\n this.client.once(eventName, handler);\n } else {\n this.client.on(eventName, handler);\n }\n }\n }\n\n /**\n * Parse a raw message into command context\n */\n async parseMessage(\n rawContent: string,\n message: Message,\n meta: {\n authorId: string;\n channelId: string;\n serverId?: string | undefined;\n reply: (content: string) => Promise<Message>;\n },\n ): Promise<CommandContext | null> {\n const prefix = await this.resolvePrefix(meta.serverId);\n let usedPrefix = prefix;\n let withoutPrefix = \"\";\n\n // Check for string prefix\n if (rawContent.startsWith(prefix)) {\n withoutPrefix = rawContent.slice(prefix.length).trim();\n usedPrefix = prefix;\n }\n // Check for mention prefix (e.g., \"<@bot-id> command\") - unless disabled\n else if (!this.disableMentionPrefix && rawContent.match(/^<@!?[\\w]+>/)) {\n const mentionMatch = rawContent.match(/^<@!?([\\w]+)>\\s*/);\n if (mentionMatch) {\n const mentionedId = mentionMatch[1];\n const botId = this.client.user?.id;\n\n // Only process if mentioned user is the bot\n if (botId && mentionedId === botId) {\n usedPrefix = mentionMatch[0];\n withoutPrefix = rawContent.slice(mentionMatch[0].length).trim();\n } else {\n }\n }\n }\n\n if (!withoutPrefix) {\n return null;\n }\n\n const [commandName, ...args] = withoutPrefix.split(/\\s+/);\n\n if (!commandName) {\n return null;\n }\n\n return {\n client: this.client,\n content: rawContent,\n authorId: meta.authorId,\n channelId: meta.channelId,\n serverId: meta.serverId,\n args,\n prefix: usedPrefix,\n commandName: commandName.toLowerCase(),\n reply: meta.reply,\n message,\n };\n }\n\n /**\n * Handle a message object using the configured message adapter\n *\n * @example\n * ```ts\n * // With message adapter configured\n * client.on('messageCreate', (message) => {\n * handler.handle(message);\n * });\n * ```\n */\n async handle(message: Message): Promise<boolean> {\n if (!message.channel || !message.author) {\n return false;\n }\n\n // Skip messages from bots\n if (message.author.bot) {\n return false;\n }\n\n const rawContent = message.content;\n const authorId = message.author.id;\n const channelId = message.channel.id;\n const serverId = message.server?.id;\n const reply = async (content: string) => {\n return await message.channel!.send(content);\n };\n\n // rawContent won't be null since a command will be invoked with a prefix\n await this.handleMessage(rawContent!, message, {\n authorId,\n channelId,\n serverId,\n reply,\n });\n\n return true;\n }\n\n /**\n * Handle a raw message string with metadata\n *\n * @example\n * ```ts\n * // Manual usage without message adapter\n * client.on('messageCreate', (message) => {\n * handler.handleMessage(message.content, message, {\n * authorId: message.author.id,\n * channelId: message.channel.id,\n * serverId: message.server?.id,\n * reply: (content) => message.channel.sendMessage(content),\n * });\n * });\n * ```\n */\n async handleMessage(\n rawContent: string,\n message: Message,\n meta: {\n authorId: string;\n channelId: string;\n serverId?: string | undefined;\n reply: (content: string) => Promise<Message>;\n },\n ): Promise<void> {\n const ctx = await this.parseMessage(rawContent, message, meta);\n\n if (!ctx) {\n return;\n }\n\n await this.execute(ctx);\n }\n\n /**\n * Execute a command with the given context\n */\n async execute(ctx: CommandContext): Promise<boolean> {\n const registered = this.registry.get(ctx.commandName);\n\n if (!registered) {\n return false;\n }\n\n const { instance, metadata, methodName, classConstructor } = registered;\n\n // Owner-only check\n if (metadata.ownerOnly && !this.owners.has(ctx.authorId)) {\n await ctx.reply(\"This command is owner-only.\");\n return false;\n }\n\n // Guard checks - use classConstructor for guard metadata\n const guards: Function[] = Reflect.getMetadata(\"stoatx:command:guards\", classConstructor) || [];\n for (const guardClass of guards) {\n const guardInstance = new (guardClass as any)();\n if (typeof guardInstance.run === \"function\") {\n const guardResult = await guardInstance.run(ctx);\n if (!guardResult) {\n if (typeof guardInstance.guardFail === \"function\") {\n await guardInstance.guardFail(ctx);\n } else {\n console.error(\"[Stoatx] Guard check failed but no guardFail method defined on\", guardClass.name);\n }\n return false;\n }\n }\n }\n\n // Cooldown check\n if (!this.checkCooldown(ctx.authorId, metadata)) {\n const remaining = this.getRemainingCooldown(ctx.authorId, metadata);\n\n // For method-based commands, check if instance has onCooldown\n if (typeof (instance as any).onCooldown === \"function\") {\n await (instance as any).onCooldown(ctx, remaining);\n } else {\n await ctx.reply(`Please wait ${(remaining / 1000).toFixed(1)} seconds before using this command again.`);\n }\n return false;\n }\n\n try {\n await (instance as any)[methodName](ctx);\n\n // Set cooldown after successful execution\n if (metadata.cooldown > 0) {\n this.setCooldown(ctx.authorId, metadata);\n }\n\n return true;\n } catch (error) {\n // Handle errors\n if (typeof (instance as any).onError === \"function\") {\n await (instance as any).onError(ctx, error as Error);\n } else {\n console.error(`[Stoatx] Error in command ${metadata.name}:`, error);\n }\n return false;\n }\n }\n\n /**\n * Get the command registry\n */\n getRegistry(): CommandRegistry {\n return this.registry;\n }\n\n /**\n * Get a command by name or alias\n */\n getCommand(name: string): RegisteredCommand | undefined {\n return this.registry.get(name);\n }\n\n /**\n * Get all commands\n */\n getCommands(): RegisteredCommand[] {\n return this.registry.getAll();\n }\n\n /**\n * Reload all commands\n */\n async reload(): Promise<void> {\n this.registry.clear();\n this.cooldowns.clear();\n if (this.commandsDir) {\n await this.registry.loadFromDirectory(this.commandsDir);\n return;\n }\n\n await this.registry.autoDiscover(this.discoveryOptions);\n }\n\n /**\n * Check if a user is an owner\n */\n isOwner(userId: string): boolean {\n return this.owners.has(userId);\n }\n\n /**\n * Add an owner\n */\n addOwner(userId: string): void {\n this.owners.add(userId);\n }\n\n /**\n * Remove an owner\n */\n removeOwner(userId: string): void {\n this.owners.delete(userId);\n }\n\n /**\n * Resolve the prefix for a context\n */\n private async resolvePrefix(serverId?: string | undefined): Promise<string> {\n if (typeof this.prefixResolver === \"function\") {\n return this.prefixResolver({ serverId });\n }\n return this.prefixResolver;\n }\n\n /**\n * Check if user is on cooldown\n */\n private checkCooldown(userId: string, metadata: CommandMetadata): boolean {\n if (metadata.cooldown <= 0) return true;\n\n const commandCooldowns = this.cooldowns.get(metadata.name);\n if (!commandCooldowns) return true;\n\n const userCooldown = commandCooldowns.get(userId);\n if (!userCooldown) return true;\n\n return Date.now() >= userCooldown;\n }\n\n /**\n * Get remaining cooldown time in ms\n */\n private getRemainingCooldown(userId: string, metadata: CommandMetadata): number {\n const commandCooldowns = this.cooldowns.get(metadata.name);\n if (!commandCooldowns) return 0;\n\n const userCooldown = commandCooldowns.get(userId);\n if (!userCooldown) return 0;\n\n return Math.max(0, userCooldown - Date.now());\n }\n\n /**\n * Set cooldown for a user\n */\n private setCooldown(userId: string, metadata: CommandMetadata): void {\n if (!this.cooldowns.has(metadata.name)) {\n this.cooldowns.set(metadata.name, new Map());\n }\n\n const commandCooldowns = this.cooldowns.get(metadata.name)!;\n commandCooldowns.set(userId, Date.now() + metadata.cooldown);\n }\n}\n"],"mappings":"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","names":["import_reflect_metadata","import_reflect_metadata","import_reflect_metadata","relative","import_reflect_metadata","StoatClient"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/decorators/Stoat.ts","../src/decorators/keys.ts","../src/decorators/store.ts","../src/decorators/SimpleCommand.ts","../src/decorators/Guard.ts","../src/decorators/Events.ts","../src/decorators/utils.ts","../src/registry.ts","../src/handler.ts"],"sourcesContent":["// Types\nexport * from \"./types\";\n\n// Decorators\nexport * from \"./decorators\";\n\n// Registry\nexport * from \"./registry\";\n\n// Handler\nexport { Client, DefaultCooldownManager } from \"./handler\";\nexport type { StoatxHandler } from \"./handler\";\nexport * from \"@stoatx/client\";\n","import \"reflect-metadata\";\nimport { METADATA_KEYS } from \"./keys\";\nimport { decoratorStore } from \"./store\";\n\n/**\n * @Stoat\n * Marks a class as a Stoat command container.\n * Use this decorator on classes that contain @SimpleCommand methods.\n *\n * @example\n * ```ts\n * import { Stoat, SimpleCommand, CommandContext } from 'stoatx';\n *\n * @Stoat()\n * class ModerationCommands {\n * @SimpleCommand({ name: 'ban', description: 'Ban a user' })\n * async ban(ctx: CommandContext) {\n * await ctx.reply('User banned!');\n * }\n *\n * @SimpleCommand({ name: 'kick', description: 'Kick a user' })\n * async kick(ctx: CommandContext) {\n * await ctx.reply('User kicked!');\n * }\n * }\n * ```\n */\nexport function Stoat(): ClassDecorator {\n return (target: Function) => {\n Reflect.defineMetadata(METADATA_KEYS.IS_STOAT_CLASS, true, target);\n decoratorStore.registerStoatClass(target);\n };\n}\n\n/**\n * Check if a class is decorated with @Stoat\n */\nexport function isStoatClass(target: Function): boolean {\n return Reflect.getMetadata(METADATA_KEYS.IS_STOAT_CLASS, target) === true;\n}\n","/**\n * Metadata keys used by decorators\n */\nexport const METADATA_KEYS = {\n IS_STOAT_CLASS: Symbol(\"stoatx:stoat:isClass\"),\n SIMPLE_COMMANDS: Symbol(\"stoatx:stoat:simpleCommands\"),\n GUARDS: \"stoatx:command:guards\",\n EVENTS: Symbol(\"stoatx:stoat:events\"),\n} as const;\n","import type { RegisteredCommand } from \"../registry\";\n\n/**\n * Global store for all decorated classes and commands\n * This allows automatic registration without directory scanning\n */\nexport class DecoratorStore {\n private static instance: DecoratorStore;\n\n /** Stoat classes with their SimpleCommand methods */\n private stoatClasses: Map<Function, object> = new Map();\n\n /** Registered commands from @Stoat/@SimpleCommand decorators */\n private commands: RegisteredCommand[] = [];\n\n /** Whether the store has been initialized */\n private initialized = false;\n\n private constructor() {}\n\n static getInstance(): DecoratorStore {\n if (!DecoratorStore.instance) {\n DecoratorStore.instance = new DecoratorStore();\n }\n return DecoratorStore.instance;\n }\n\n /**\n * Register a @Stoat decorated class\n */\n registerStoatClass(classConstructor: Function): void {\n if (!this.stoatClasses.has(classConstructor)) {\n // Create instance immediately when decorated\n const instance = new (classConstructor as new () => object)();\n this.stoatClasses.set(classConstructor, instance);\n }\n }\n\n /**\n * Get all registered Stoat classes with their instances\n */\n getStoatClasses(): Map<Function, object> {\n return this.stoatClasses;\n }\n\n /**\n * Add a registered command\n */\n addCommand(command: RegisteredCommand): void {\n this.commands.push(command);\n }\n\n /**\n * Get all registered commands\n */\n getCommands(): RegisteredCommand[] {\n return this.commands;\n }\n\n /**\n * Clear all registered classes (useful for testing)\n */\n clear(): void {\n this.stoatClasses.clear();\n this.commands = [];\n this.initialized = false;\n }\n\n /**\n * Mark as initialized\n */\n markInitialized(): void {\n this.initialized = true;\n }\n\n /**\n * Check if initialized\n */\n isInitialized(): boolean {\n return this.initialized;\n }\n}\n\nexport const decoratorStore = DecoratorStore.getInstance();\n","import \"reflect-metadata\";\nimport type { SimpleCommandOptions } from \"../types\";\nimport { METADATA_KEYS } from \"./keys\";\n\n/**\n * Stored simple command metadata from method decorator\n */\nexport interface SimpleCommandDefinition {\n methodName: string;\n options: SimpleCommandOptions;\n}\n\n/**\n * @SimpleCommand\n * Marks a method as a simple command within a @Stoat() decorated class.\n *\n * @example\n * ```ts\n * @Stoat()\n * class Example {\n * @SimpleCommand({ name: 'ping', description: 'Replies with Pong!' })\n * async ping(ctx: CommandContext) {\n * await ctx.reply('Pong!');\n * }\n *\n * @SimpleCommand({ aliases: ['perm'], name: 'permission' })\n * async permission(ctx: CommandContext) {\n * await ctx.reply('Access granted');\n * }\n * }\n * ```\n */\nexport function SimpleCommand(options: SimpleCommandOptions = {}): MethodDecorator {\n return (target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {\n const constructor = target.constructor;\n\n // Get existing simple commands or create new array\n const existingCommands: SimpleCommandDefinition[] =\n Reflect.getMetadata(METADATA_KEYS.SIMPLE_COMMANDS, constructor) || [];\n\n // Add this command definition\n existingCommands.push({\n methodName: String(propertyKey),\n options,\n });\n\n Reflect.defineMetadata(METADATA_KEYS.SIMPLE_COMMANDS, existingCommands, constructor);\n\n return descriptor;\n };\n}\n\n/**\n * Get all simple command definitions from a @Stoat class\n */\nexport function getSimpleCommands(target: Function): SimpleCommandDefinition[] {\n return Reflect.getMetadata(METADATA_KEYS.SIMPLE_COMMANDS, target) || [];\n}\n","import \"reflect-metadata\";\nimport { METADATA_KEYS } from \"./keys\";\n\n/**\n * @Guard\n * Runs before a command to check if it should execute.\n * Should return true to allow execution, false to block.\n * Applied on @Stoat classes to guard all contained @SimpleCommand methods.\n *\n * @example\n * ```ts\n * import { Guard, Stoat, SimpleCommand, CommandContext } from 'stoatx';\n *\n * // Define a guard\n * class NotBot implements StoatxGuard {\n * run(ctx: CommandContext): boolean {\n * return !ctx.message.author.bot;\n * }\n *\n * guardFail(ctx: CommandContext): void {\n * ctx.reply(\"Bots cannot use this command!\");\n * }\n * }\n *\n * @Stoat()\n * @Guard(NotBot)\n * class AdminCommands {\n * @SimpleCommand({ name: 'admin', description: 'Admin only command' })\n * async admin(ctx: CommandContext) {\n * ctx.reply(\"You passed the guard check!\");\n * }\n * }\n * ```\n */\nexport function Guard(guardClass: Function): ClassDecorator {\n return (target: Function) => {\n const existingGuards: Function[] = Reflect.getMetadata(METADATA_KEYS.GUARDS, target) || [];\n existingGuards.push(guardClass);\n Reflect.defineMetadata(METADATA_KEYS.GUARDS, existingGuards, target);\n };\n}\n\n/**\n * Get all guards from a decorated class\n */\nexport function getGuards(target: Function): Function[] {\n return Reflect.getMetadata(METADATA_KEYS.GUARDS, target) || [];\n}\n","import \"reflect-metadata\";\nimport { METADATA_KEYS } from \"./keys\";\n\nexport interface EventDefinition {\n methodName: string;\n event: string;\n type: \"on\" | \"once\";\n}\n\nfunction createEventDecorator(event: string, type: \"on\" | \"once\"): MethodDecorator {\n return (target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {\n const constructor = target.constructor;\n\n // Retrieve existing events or initialize a fresh array\n const existingEvents: EventDefinition[] = Reflect.getMetadata(METADATA_KEYS.EVENTS, constructor) || [];\n\n existingEvents.push({\n methodName: String(propertyKey),\n event,\n type,\n });\n\n Reflect.defineMetadata(METADATA_KEYS.EVENTS, existingEvents, constructor);\n\n return descriptor;\n };\n}\n\n/**\n * @On\n * Triggered on every occurrence of the event.\n * Marks a method to be executed whenever the specified client event is emitted.\n *\n * @example\n * ```ts\n * import { Stoat, On } from 'stoatx';\n * import { Message, Client } from 'stoat.js';\n *\n * @Stoat()\n * class BotEvents {\n * @On('messageCreate')\n * async onMessage(message: Message, client: Client) {\n * console.log('New message received:', message.content);\n * }\n * }\n * ```\n *\n * @param event The name of the client event to listen to\n */\nexport function On(event: string): MethodDecorator {\n return createEventDecorator(event, \"on\");\n}\n\n/**\n * @Once\n * Triggered only fully once.\n * Marks a method to be executed only the FIRST time the specified client event is emitted.\n *\n * @example\n * ```ts\n * import { Stoat, Once } from 'stoatx';\n * import { Client } from 'stoat.js';\n *\n * @Stoat()\n * class BotEvents {\n * @Once('ready')\n * async onReady(client: Client) {\n * console.log('Bot successfully started and logged in!');\n * }\n * }\n * ```\n *\n * @param event The name of the client event to listen to\n */\nexport function Once(event: string): MethodDecorator {\n return createEventDecorator(event, \"once\");\n}\n\n/**\n * Get all event definitions from a @Stoat class\n */\nexport function getEventsMetadata(target: Function): EventDefinition[] {\n return Reflect.getMetadata(METADATA_KEYS.EVENTS, target) || [];\n}\n","import type { CommandMetadata, SimpleCommandOptions } from \"../types\";\n\n/**\n * Build CommandMetadata from SimpleCommandOptions\n */\nexport function buildSimpleCommandMetadata(\n options: SimpleCommandOptions,\n methodName: string,\n category?: string,\n): CommandMetadata {\n return {\n name: options.name ?? methodName.toLowerCase(),\n description: options.description ?? \"No description provided\",\n aliases: options.aliases ?? [],\n permissions: options.permissions ?? [],\n category: options.category ?? category ?? \"uncategorized\",\n cooldown: options.cooldown ?? 0,\n ...(options.cooldownStorage !== undefined ? { cooldownStorage: options.cooldownStorage } : {}),\n nsfw: options.nsfw ?? false,\n ownerOnly: options.ownerOnly ?? false,\n };\n}\n","import * as path from \"node:path\";\nimport * as fs from \"node:fs/promises\";\nimport { pathToFileURL } from \"node:url\";\nimport { glob } from \"tinyglobby\";\nimport { buildSimpleCommandMetadata, getSimpleCommands, getEventsMetadata } from \"./decorators\";\nimport { decoratorStore } from \"./decorators/store\";\nimport type { CommandMetadata } from \"./types\";\n\ninterface AutoDiscoveryOptions {\n roots?: string[];\n include?: string[];\n ignore?: string[];\n}\n\n/**\n * Stored command entry from @Stoat/@SimpleCommand registration.\n */\nexport interface RegisteredCommand {\n /** Instance of the @Stoat class */\n instance: object;\n /** Command metadata */\n metadata: CommandMetadata;\n /** Method name to call */\n methodName: string;\n /** The original class constructor (for guard validation) */\n classConstructor: Function;\n}\n\n/**\n * Stored event entry from @On/@Once registration.\n */\nexport interface RegisteredEvent {\n instance: object;\n methodName: string;\n event: string;\n type: \"on\" | \"once\";\n}\n\n/**\n * CommandRegistry - Scans directories and stores commands in a Map\n *\n * @example\n * ```ts\n * const registry = new CommandRegistry();\n * await registry.loadFromDirectory('./src/commands');\n *\n * const ping = registry.get('ping');\n * const allCommands = registry.getAll();\n * ```\n */\nexport class CommandRegistry {\n private static readonly DEFAULT_AUTO_DISCOVERY_IGNORES = [\n \"**/node_modules/**\",\n \"**/.git/**\",\n \"**/*.d.ts\",\n \"**/*.test.*\",\n \"**/*.spec.*\",\n ];\n\n private readonly commands: Map<string, RegisteredCommand> = new Map();\n private readonly aliases: Map<string, string> = new Map();\n private readonly registeredEvents: RegisteredEvent[] = [];\n private readonly extensions: string[];\n private readonly processedStoatClasses: Set<Function> = new Set();\n\n constructor(extensions: string[] = [\".js\", \".mjs\", \".cjs\"]) {\n this.extensions = extensions;\n }\n\n /**\n * Get the number of registered commands\n */\n get size(): number {\n return this.commands.size;\n }\n\n /**\n * Load commands from a directory using glob pattern matching\n */\n async loadFromDirectory(directory: string): Promise<void> {\n const patterns = this.extensions.map((ext) => path.join(directory, \"**\", `*${ext}`).replace(/\\\\/g, \"/\"));\n\n for (const pattern of patterns) {\n const files = await glob(pattern, {\n ignore: [\"**/*.d.ts\", \"**/*.test.ts\", \"**/*.spec.ts\"],\n absolute: true,\n });\n\n for (const file of files) {\n await this.loadFile(file, directory);\n }\n }\n\n console.log(`[Stoatx] Loaded ${this.commands.size} command(s) and ${this.registeredEvents.length} event(s)`);\n }\n\n /**\n * Auto-discover command files across one or more roots.\n */\n async autoDiscover(options: AutoDiscoveryOptions = {}): Promise<void> {\n const roots = options.roots?.length ? options.roots : [process.cwd()];\n const includePatterns = options.include?.length ? options.include : this.getDefaultAutoDiscoveryPatterns();\n\n const patterns = roots.flatMap((root) =>\n includePatterns.map((pattern) => path.join(root, pattern).replace(/\\\\/g, \"/\")),\n );\n\n const files = await glob(patterns, {\n ignore: [...CommandRegistry.DEFAULT_AUTO_DISCOVERY_IGNORES, ...(options.ignore ?? [])],\n absolute: true,\n });\n\n const uniqueFiles = [...new Set(files)];\n let candidateFiles = 0;\n for (const file of uniqueFiles) {\n if (!(await this.isLikelyCommandModule(file))) {\n continue;\n }\n candidateFiles++;\n\n const baseDir =\n roots.find((root) => {\n const relative = path.relative(root, file);\n return relative && !relative.startsWith(\"..\") && !path.isAbsolute(relative);\n }) ?? roots[0]!;\n await this.loadFile(file, baseDir);\n }\n\n console.log(`[Stoatx] Loaded ${this.commands.size} command(s) and ${this.registeredEvents.length} event(s)`);\n }\n\n private getDefaultAutoDiscoveryPatterns(): string[] {\n // discordx-like default: scan broadly, then register only decorated classes\n return this.extensions.map((ext) => `**/*${ext}`);\n }\n\n private async isLikelyCommandModule(filePath: string): Promise<boolean> {\n try {\n const source = await fs.readFile(filePath, \"utf8\");\n return source.includes(\"Stoat\") || source.includes(\"SimpleCommand\") || source.includes(\"stoatx:command\");\n } catch {\n // If the file can't be pre-read, fall back to attempting import.\n return true;\n }\n }\n\n /**\n * Register a command instance\n */\n register(instance: object, metadata: CommandMetadata, classConstructor: Function, methodName: string): void {\n const name = metadata.name.toLowerCase();\n\n if (this.commands.has(name)) {\n console.warn(`[Stoatx] Duplicate command name: ${name}. Skipping...`);\n return;\n }\n\n this.validateGuards(classConstructor, metadata.name);\n\n this.commands.set(name, { instance, metadata, methodName, classConstructor });\n\n for (const alias of metadata.aliases) {\n const aliasLower = alias.toLowerCase();\n if (this.aliases.has(aliasLower) || this.commands.has(aliasLower)) {\n console.warn(`[Stoatx] Duplicate alias: ${aliasLower}. Skipping...`);\n continue;\n }\n this.aliases.set(aliasLower, name);\n }\n }\n\n /**\n * Get a command by name or alias\n */\n get(name: string): RegisteredCommand | undefined {\n const lowerName = name.toLowerCase();\n const resolvedName = this.aliases.get(lowerName) ?? lowerName;\n return this.commands.get(resolvedName);\n }\n\n /**\n * Check if a command exists\n */\n has(name: string): boolean {\n const lowerName = name.toLowerCase();\n return this.commands.has(lowerName) || this.aliases.has(lowerName);\n }\n\n /**\n * Get all registered commands\n */\n getAll(): RegisteredCommand[] {\n return Array.from(this.commands.values());\n }\n\n /**\n * Get all command metadata\n */\n getAllMetadata(): CommandMetadata[] {\n return this.getAll().map((c) => c.metadata);\n }\n\n /**\n * Get all registered events\n */\n getEvents(): RegisteredEvent[] {\n return this.registeredEvents;\n }\n\n /**\n * Get commands grouped by category\n */\n getByCategory(): Map<string, RegisteredCommand[]> {\n const categories = new Map<string, RegisteredCommand[]>();\n\n for (const cmd of this.commands.values()) {\n const category = cmd.metadata.category;\n const existing = categories.get(category) ?? [];\n existing.push(cmd);\n categories.set(category, existing);\n }\n\n return categories;\n }\n\n /**\n * Clear all commands\n */\n clear(): void {\n this.commands.clear();\n this.aliases.clear();\n this.registeredEvents.length = 0;\n this.processedStoatClasses.clear();\n }\n\n /**\n * Iterate over commands\n */\n [Symbol.iterator](): IterableIterator<[string, RegisteredCommand]> {\n return this.commands.entries();\n }\n\n /**\n * Iterate over command values\n */\n values(): IterableIterator<RegisteredCommand> {\n return this.commands.values();\n }\n\n /**\n * Iterate over command names\n */\n keys(): IterableIterator<string> {\n return this.commands.keys();\n }\n\n /**\n * Validate that all guards on a command implement the required methods\n * @param commandClass\n * @param commandName\n * @private\n */\n private validateGuards(commandClass: Function, commandName: string): void {\n const guards: Function[] = Reflect.getMetadata(\"stoatx:command:guards\", commandClass) || [];\n\n for (const GuardClass of guards) {\n const guardInstance = new (GuardClass as any)();\n\n if (typeof guardInstance.run !== \"function\") {\n console.error(\n `[Stoatx] FATAL: Guard \"${GuardClass.name}\" on command \"${commandName}\" does not have a run() method.`,\n );\n process.exit(1);\n }\n\n if (typeof guardInstance.guardFail !== \"function\") {\n console.error(\n `[Stoatx] FATAL: Guard \"${GuardClass.name}\" on command \"${commandName}\" does not have a guardFail() method.`,\n );\n console.error(`[Stoatx] All guards must implement guardFail() to handle failed checks.`);\n process.exit(1);\n }\n }\n }\n\n /**\n * Load commands from a single file\n */\n private async loadFile(filePath: string, baseDir: string): Promise<void> {\n try {\n const knownStoatClasses = new Set(decoratorStore.getStoatClasses().keys());\n const fileUrl = pathToFileURL(filePath).href;\n await import(fileUrl);\n\n const allStoatClasses = decoratorStore.getStoatClasses();\n for (const [stoatClass, stoatInstance] of allStoatClasses.entries()) {\n if (knownStoatClasses.has(stoatClass) || this.processedStoatClasses.has(stoatClass)) {\n continue;\n }\n this.registerStoatClassCommands(stoatClass, stoatInstance, filePath, baseDir);\n }\n } catch (error) {\n console.error(`[Stoatx] Failed to load command file: ${filePath}`, error);\n }\n }\n\n private registerStoatClassCommands(stoatClass: Function, instance: object, filePath: string, baseDir: string): void {\n const simpleCommands = getSimpleCommands(stoatClass);\n const events = getEventsMetadata(stoatClass);\n const category = this.getCategoryFromPath(filePath, baseDir);\n\n if (simpleCommands.length === 0 && events.length === 0) {\n console.warn(\n `[Stoatx] Class ${stoatClass.name} is decorated with @Stoat but has no @SimpleCommand, @On or @Once methods. Skipping...`,\n );\n this.processedStoatClasses.add(stoatClass);\n return;\n }\n\n for (const cmdDef of simpleCommands) {\n const method = (instance as any)[cmdDef.methodName];\n if (typeof method !== \"function\") {\n console.warn(`[Stoatx] Method ${cmdDef.methodName} not found on ${stoatClass.name}. Skipping...`);\n continue;\n }\n\n const metadata = buildSimpleCommandMetadata(cmdDef.options, cmdDef.methodName, category);\n this.register(instance, metadata, stoatClass, cmdDef.methodName);\n }\n\n for (const eventDef of events) {\n const method = (instance as any)[eventDef.methodName];\n if (typeof method !== \"function\") {\n console.warn(`[Stoatx] Method ${eventDef.methodName} not found on ${stoatClass.name}. Skipping...`);\n continue;\n }\n\n this.registeredEvents.push({\n instance,\n methodName: eventDef.methodName,\n event: eventDef.event,\n type: eventDef.type,\n });\n }\n\n this.processedStoatClasses.add(stoatClass);\n }\n\n /**\n * Derive category from file path relative to base directory\n */\n private getCategoryFromPath(filePath: string, baseDir: string): string | undefined {\n const relative = path.relative(baseDir, filePath);\n const parts = relative.split(path.sep);\n\n if (parts.length > 1) {\n return parts[0];\n }\n\n return undefined;\n }\n}\n","import \"reflect-metadata\";\nimport { CommandRegistry, RegisteredCommand } from \"./registry\";\nimport type {\n CommandContext,\n CommandMetadata,\n StoatxDiscoveryOptions,\n StoatxHandlerOptions,\n CooldownManager,\n} from \"./types\";\nimport { Client as StoatClient, ClientEvents, Message } from \"@stoatx/client\";\n\n/**\n * Default in-memory cooldown manager\n */\nexport class DefaultCooldownManager implements CooldownManager {\n private readonly cooldowns: Map<string, Map<string, number>> = new Map();\n\n check(ctx: CommandContext, metadata: CommandMetadata): boolean {\n if (metadata.cooldown <= 0) return true;\n\n const commandCooldowns = this.cooldowns.get(metadata.name);\n if (!commandCooldowns) return true;\n\n const expirationTime = commandCooldowns.get(ctx.authorId);\n if (!expirationTime) return true;\n\n if (Date.now() > expirationTime) {\n commandCooldowns.delete(ctx.authorId);\n return true;\n }\n\n return false;\n }\n\n getRemaining(ctx: CommandContext, metadata: CommandMetadata): number {\n const commandCooldowns = this.cooldowns.get(metadata.name);\n if (!commandCooldowns) return 0;\n\n const userCooldown = commandCooldowns.get(ctx.authorId);\n if (!userCooldown) return 0;\n\n return Math.max(0, userCooldown - Date.now());\n }\n\n set(ctx: CommandContext, metadata: CommandMetadata): void {\n if (!this.cooldowns.has(metadata.name)) {\n this.cooldowns.set(metadata.name, new Map());\n }\n\n const commandCooldowns = this.cooldowns.get(metadata.name)!;\n commandCooldowns.set(ctx.authorId, Date.now() + metadata.cooldown);\n }\n\n clear(): void {\n this.cooldowns.clear();\n }\n}\n\n/**\n * Client - An extended Client that integrates StoatxHandler directly\n *\n * @example\n * ```ts\n * import { Client } from 'stoatx';\n *\n * const client = new Client({\n * prefix: '!',\n * owners: ['owner-user-id'],\n * });\n *\n * await client.initCommands();\n * ```\n */\nexport class Client extends StoatClient {\n public readonly handler: StoatxHandler;\n\n constructor(options: Omit<StoatxHandlerOptions, \"client\">) {\n super();\n this.handler = new StoatxHandler({ ...options, client: this });\n\n // Automatically hook up the message handler\n this.on(\"messageCreate\", async (message) => {\n await this.handler.handle(message);\n });\n }\n\n /**\n * Initialize the StoatxHandler commands\n */\n async initCommands(): Promise<void> {\n await this.handler.init();\n }\n}\n\n/**\n * StoatxHandler - The execution engine for commands\n *\n * Handles message parsing, middleware execution, and command dispatching\n *\n * @internal This class is not intended to be instantiated directly. Use the `Client` from `stoatx` instead.\n */\nexport class StoatxHandler {\n private readonly commandsDir: string | undefined;\n private readonly discoveryOptions: StoatxDiscoveryOptions | undefined;\n private readonly prefixResolver: string | ((ctx: { serverId?: string | undefined }) => string | Promise<string>);\n private readonly owners: Set<string>;\n private readonly registry: CommandRegistry;\n private readonly cooldownManager: CooldownManager;\n private readonly disableMentionPrefix: boolean;\n private readonly client: StoatClient;\n constructor(options: StoatxHandlerOptions) {\n this.client = options.client;\n this.commandsDir = options.commandsDir;\n this.discoveryOptions = options.discovery;\n this.prefixResolver = options.prefix;\n this.owners = new Set(options.owners ?? []);\n this.registry = new CommandRegistry(options.extensions);\n this.disableMentionPrefix = options.disableMentionPrefix ?? false;\n this.cooldownManager = options.cooldownManager ?? new DefaultCooldownManager();\n }\n\n /**\n * Initialize the handler - load all commands\n */\n async init(): Promise<void> {\n if (this.commandsDir) {\n await this.registry.loadFromDirectory(this.commandsDir);\n } else {\n await this.registry.autoDiscover(this.discoveryOptions);\n }\n\n this.attachEvents();\n }\n\n /**\n * Attach registered events to the client\n */\n private attachEvents(): void {\n const events = this.registry.getEvents();\n\n for (const eventDef of events) {\n const handler = async (...args: any[]) => {\n try {\n await (eventDef.instance as any)[eventDef.methodName](...args, this.client);\n } catch (error) {\n console.error(\n `[Stoatx] Event Handler Error in @${eventDef.type === \"on\" ? \"On\" : \"Once\"}('${eventDef.event}'):`,\n error,\n );\n }\n };\n\n const eventName = eventDef.event as keyof ClientEvents;\n if (eventDef.type === \"once\") {\n this.client.once(eventName, handler);\n } else {\n this.client.on(eventName, handler);\n }\n }\n }\n\n /**\n * Parse a raw message into command context\n */\n async parseMessage(\n rawContent: string,\n message: Message,\n meta: {\n authorId: string;\n channelId: string;\n serverId?: string | undefined;\n reply: (content: string) => Promise<Message>;\n },\n ): Promise<CommandContext | null> {\n const prefix = await this.resolvePrefix(meta.serverId);\n let usedPrefix = prefix;\n let withoutPrefix = \"\";\n\n // Check for string prefix\n if (rawContent.startsWith(prefix)) {\n withoutPrefix = rawContent.slice(prefix.length).trim();\n usedPrefix = prefix;\n }\n // Check for mention prefix (e.g., \"<@bot-id> command\") - unless disabled\n else if (!this.disableMentionPrefix && rawContent.match(/^<@!?[\\w]+>/)) {\n const mentionMatch = rawContent.match(/^<@!?([\\w]+)>\\s*/);\n if (mentionMatch) {\n const mentionedId = mentionMatch[1];\n const botId = this.client.user?.id;\n\n // Only process if mentioned user is the bot\n if (botId && mentionedId === botId) {\n usedPrefix = mentionMatch[0];\n withoutPrefix = rawContent.slice(mentionMatch[0].length).trim();\n } else {\n }\n }\n }\n\n if (!withoutPrefix) {\n return null;\n }\n\n const [commandName, ...args] = withoutPrefix.split(/\\s+/);\n\n if (!commandName) {\n return null;\n }\n\n return {\n client: this.client,\n content: rawContent,\n authorId: meta.authorId,\n channelId: meta.channelId,\n serverId: meta.serverId,\n args,\n prefix: usedPrefix,\n commandName: commandName.toLowerCase(),\n reply: meta.reply,\n message,\n };\n }\n\n /**\n * Handle a message object using the configured message adapter\n *\n * @example\n * ```ts\n * // With message adapter configured\n * client.on('messageCreate', (message) => {\n * handler.handle(message);\n * });\n * ```\n */\n async handle(message: Message): Promise<boolean> {\n if (!message.channel || !message.author) {\n return false;\n }\n\n // Skip messages from bots\n if (message.author.bot) {\n return false;\n }\n\n const rawContent = message.content;\n const authorId = message.author.id;\n const channelId = message.channel.id;\n const serverId = message.server?.id;\n const reply = async (content: string) => {\n return await message.channel!.send(content);\n };\n\n // rawContent won't be null since a command will be invoked with a prefix\n await this.handleMessage(rawContent!, message, {\n authorId,\n channelId,\n serverId,\n reply,\n });\n\n return true;\n }\n\n /**\n * Handle a raw message string with metadata\n *\n * @example\n * ```ts\n * // Manual usage without message adapter\n * client.on('messageCreate', (message) => {\n * handler.handleMessage(message.content, message, {\n * authorId: message.author.id,\n * channelId: message.channel.id,\n * serverId: message.server?.id,\n * reply: (content) => message.channel.sendMessage(content),\n * });\n * });\n * ```\n */\n async handleMessage(\n rawContent: string,\n message: Message,\n meta: {\n authorId: string;\n channelId: string;\n serverId?: string | undefined;\n reply: (content: string) => Promise<Message>;\n },\n ): Promise<void> {\n const ctx = await this.parseMessage(rawContent, message, meta);\n\n if (!ctx) {\n return;\n }\n\n await this.execute(ctx);\n }\n\n /**\n * Execute a command with the given context\n */\n async execute(ctx: CommandContext): Promise<boolean> {\n const registered = this.registry.get(ctx.commandName);\n\n if (!registered) {\n return false;\n }\n\n const { instance, metadata, methodName, classConstructor } = registered;\n\n // Owner-only check\n if (metadata.ownerOnly && !this.owners.has(ctx.authorId)) {\n await ctx.reply(\"This command is owner-only.\");\n return false;\n }\n\n // Guard checks - use classConstructor for guard metadata\n const guards: Function[] = Reflect.getMetadata(\"stoatx:command:guards\", classConstructor) || [];\n for (const guardClass of guards) {\n const guardInstance = new (guardClass as any)();\n if (typeof guardInstance.run === \"function\") {\n const guardResult = await guardInstance.run(ctx);\n if (!guardResult) {\n if (typeof guardInstance.guardFail === \"function\") {\n await guardInstance.guardFail(ctx);\n } else {\n console.error(\"[Stoatx] Guard check failed but no guardFail method defined on\", guardClass.name);\n }\n return false;\n }\n }\n }\n\n // Cooldown check\n if (!(await this.cooldownManager.check(ctx, metadata))) {\n const remaining = await this.cooldownManager.getRemaining(ctx, metadata);\n\n // For method-based commands, check if instance has onCooldown\n if (typeof (instance as any).onCooldown === \"function\") {\n await (instance as any).onCooldown(ctx, remaining);\n } else {\n await ctx.reply(`Please wait ${(remaining / 1000).toFixed(1)} seconds before using this command again.`);\n }\n return false;\n }\n\n try {\n // Set cooldown before execution to prevent concurrent executions\n if (metadata.cooldown > 0) {\n await this.cooldownManager.set(ctx, metadata);\n }\n\n await (instance as any)[methodName](ctx);\n\n return true;\n } catch (error) {\n // Handle errors\n if (typeof (instance as any).onError === \"function\") {\n await (instance as any).onError(ctx, error as Error);\n } else {\n console.error(`[Stoatx] Error in command ${metadata.name}:`, error);\n }\n return false;\n }\n }\n\n /**\n * Get the command registry\n */\n getRegistry(): CommandRegistry {\n return this.registry;\n }\n\n /**\n * Get a command by name or alias\n */\n getCommand(name: string): RegisteredCommand | undefined {\n return this.registry.get(name);\n }\n\n /**\n * Get all commands\n */\n getCommands(): RegisteredCommand[] {\n return this.registry.getAll();\n }\n\n /**\n * Reload all commands\n */\n async reload(): Promise<void> {\n this.registry.clear();\n if (this.cooldownManager.clear) {\n await this.cooldownManager.clear();\n }\n if (this.commandsDir) {\n await this.registry.loadFromDirectory(this.commandsDir);\n return;\n }\n\n await this.registry.autoDiscover(this.discoveryOptions);\n }\n\n /**\n * Check if a user is an owner\n */\n isOwner(userId: string): boolean {\n return this.owners.has(userId);\n }\n\n /**\n * Add an owner\n */\n addOwner(userId: string): void {\n this.owners.add(userId);\n }\n\n /**\n * Remove an owner\n */\n removeOwner(userId: string): void {\n this.owners.delete(userId);\n }\n\n /**\n * Resolve the prefix for a context\n */\n private async resolvePrefix(serverId?: string | undefined): Promise<string> {\n if (typeof this.prefixResolver === \"function\") {\n return this.prefixResolver({ serverId });\n }\n return this.prefixResolver;\n }\n}\n"],"mappings":"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","names":["import_reflect_metadata","import_reflect_metadata","import_reflect_metadata","relative","import_reflect_metadata","StoatClient"]}
|
package/dist/index.mjs
CHANGED
|
@@ -152,6 +152,7 @@ function buildSimpleCommandMetadata(options, methodName, category) {
|
|
|
152
152
|
permissions: options.permissions ?? [],
|
|
153
153
|
category: options.category ?? category ?? "uncategorized",
|
|
154
154
|
cooldown: options.cooldown ?? 0,
|
|
155
|
+
...options.cooldownStorage !== void 0 ? { cooldownStorage: options.cooldownStorage } : {},
|
|
155
156
|
nsfw: options.nsfw ?? false,
|
|
156
157
|
ownerOnly: options.ownerOnly ?? false
|
|
157
158
|
};
|
|
@@ -427,6 +428,38 @@ var CommandRegistry = class _CommandRegistry {
|
|
|
427
428
|
// src/handler.ts
|
|
428
429
|
import "reflect-metadata";
|
|
429
430
|
import { Client as StoatClient } from "@stoatx/client";
|
|
431
|
+
var DefaultCooldownManager = class {
|
|
432
|
+
cooldowns = /* @__PURE__ */ new Map();
|
|
433
|
+
check(ctx, metadata) {
|
|
434
|
+
if (metadata.cooldown <= 0) return true;
|
|
435
|
+
const commandCooldowns = this.cooldowns.get(metadata.name);
|
|
436
|
+
if (!commandCooldowns) return true;
|
|
437
|
+
const expirationTime = commandCooldowns.get(ctx.authorId);
|
|
438
|
+
if (!expirationTime) return true;
|
|
439
|
+
if (Date.now() > expirationTime) {
|
|
440
|
+
commandCooldowns.delete(ctx.authorId);
|
|
441
|
+
return true;
|
|
442
|
+
}
|
|
443
|
+
return false;
|
|
444
|
+
}
|
|
445
|
+
getRemaining(ctx, metadata) {
|
|
446
|
+
const commandCooldowns = this.cooldowns.get(metadata.name);
|
|
447
|
+
if (!commandCooldowns) return 0;
|
|
448
|
+
const userCooldown = commandCooldowns.get(ctx.authorId);
|
|
449
|
+
if (!userCooldown) return 0;
|
|
450
|
+
return Math.max(0, userCooldown - Date.now());
|
|
451
|
+
}
|
|
452
|
+
set(ctx, metadata) {
|
|
453
|
+
if (!this.cooldowns.has(metadata.name)) {
|
|
454
|
+
this.cooldowns.set(metadata.name, /* @__PURE__ */ new Map());
|
|
455
|
+
}
|
|
456
|
+
const commandCooldowns = this.cooldowns.get(metadata.name);
|
|
457
|
+
commandCooldowns.set(ctx.authorId, Date.now() + metadata.cooldown);
|
|
458
|
+
}
|
|
459
|
+
clear() {
|
|
460
|
+
this.cooldowns.clear();
|
|
461
|
+
}
|
|
462
|
+
};
|
|
430
463
|
var Client = class extends StoatClient {
|
|
431
464
|
handler;
|
|
432
465
|
constructor(options) {
|
|
@@ -449,7 +482,7 @@ var StoatxHandler = class {
|
|
|
449
482
|
prefixResolver;
|
|
450
483
|
owners;
|
|
451
484
|
registry;
|
|
452
|
-
|
|
485
|
+
cooldownManager;
|
|
453
486
|
disableMentionPrefix;
|
|
454
487
|
client;
|
|
455
488
|
constructor(options) {
|
|
@@ -460,6 +493,7 @@ var StoatxHandler = class {
|
|
|
460
493
|
this.owners = new Set(options.owners ?? []);
|
|
461
494
|
this.registry = new CommandRegistry(options.extensions);
|
|
462
495
|
this.disableMentionPrefix = options.disableMentionPrefix ?? false;
|
|
496
|
+
this.cooldownManager = options.cooldownManager ?? new DefaultCooldownManager();
|
|
463
497
|
}
|
|
464
498
|
/**
|
|
465
499
|
* Initialize the handler - load all commands
|
|
@@ -622,8 +656,8 @@ var StoatxHandler = class {
|
|
|
622
656
|
}
|
|
623
657
|
}
|
|
624
658
|
}
|
|
625
|
-
if (!this.
|
|
626
|
-
const remaining = this.
|
|
659
|
+
if (!await this.cooldownManager.check(ctx, metadata)) {
|
|
660
|
+
const remaining = await this.cooldownManager.getRemaining(ctx, metadata);
|
|
627
661
|
if (typeof instance.onCooldown === "function") {
|
|
628
662
|
await instance.onCooldown(ctx, remaining);
|
|
629
663
|
} else {
|
|
@@ -632,10 +666,10 @@ var StoatxHandler = class {
|
|
|
632
666
|
return false;
|
|
633
667
|
}
|
|
634
668
|
try {
|
|
635
|
-
await instance[methodName](ctx);
|
|
636
669
|
if (metadata.cooldown > 0) {
|
|
637
|
-
this.
|
|
670
|
+
await this.cooldownManager.set(ctx, metadata);
|
|
638
671
|
}
|
|
672
|
+
await instance[methodName](ctx);
|
|
639
673
|
return true;
|
|
640
674
|
} catch (error) {
|
|
641
675
|
if (typeof instance.onError === "function") {
|
|
@@ -669,7 +703,9 @@ var StoatxHandler = class {
|
|
|
669
703
|
*/
|
|
670
704
|
async reload() {
|
|
671
705
|
this.registry.clear();
|
|
672
|
-
this.
|
|
706
|
+
if (this.cooldownManager.clear) {
|
|
707
|
+
await this.cooldownManager.clear();
|
|
708
|
+
}
|
|
673
709
|
if (this.commandsDir) {
|
|
674
710
|
await this.registry.loadFromDirectory(this.commandsDir);
|
|
675
711
|
return;
|
|
@@ -703,37 +739,6 @@ var StoatxHandler = class {
|
|
|
703
739
|
}
|
|
704
740
|
return this.prefixResolver;
|
|
705
741
|
}
|
|
706
|
-
/**
|
|
707
|
-
* Check if user is on cooldown
|
|
708
|
-
*/
|
|
709
|
-
checkCooldown(userId, metadata) {
|
|
710
|
-
if (metadata.cooldown <= 0) return true;
|
|
711
|
-
const commandCooldowns = this.cooldowns.get(metadata.name);
|
|
712
|
-
if (!commandCooldowns) return true;
|
|
713
|
-
const userCooldown = commandCooldowns.get(userId);
|
|
714
|
-
if (!userCooldown) return true;
|
|
715
|
-
return Date.now() >= userCooldown;
|
|
716
|
-
}
|
|
717
|
-
/**
|
|
718
|
-
* Get remaining cooldown time in ms
|
|
719
|
-
*/
|
|
720
|
-
getRemainingCooldown(userId, metadata) {
|
|
721
|
-
const commandCooldowns = this.cooldowns.get(metadata.name);
|
|
722
|
-
if (!commandCooldowns) return 0;
|
|
723
|
-
const userCooldown = commandCooldowns.get(userId);
|
|
724
|
-
if (!userCooldown) return 0;
|
|
725
|
-
return Math.max(0, userCooldown - Date.now());
|
|
726
|
-
}
|
|
727
|
-
/**
|
|
728
|
-
* Set cooldown for a user
|
|
729
|
-
*/
|
|
730
|
-
setCooldown(userId, metadata) {
|
|
731
|
-
if (!this.cooldowns.has(metadata.name)) {
|
|
732
|
-
this.cooldowns.set(metadata.name, /* @__PURE__ */ new Map());
|
|
733
|
-
}
|
|
734
|
-
const commandCooldowns = this.cooldowns.get(metadata.name);
|
|
735
|
-
commandCooldowns.set(userId, Date.now() + metadata.cooldown);
|
|
736
|
-
}
|
|
737
742
|
};
|
|
738
743
|
|
|
739
744
|
// src/index.ts
|
|
@@ -741,6 +746,7 @@ export * from "@stoatx/client";
|
|
|
741
746
|
export {
|
|
742
747
|
Client,
|
|
743
748
|
CommandRegistry,
|
|
749
|
+
DefaultCooldownManager,
|
|
744
750
|
Guard,
|
|
745
751
|
METADATA_KEYS,
|
|
746
752
|
On,
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/decorators/Stoat.ts","../src/decorators/keys.ts","../src/decorators/store.ts","../src/decorators/SimpleCommand.ts","../src/decorators/Guard.ts","../src/decorators/Events.ts","../src/decorators/utils.ts","../src/registry.ts","../src/handler.ts","../src/index.ts"],"sourcesContent":["import \"reflect-metadata\";\nimport { METADATA_KEYS } from \"./keys\";\nimport { decoratorStore } from \"./store\";\n\n/**\n * @Stoat\n * Marks a class as a Stoat command container.\n * Use this decorator on classes that contain @SimpleCommand methods.\n *\n * @example\n * ```ts\n * import { Stoat, SimpleCommand, CommandContext } from 'stoatx';\n *\n * @Stoat()\n * class ModerationCommands {\n * @SimpleCommand({ name: 'ban', description: 'Ban a user' })\n * async ban(ctx: CommandContext) {\n * await ctx.reply('User banned!');\n * }\n *\n * @SimpleCommand({ name: 'kick', description: 'Kick a user' })\n * async kick(ctx: CommandContext) {\n * await ctx.reply('User kicked!');\n * }\n * }\n * ```\n */\nexport function Stoat(): ClassDecorator {\n return (target: Function) => {\n Reflect.defineMetadata(METADATA_KEYS.IS_STOAT_CLASS, true, target);\n decoratorStore.registerStoatClass(target);\n };\n}\n\n/**\n * Check if a class is decorated with @Stoat\n */\nexport function isStoatClass(target: Function): boolean {\n return Reflect.getMetadata(METADATA_KEYS.IS_STOAT_CLASS, target) === true;\n}\n","/**\n * Metadata keys used by decorators\n */\nexport const METADATA_KEYS = {\n IS_STOAT_CLASS: Symbol(\"stoatx:stoat:isClass\"),\n SIMPLE_COMMANDS: Symbol(\"stoatx:stoat:simpleCommands\"),\n GUARDS: \"stoatx:command:guards\",\n EVENTS: Symbol(\"stoatx:stoat:events\"),\n} as const;\n","import type { RegisteredCommand } from \"../registry\";\n\n/**\n * Global store for all decorated classes and commands\n * This allows automatic registration without directory scanning\n */\nexport class DecoratorStore {\n private static instance: DecoratorStore;\n\n /** Stoat classes with their SimpleCommand methods */\n private stoatClasses: Map<Function, object> = new Map();\n\n /** Registered commands from @Stoat/@SimpleCommand decorators */\n private commands: RegisteredCommand[] = [];\n\n /** Whether the store has been initialized */\n private initialized = false;\n\n private constructor() {}\n\n static getInstance(): DecoratorStore {\n if (!DecoratorStore.instance) {\n DecoratorStore.instance = new DecoratorStore();\n }\n return DecoratorStore.instance;\n }\n\n /**\n * Register a @Stoat decorated class\n */\n registerStoatClass(classConstructor: Function): void {\n if (!this.stoatClasses.has(classConstructor)) {\n // Create instance immediately when decorated\n const instance = new (classConstructor as new () => object)();\n this.stoatClasses.set(classConstructor, instance);\n }\n }\n\n /**\n * Get all registered Stoat classes with their instances\n */\n getStoatClasses(): Map<Function, object> {\n return this.stoatClasses;\n }\n\n /**\n * Add a registered command\n */\n addCommand(command: RegisteredCommand): void {\n this.commands.push(command);\n }\n\n /**\n * Get all registered commands\n */\n getCommands(): RegisteredCommand[] {\n return this.commands;\n }\n\n /**\n * Clear all registered classes (useful for testing)\n */\n clear(): void {\n this.stoatClasses.clear();\n this.commands = [];\n this.initialized = false;\n }\n\n /**\n * Mark as initialized\n */\n markInitialized(): void {\n this.initialized = true;\n }\n\n /**\n * Check if initialized\n */\n isInitialized(): boolean {\n return this.initialized;\n }\n}\n\nexport const decoratorStore = DecoratorStore.getInstance();\n","import \"reflect-metadata\";\nimport type { SimpleCommandOptions } from \"../types\";\nimport { METADATA_KEYS } from \"./keys\";\n\n/**\n * Stored simple command metadata from method decorator\n */\nexport interface SimpleCommandDefinition {\n methodName: string;\n options: SimpleCommandOptions;\n}\n\n/**\n * @SimpleCommand\n * Marks a method as a simple command within a @Stoat() decorated class.\n *\n * @example\n * ```ts\n * @Stoat()\n * class Example {\n * @SimpleCommand({ name: 'ping', description: 'Replies with Pong!' })\n * async ping(ctx: CommandContext) {\n * await ctx.reply('Pong!');\n * }\n *\n * @SimpleCommand({ aliases: ['perm'], name: 'permission' })\n * async permission(ctx: CommandContext) {\n * await ctx.reply('Access granted');\n * }\n * }\n * ```\n */\nexport function SimpleCommand(options: SimpleCommandOptions = {}): MethodDecorator {\n return (target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {\n const constructor = target.constructor;\n\n // Get existing simple commands or create new array\n const existingCommands: SimpleCommandDefinition[] =\n Reflect.getMetadata(METADATA_KEYS.SIMPLE_COMMANDS, constructor) || [];\n\n // Add this command definition\n existingCommands.push({\n methodName: String(propertyKey),\n options,\n });\n\n Reflect.defineMetadata(METADATA_KEYS.SIMPLE_COMMANDS, existingCommands, constructor);\n\n return descriptor;\n };\n}\n\n/**\n * Get all simple command definitions from a @Stoat class\n */\nexport function getSimpleCommands(target: Function): SimpleCommandDefinition[] {\n return Reflect.getMetadata(METADATA_KEYS.SIMPLE_COMMANDS, target) || [];\n}\n","import \"reflect-metadata\";\nimport { METADATA_KEYS } from \"./keys\";\n\n/**\n * @Guard\n * Runs before a command to check if it should execute.\n * Should return true to allow execution, false to block.\n * Applied on @Stoat classes to guard all contained @SimpleCommand methods.\n *\n * @example\n * ```ts\n * import { Guard, Stoat, SimpleCommand, CommandContext } from 'stoatx';\n *\n * // Define a guard\n * class NotBot implements StoatxGuard {\n * run(ctx: CommandContext): boolean {\n * return !ctx.message.author.bot;\n * }\n *\n * guardFail(ctx: CommandContext): void {\n * ctx.reply(\"Bots cannot use this command!\");\n * }\n * }\n *\n * @Stoat()\n * @Guard(NotBot)\n * class AdminCommands {\n * @SimpleCommand({ name: 'admin', description: 'Admin only command' })\n * async admin(ctx: CommandContext) {\n * ctx.reply(\"You passed the guard check!\");\n * }\n * }\n * ```\n */\nexport function Guard(guardClass: Function): ClassDecorator {\n return (target: Function) => {\n const existingGuards: Function[] = Reflect.getMetadata(METADATA_KEYS.GUARDS, target) || [];\n existingGuards.push(guardClass);\n Reflect.defineMetadata(METADATA_KEYS.GUARDS, existingGuards, target);\n };\n}\n\n/**\n * Get all guards from a decorated class\n */\nexport function getGuards(target: Function): Function[] {\n return Reflect.getMetadata(METADATA_KEYS.GUARDS, target) || [];\n}\n","import \"reflect-metadata\";\nimport { METADATA_KEYS } from \"./keys\";\n\nexport interface EventDefinition {\n methodName: string;\n event: string;\n type: \"on\" | \"once\";\n}\n\nfunction createEventDecorator(event: string, type: \"on\" | \"once\"): MethodDecorator {\n return (target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {\n const constructor = target.constructor;\n\n // Retrieve existing events or initialize a fresh array\n const existingEvents: EventDefinition[] = Reflect.getMetadata(METADATA_KEYS.EVENTS, constructor) || [];\n\n existingEvents.push({\n methodName: String(propertyKey),\n event,\n type,\n });\n\n Reflect.defineMetadata(METADATA_KEYS.EVENTS, existingEvents, constructor);\n\n return descriptor;\n };\n}\n\n/**\n * @On\n * Triggered on every occurrence of the event.\n * Marks a method to be executed whenever the specified client event is emitted.\n *\n * @example\n * ```ts\n * import { Stoat, On } from 'stoatx';\n * import { Message, Client } from 'stoat.js';\n *\n * @Stoat()\n * class BotEvents {\n * @On('messageCreate')\n * async onMessage(message: Message, client: Client) {\n * console.log('New message received:', message.content);\n * }\n * }\n * ```\n *\n * @param event The name of the client event to listen to\n */\nexport function On(event: string): MethodDecorator {\n return createEventDecorator(event, \"on\");\n}\n\n/**\n * @Once\n * Triggered only fully once.\n * Marks a method to be executed only the FIRST time the specified client event is emitted.\n *\n * @example\n * ```ts\n * import { Stoat, Once } from 'stoatx';\n * import { Client } from 'stoat.js';\n *\n * @Stoat()\n * class BotEvents {\n * @Once('ready')\n * async onReady(client: Client) {\n * console.log('Bot successfully started and logged in!');\n * }\n * }\n * ```\n *\n * @param event The name of the client event to listen to\n */\nexport function Once(event: string): MethodDecorator {\n return createEventDecorator(event, \"once\");\n}\n\n/**\n * Get all event definitions from a @Stoat class\n */\nexport function getEventsMetadata(target: Function): EventDefinition[] {\n return Reflect.getMetadata(METADATA_KEYS.EVENTS, target) || [];\n}\n","import type { CommandMetadata, SimpleCommandOptions } from \"../types\";\n\n/**\n * Build CommandMetadata from SimpleCommandOptions\n */\nexport function buildSimpleCommandMetadata(\n options: SimpleCommandOptions,\n methodName: string,\n category?: string,\n): CommandMetadata {\n return {\n name: options.name ?? methodName.toLowerCase(),\n description: options.description ?? \"No description provided\",\n aliases: options.aliases ?? [],\n permissions: options.permissions ?? [],\n category: options.category ?? category ?? \"uncategorized\",\n cooldown: options.cooldown ?? 0,\n nsfw: options.nsfw ?? false,\n ownerOnly: options.ownerOnly ?? false,\n };\n}\n","import * as path from \"node:path\";\nimport * as fs from \"node:fs/promises\";\nimport { pathToFileURL } from \"node:url\";\nimport { glob } from \"tinyglobby\";\nimport { buildSimpleCommandMetadata, getSimpleCommands, getEventsMetadata } from \"./decorators\";\nimport { decoratorStore } from \"./decorators/store\";\nimport type { CommandMetadata } from \"./types\";\n\ninterface AutoDiscoveryOptions {\n roots?: string[];\n include?: string[];\n ignore?: string[];\n}\n\n/**\n * Stored command entry from @Stoat/@SimpleCommand registration.\n */\nexport interface RegisteredCommand {\n /** Instance of the @Stoat class */\n instance: object;\n /** Command metadata */\n metadata: CommandMetadata;\n /** Method name to call */\n methodName: string;\n /** The original class constructor (for guard validation) */\n classConstructor: Function;\n}\n\n/**\n * Stored event entry from @On/@Once registration.\n */\nexport interface RegisteredEvent {\n instance: object;\n methodName: string;\n event: string;\n type: \"on\" | \"once\";\n}\n\n/**\n * CommandRegistry - Scans directories and stores commands in a Map\n *\n * @example\n * ```ts\n * const registry = new CommandRegistry();\n * await registry.loadFromDirectory('./src/commands');\n *\n * const ping = registry.get('ping');\n * const allCommands = registry.getAll();\n * ```\n */\nexport class CommandRegistry {\n private static readonly DEFAULT_AUTO_DISCOVERY_IGNORES = [\n \"**/node_modules/**\",\n \"**/.git/**\",\n \"**/*.d.ts\",\n \"**/*.test.*\",\n \"**/*.spec.*\",\n ];\n\n private readonly commands: Map<string, RegisteredCommand> = new Map();\n private readonly aliases: Map<string, string> = new Map();\n private readonly registeredEvents: RegisteredEvent[] = [];\n private readonly extensions: string[];\n private readonly processedStoatClasses: Set<Function> = new Set();\n\n constructor(extensions: string[] = [\".js\", \".mjs\", \".cjs\"]) {\n this.extensions = extensions;\n }\n\n /**\n * Get the number of registered commands\n */\n get size(): number {\n return this.commands.size;\n }\n\n /**\n * Load commands from a directory using glob pattern matching\n */\n async loadFromDirectory(directory: string): Promise<void> {\n const patterns = this.extensions.map((ext) => path.join(directory, \"**\", `*${ext}`).replace(/\\\\/g, \"/\"));\n\n for (const pattern of patterns) {\n const files = await glob(pattern, {\n ignore: [\"**/*.d.ts\", \"**/*.test.ts\", \"**/*.spec.ts\"],\n absolute: true,\n });\n\n for (const file of files) {\n await this.loadFile(file, directory);\n }\n }\n\n console.log(`[Stoatx] Loaded ${this.commands.size} command(s) and ${this.registeredEvents.length} event(s)`);\n }\n\n /**\n * Auto-discover command files across one or more roots.\n */\n async autoDiscover(options: AutoDiscoveryOptions = {}): Promise<void> {\n const roots = options.roots?.length ? options.roots : [process.cwd()];\n const includePatterns = options.include?.length ? options.include : this.getDefaultAutoDiscoveryPatterns();\n\n const patterns = roots.flatMap((root) =>\n includePatterns.map((pattern) => path.join(root, pattern).replace(/\\\\/g, \"/\")),\n );\n\n const files = await glob(patterns, {\n ignore: [...CommandRegistry.DEFAULT_AUTO_DISCOVERY_IGNORES, ...(options.ignore ?? [])],\n absolute: true,\n });\n\n const uniqueFiles = [...new Set(files)];\n let candidateFiles = 0;\n for (const file of uniqueFiles) {\n if (!(await this.isLikelyCommandModule(file))) {\n continue;\n }\n candidateFiles++;\n\n const baseDir =\n roots.find((root) => {\n const relative = path.relative(root, file);\n return relative && !relative.startsWith(\"..\") && !path.isAbsolute(relative);\n }) ?? roots[0]!;\n await this.loadFile(file, baseDir);\n }\n\n console.log(`[Stoatx] Loaded ${this.commands.size} command(s) and ${this.registeredEvents.length} event(s)`);\n }\n\n private getDefaultAutoDiscoveryPatterns(): string[] {\n // discordx-like default: scan broadly, then register only decorated classes\n return this.extensions.map((ext) => `**/*${ext}`);\n }\n\n private async isLikelyCommandModule(filePath: string): Promise<boolean> {\n try {\n const source = await fs.readFile(filePath, \"utf8\");\n return source.includes(\"Stoat\") || source.includes(\"SimpleCommand\") || source.includes(\"stoatx:command\");\n } catch {\n // If the file can't be pre-read, fall back to attempting import.\n return true;\n }\n }\n\n /**\n * Register a command instance\n */\n register(instance: object, metadata: CommandMetadata, classConstructor: Function, methodName: string): void {\n const name = metadata.name.toLowerCase();\n\n if (this.commands.has(name)) {\n console.warn(`[Stoatx] Duplicate command name: ${name}. Skipping...`);\n return;\n }\n\n this.validateGuards(classConstructor, metadata.name);\n\n this.commands.set(name, { instance, metadata, methodName, classConstructor });\n\n for (const alias of metadata.aliases) {\n const aliasLower = alias.toLowerCase();\n if (this.aliases.has(aliasLower) || this.commands.has(aliasLower)) {\n console.warn(`[Stoatx] Duplicate alias: ${aliasLower}. Skipping...`);\n continue;\n }\n this.aliases.set(aliasLower, name);\n }\n }\n\n /**\n * Get a command by name or alias\n */\n get(name: string): RegisteredCommand | undefined {\n const lowerName = name.toLowerCase();\n const resolvedName = this.aliases.get(lowerName) ?? lowerName;\n return this.commands.get(resolvedName);\n }\n\n /**\n * Check if a command exists\n */\n has(name: string): boolean {\n const lowerName = name.toLowerCase();\n return this.commands.has(lowerName) || this.aliases.has(lowerName);\n }\n\n /**\n * Get all registered commands\n */\n getAll(): RegisteredCommand[] {\n return Array.from(this.commands.values());\n }\n\n /**\n * Get all command metadata\n */\n getAllMetadata(): CommandMetadata[] {\n return this.getAll().map((c) => c.metadata);\n }\n\n /**\n * Get all registered events\n */\n getEvents(): RegisteredEvent[] {\n return this.registeredEvents;\n }\n\n /**\n * Get commands grouped by category\n */\n getByCategory(): Map<string, RegisteredCommand[]> {\n const categories = new Map<string, RegisteredCommand[]>();\n\n for (const cmd of this.commands.values()) {\n const category = cmd.metadata.category;\n const existing = categories.get(category) ?? [];\n existing.push(cmd);\n categories.set(category, existing);\n }\n\n return categories;\n }\n\n /**\n * Clear all commands\n */\n clear(): void {\n this.commands.clear();\n this.aliases.clear();\n this.registeredEvents.length = 0;\n this.processedStoatClasses.clear();\n }\n\n /**\n * Iterate over commands\n */\n [Symbol.iterator](): IterableIterator<[string, RegisteredCommand]> {\n return this.commands.entries();\n }\n\n /**\n * Iterate over command values\n */\n values(): IterableIterator<RegisteredCommand> {\n return this.commands.values();\n }\n\n /**\n * Iterate over command names\n */\n keys(): IterableIterator<string> {\n return this.commands.keys();\n }\n\n /**\n * Validate that all guards on a command implement the required methods\n * @param commandClass\n * @param commandName\n * @private\n */\n private validateGuards(commandClass: Function, commandName: string): void {\n const guards: Function[] = Reflect.getMetadata(\"stoatx:command:guards\", commandClass) || [];\n\n for (const GuardClass of guards) {\n const guardInstance = new (GuardClass as any)();\n\n if (typeof guardInstance.run !== \"function\") {\n console.error(\n `[Stoatx] FATAL: Guard \"${GuardClass.name}\" on command \"${commandName}\" does not have a run() method.`,\n );\n process.exit(1);\n }\n\n if (typeof guardInstance.guardFail !== \"function\") {\n console.error(\n `[Stoatx] FATAL: Guard \"${GuardClass.name}\" on command \"${commandName}\" does not have a guardFail() method.`,\n );\n console.error(`[Stoatx] All guards must implement guardFail() to handle failed checks.`);\n process.exit(1);\n }\n }\n }\n\n /**\n * Load commands from a single file\n */\n private async loadFile(filePath: string, baseDir: string): Promise<void> {\n try {\n const knownStoatClasses = new Set(decoratorStore.getStoatClasses().keys());\n const fileUrl = pathToFileURL(filePath).href;\n await import(fileUrl);\n\n const allStoatClasses = decoratorStore.getStoatClasses();\n for (const [stoatClass, stoatInstance] of allStoatClasses.entries()) {\n if (knownStoatClasses.has(stoatClass) || this.processedStoatClasses.has(stoatClass)) {\n continue;\n }\n this.registerStoatClassCommands(stoatClass, stoatInstance, filePath, baseDir);\n }\n } catch (error) {\n console.error(`[Stoatx] Failed to load command file: ${filePath}`, error);\n }\n }\n\n private registerStoatClassCommands(stoatClass: Function, instance: object, filePath: string, baseDir: string): void {\n const simpleCommands = getSimpleCommands(stoatClass);\n const events = getEventsMetadata(stoatClass);\n const category = this.getCategoryFromPath(filePath, baseDir);\n\n if (simpleCommands.length === 0 && events.length === 0) {\n console.warn(\n `[Stoatx] Class ${stoatClass.name} is decorated with @Stoat but has no @SimpleCommand, @On or @Once methods. Skipping...`,\n );\n this.processedStoatClasses.add(stoatClass);\n return;\n }\n\n for (const cmdDef of simpleCommands) {\n const method = (instance as any)[cmdDef.methodName];\n if (typeof method !== \"function\") {\n console.warn(`[Stoatx] Method ${cmdDef.methodName} not found on ${stoatClass.name}. Skipping...`);\n continue;\n }\n\n const metadata = buildSimpleCommandMetadata(cmdDef.options, cmdDef.methodName, category);\n this.register(instance, metadata, stoatClass, cmdDef.methodName);\n }\n\n for (const eventDef of events) {\n const method = (instance as any)[eventDef.methodName];\n if (typeof method !== \"function\") {\n console.warn(`[Stoatx] Method ${eventDef.methodName} not found on ${stoatClass.name}. Skipping...`);\n continue;\n }\n\n this.registeredEvents.push({\n instance,\n methodName: eventDef.methodName,\n event: eventDef.event,\n type: eventDef.type,\n });\n }\n\n this.processedStoatClasses.add(stoatClass);\n }\n\n /**\n * Derive category from file path relative to base directory\n */\n private getCategoryFromPath(filePath: string, baseDir: string): string | undefined {\n const relative = path.relative(baseDir, filePath);\n const parts = relative.split(path.sep);\n\n if (parts.length > 1) {\n return parts[0];\n }\n\n return undefined;\n }\n}\n","import \"reflect-metadata\";\nimport { CommandRegistry, RegisteredCommand } from \"./registry\";\nimport type { CommandContext, CommandMetadata, StoatxDiscoveryOptions, StoatxHandlerOptions } from \"./types\";\nimport { Client as StoatClient, ClientEvents, Message } from \"@stoatx/client\";\n\n/**\n * Client - An extended Client that integrates StoatxHandler directly\n *\n * @example\n * ```ts\n * import { Client } from 'stoatx';\n *\n * const client = new Client({\n * prefix: '!',\n * owners: ['owner-user-id'],\n * });\n *\n * await client.initCommands();\n * ```\n */\nexport class Client extends StoatClient {\n public readonly handler: StoatxHandler;\n\n constructor(options: Omit<StoatxHandlerOptions, \"client\">) {\n super();\n this.handler = new StoatxHandler({ ...options, client: this });\n\n // Automatically hook up the message handler\n this.on(\"messageCreate\", async (message) => {\n await this.handler.handle(message);\n });\n }\n\n /**\n * Initialize the StoatxHandler commands\n */\n async initCommands(): Promise<void> {\n await this.handler.init();\n }\n}\n\n/**\n * StoatxHandler - The execution engine for commands\n *\n * Handles message parsing, middleware execution, and command dispatching\n *\n * @internal This class is not intended to be instantiated directly. Use the `Client` from `stoatx` instead.\n */\nexport class StoatxHandler {\n private readonly commandsDir: string | undefined;\n private readonly discoveryOptions: StoatxDiscoveryOptions | undefined;\n private readonly prefixResolver: string | ((ctx: { serverId?: string | undefined }) => string | Promise<string>);\n private readonly owners: Set<string>;\n private readonly registry: CommandRegistry;\n private readonly cooldowns: Map<string, Map<string, number>> = new Map();\n private readonly disableMentionPrefix: boolean;\n private readonly client: StoatClient;\n constructor(options: StoatxHandlerOptions) {\n this.client = options.client;\n this.commandsDir = options.commandsDir;\n this.discoveryOptions = options.discovery;\n this.prefixResolver = options.prefix;\n this.owners = new Set(options.owners ?? []);\n this.registry = new CommandRegistry(options.extensions);\n this.disableMentionPrefix = options.disableMentionPrefix ?? false;\n }\n\n /**\n * Initialize the handler - load all commands\n */\n async init(): Promise<void> {\n if (this.commandsDir) {\n await this.registry.loadFromDirectory(this.commandsDir);\n } else {\n await this.registry.autoDiscover(this.discoveryOptions);\n }\n\n this.attachEvents();\n }\n\n /**\n * Attach registered events to the client\n */\n private attachEvents(): void {\n const events = this.registry.getEvents();\n\n for (const eventDef of events) {\n const handler = async (...args: any[]) => {\n try {\n await (eventDef.instance as any)[eventDef.methodName](...args, this.client);\n } catch (error) {\n console.error(\n `[Stoatx] Event Handler Error in @${eventDef.type === \"on\" ? \"On\" : \"Once\"}('${eventDef.event}'):`,\n error,\n );\n }\n };\n\n const eventName = eventDef.event as keyof ClientEvents;\n if (eventDef.type === \"once\") {\n this.client.once(eventName, handler);\n } else {\n this.client.on(eventName, handler);\n }\n }\n }\n\n /**\n * Parse a raw message into command context\n */\n async parseMessage(\n rawContent: string,\n message: Message,\n meta: {\n authorId: string;\n channelId: string;\n serverId?: string | undefined;\n reply: (content: string) => Promise<Message>;\n },\n ): Promise<CommandContext | null> {\n const prefix = await this.resolvePrefix(meta.serverId);\n let usedPrefix = prefix;\n let withoutPrefix = \"\";\n\n // Check for string prefix\n if (rawContent.startsWith(prefix)) {\n withoutPrefix = rawContent.slice(prefix.length).trim();\n usedPrefix = prefix;\n }\n // Check for mention prefix (e.g., \"<@bot-id> command\") - unless disabled\n else if (!this.disableMentionPrefix && rawContent.match(/^<@!?[\\w]+>/)) {\n const mentionMatch = rawContent.match(/^<@!?([\\w]+)>\\s*/);\n if (mentionMatch) {\n const mentionedId = mentionMatch[1];\n const botId = this.client.user?.id;\n\n // Only process if mentioned user is the bot\n if (botId && mentionedId === botId) {\n usedPrefix = mentionMatch[0];\n withoutPrefix = rawContent.slice(mentionMatch[0].length).trim();\n } else {\n }\n }\n }\n\n if (!withoutPrefix) {\n return null;\n }\n\n const [commandName, ...args] = withoutPrefix.split(/\\s+/);\n\n if (!commandName) {\n return null;\n }\n\n return {\n client: this.client,\n content: rawContent,\n authorId: meta.authorId,\n channelId: meta.channelId,\n serverId: meta.serverId,\n args,\n prefix: usedPrefix,\n commandName: commandName.toLowerCase(),\n reply: meta.reply,\n message,\n };\n }\n\n /**\n * Handle a message object using the configured message adapter\n *\n * @example\n * ```ts\n * // With message adapter configured\n * client.on('messageCreate', (message) => {\n * handler.handle(message);\n * });\n * ```\n */\n async handle(message: Message): Promise<boolean> {\n if (!message.channel || !message.author) {\n return false;\n }\n\n // Skip messages from bots\n if (message.author.bot) {\n return false;\n }\n\n const rawContent = message.content;\n const authorId = message.author.id;\n const channelId = message.channel.id;\n const serverId = message.server?.id;\n const reply = async (content: string) => {\n return await message.channel!.send(content);\n };\n\n // rawContent won't be null since a command will be invoked with a prefix\n await this.handleMessage(rawContent!, message, {\n authorId,\n channelId,\n serverId,\n reply,\n });\n\n return true;\n }\n\n /**\n * Handle a raw message string with metadata\n *\n * @example\n * ```ts\n * // Manual usage without message adapter\n * client.on('messageCreate', (message) => {\n * handler.handleMessage(message.content, message, {\n * authorId: message.author.id,\n * channelId: message.channel.id,\n * serverId: message.server?.id,\n * reply: (content) => message.channel.sendMessage(content),\n * });\n * });\n * ```\n */\n async handleMessage(\n rawContent: string,\n message: Message,\n meta: {\n authorId: string;\n channelId: string;\n serverId?: string | undefined;\n reply: (content: string) => Promise<Message>;\n },\n ): Promise<void> {\n const ctx = await this.parseMessage(rawContent, message, meta);\n\n if (!ctx) {\n return;\n }\n\n await this.execute(ctx);\n }\n\n /**\n * Execute a command with the given context\n */\n async execute(ctx: CommandContext): Promise<boolean> {\n const registered = this.registry.get(ctx.commandName);\n\n if (!registered) {\n return false;\n }\n\n const { instance, metadata, methodName, classConstructor } = registered;\n\n // Owner-only check\n if (metadata.ownerOnly && !this.owners.has(ctx.authorId)) {\n await ctx.reply(\"This command is owner-only.\");\n return false;\n }\n\n // Guard checks - use classConstructor for guard metadata\n const guards: Function[] = Reflect.getMetadata(\"stoatx:command:guards\", classConstructor) || [];\n for (const guardClass of guards) {\n const guardInstance = new (guardClass as any)();\n if (typeof guardInstance.run === \"function\") {\n const guardResult = await guardInstance.run(ctx);\n if (!guardResult) {\n if (typeof guardInstance.guardFail === \"function\") {\n await guardInstance.guardFail(ctx);\n } else {\n console.error(\"[Stoatx] Guard check failed but no guardFail method defined on\", guardClass.name);\n }\n return false;\n }\n }\n }\n\n // Cooldown check\n if (!this.checkCooldown(ctx.authorId, metadata)) {\n const remaining = this.getRemainingCooldown(ctx.authorId, metadata);\n\n // For method-based commands, check if instance has onCooldown\n if (typeof (instance as any).onCooldown === \"function\") {\n await (instance as any).onCooldown(ctx, remaining);\n } else {\n await ctx.reply(`Please wait ${(remaining / 1000).toFixed(1)} seconds before using this command again.`);\n }\n return false;\n }\n\n try {\n await (instance as any)[methodName](ctx);\n\n // Set cooldown after successful execution\n if (metadata.cooldown > 0) {\n this.setCooldown(ctx.authorId, metadata);\n }\n\n return true;\n } catch (error) {\n // Handle errors\n if (typeof (instance as any).onError === \"function\") {\n await (instance as any).onError(ctx, error as Error);\n } else {\n console.error(`[Stoatx] Error in command ${metadata.name}:`, error);\n }\n return false;\n }\n }\n\n /**\n * Get the command registry\n */\n getRegistry(): CommandRegistry {\n return this.registry;\n }\n\n /**\n * Get a command by name or alias\n */\n getCommand(name: string): RegisteredCommand | undefined {\n return this.registry.get(name);\n }\n\n /**\n * Get all commands\n */\n getCommands(): RegisteredCommand[] {\n return this.registry.getAll();\n }\n\n /**\n * Reload all commands\n */\n async reload(): Promise<void> {\n this.registry.clear();\n this.cooldowns.clear();\n if (this.commandsDir) {\n await this.registry.loadFromDirectory(this.commandsDir);\n return;\n }\n\n await this.registry.autoDiscover(this.discoveryOptions);\n }\n\n /**\n * Check if a user is an owner\n */\n isOwner(userId: string): boolean {\n return this.owners.has(userId);\n }\n\n /**\n * Add an owner\n */\n addOwner(userId: string): void {\n this.owners.add(userId);\n }\n\n /**\n * Remove an owner\n */\n removeOwner(userId: string): void {\n this.owners.delete(userId);\n }\n\n /**\n * Resolve the prefix for a context\n */\n private async resolvePrefix(serverId?: string | undefined): Promise<string> {\n if (typeof this.prefixResolver === \"function\") {\n return this.prefixResolver({ serverId });\n }\n return this.prefixResolver;\n }\n\n /**\n * Check if user is on cooldown\n */\n private checkCooldown(userId: string, metadata: CommandMetadata): boolean {\n if (metadata.cooldown <= 0) return true;\n\n const commandCooldowns = this.cooldowns.get(metadata.name);\n if (!commandCooldowns) return true;\n\n const userCooldown = commandCooldowns.get(userId);\n if (!userCooldown) return true;\n\n return Date.now() >= userCooldown;\n }\n\n /**\n * Get remaining cooldown time in ms\n */\n private getRemainingCooldown(userId: string, metadata: CommandMetadata): number {\n const commandCooldowns = this.cooldowns.get(metadata.name);\n if (!commandCooldowns) return 0;\n\n const userCooldown = commandCooldowns.get(userId);\n if (!userCooldown) return 0;\n\n return Math.max(0, userCooldown - Date.now());\n }\n\n /**\n * Set cooldown for a user\n */\n private setCooldown(userId: string, metadata: CommandMetadata): void {\n if (!this.cooldowns.has(metadata.name)) {\n this.cooldowns.set(metadata.name, new Map());\n }\n\n const commandCooldowns = this.cooldowns.get(metadata.name)!;\n commandCooldowns.set(userId, Date.now() + metadata.cooldown);\n }\n}\n","// Types\nexport * from \"./types\";\n\n// Decorators\nexport * from \"./decorators\";\n\n// Registry\nexport * from \"./registry\";\n\n// Handler\nexport { Client } from \"./handler\";\nexport type { StoatxHandler } from \"./handler\";\nexport * from \"@stoatx/client\";\n"],"mappings":"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","names":["relative"]}
|
|
1
|
+
{"version":3,"sources":["../src/decorators/Stoat.ts","../src/decorators/keys.ts","../src/decorators/store.ts","../src/decorators/SimpleCommand.ts","../src/decorators/Guard.ts","../src/decorators/Events.ts","../src/decorators/utils.ts","../src/registry.ts","../src/handler.ts","../src/index.ts"],"sourcesContent":["import \"reflect-metadata\";\nimport { METADATA_KEYS } from \"./keys\";\nimport { decoratorStore } from \"./store\";\n\n/**\n * @Stoat\n * Marks a class as a Stoat command container.\n * Use this decorator on classes that contain @SimpleCommand methods.\n *\n * @example\n * ```ts\n * import { Stoat, SimpleCommand, CommandContext } from 'stoatx';\n *\n * @Stoat()\n * class ModerationCommands {\n * @SimpleCommand({ name: 'ban', description: 'Ban a user' })\n * async ban(ctx: CommandContext) {\n * await ctx.reply('User banned!');\n * }\n *\n * @SimpleCommand({ name: 'kick', description: 'Kick a user' })\n * async kick(ctx: CommandContext) {\n * await ctx.reply('User kicked!');\n * }\n * }\n * ```\n */\nexport function Stoat(): ClassDecorator {\n return (target: Function) => {\n Reflect.defineMetadata(METADATA_KEYS.IS_STOAT_CLASS, true, target);\n decoratorStore.registerStoatClass(target);\n };\n}\n\n/**\n * Check if a class is decorated with @Stoat\n */\nexport function isStoatClass(target: Function): boolean {\n return Reflect.getMetadata(METADATA_KEYS.IS_STOAT_CLASS, target) === true;\n}\n","/**\n * Metadata keys used by decorators\n */\nexport const METADATA_KEYS = {\n IS_STOAT_CLASS: Symbol(\"stoatx:stoat:isClass\"),\n SIMPLE_COMMANDS: Symbol(\"stoatx:stoat:simpleCommands\"),\n GUARDS: \"stoatx:command:guards\",\n EVENTS: Symbol(\"stoatx:stoat:events\"),\n} as const;\n","import type { RegisteredCommand } from \"../registry\";\n\n/**\n * Global store for all decorated classes and commands\n * This allows automatic registration without directory scanning\n */\nexport class DecoratorStore {\n private static instance: DecoratorStore;\n\n /** Stoat classes with their SimpleCommand methods */\n private stoatClasses: Map<Function, object> = new Map();\n\n /** Registered commands from @Stoat/@SimpleCommand decorators */\n private commands: RegisteredCommand[] = [];\n\n /** Whether the store has been initialized */\n private initialized = false;\n\n private constructor() {}\n\n static getInstance(): DecoratorStore {\n if (!DecoratorStore.instance) {\n DecoratorStore.instance = new DecoratorStore();\n }\n return DecoratorStore.instance;\n }\n\n /**\n * Register a @Stoat decorated class\n */\n registerStoatClass(classConstructor: Function): void {\n if (!this.stoatClasses.has(classConstructor)) {\n // Create instance immediately when decorated\n const instance = new (classConstructor as new () => object)();\n this.stoatClasses.set(classConstructor, instance);\n }\n }\n\n /**\n * Get all registered Stoat classes with their instances\n */\n getStoatClasses(): Map<Function, object> {\n return this.stoatClasses;\n }\n\n /**\n * Add a registered command\n */\n addCommand(command: RegisteredCommand): void {\n this.commands.push(command);\n }\n\n /**\n * Get all registered commands\n */\n getCommands(): RegisteredCommand[] {\n return this.commands;\n }\n\n /**\n * Clear all registered classes (useful for testing)\n */\n clear(): void {\n this.stoatClasses.clear();\n this.commands = [];\n this.initialized = false;\n }\n\n /**\n * Mark as initialized\n */\n markInitialized(): void {\n this.initialized = true;\n }\n\n /**\n * Check if initialized\n */\n isInitialized(): boolean {\n return this.initialized;\n }\n}\n\nexport const decoratorStore = DecoratorStore.getInstance();\n","import \"reflect-metadata\";\nimport type { SimpleCommandOptions } from \"../types\";\nimport { METADATA_KEYS } from \"./keys\";\n\n/**\n * Stored simple command metadata from method decorator\n */\nexport interface SimpleCommandDefinition {\n methodName: string;\n options: SimpleCommandOptions;\n}\n\n/**\n * @SimpleCommand\n * Marks a method as a simple command within a @Stoat() decorated class.\n *\n * @example\n * ```ts\n * @Stoat()\n * class Example {\n * @SimpleCommand({ name: 'ping', description: 'Replies with Pong!' })\n * async ping(ctx: CommandContext) {\n * await ctx.reply('Pong!');\n * }\n *\n * @SimpleCommand({ aliases: ['perm'], name: 'permission' })\n * async permission(ctx: CommandContext) {\n * await ctx.reply('Access granted');\n * }\n * }\n * ```\n */\nexport function SimpleCommand(options: SimpleCommandOptions = {}): MethodDecorator {\n return (target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {\n const constructor = target.constructor;\n\n // Get existing simple commands or create new array\n const existingCommands: SimpleCommandDefinition[] =\n Reflect.getMetadata(METADATA_KEYS.SIMPLE_COMMANDS, constructor) || [];\n\n // Add this command definition\n existingCommands.push({\n methodName: String(propertyKey),\n options,\n });\n\n Reflect.defineMetadata(METADATA_KEYS.SIMPLE_COMMANDS, existingCommands, constructor);\n\n return descriptor;\n };\n}\n\n/**\n * Get all simple command definitions from a @Stoat class\n */\nexport function getSimpleCommands(target: Function): SimpleCommandDefinition[] {\n return Reflect.getMetadata(METADATA_KEYS.SIMPLE_COMMANDS, target) || [];\n}\n","import \"reflect-metadata\";\nimport { METADATA_KEYS } from \"./keys\";\n\n/**\n * @Guard\n * Runs before a command to check if it should execute.\n * Should return true to allow execution, false to block.\n * Applied on @Stoat classes to guard all contained @SimpleCommand methods.\n *\n * @example\n * ```ts\n * import { Guard, Stoat, SimpleCommand, CommandContext } from 'stoatx';\n *\n * // Define a guard\n * class NotBot implements StoatxGuard {\n * run(ctx: CommandContext): boolean {\n * return !ctx.message.author.bot;\n * }\n *\n * guardFail(ctx: CommandContext): void {\n * ctx.reply(\"Bots cannot use this command!\");\n * }\n * }\n *\n * @Stoat()\n * @Guard(NotBot)\n * class AdminCommands {\n * @SimpleCommand({ name: 'admin', description: 'Admin only command' })\n * async admin(ctx: CommandContext) {\n * ctx.reply(\"You passed the guard check!\");\n * }\n * }\n * ```\n */\nexport function Guard(guardClass: Function): ClassDecorator {\n return (target: Function) => {\n const existingGuards: Function[] = Reflect.getMetadata(METADATA_KEYS.GUARDS, target) || [];\n existingGuards.push(guardClass);\n Reflect.defineMetadata(METADATA_KEYS.GUARDS, existingGuards, target);\n };\n}\n\n/**\n * Get all guards from a decorated class\n */\nexport function getGuards(target: Function): Function[] {\n return Reflect.getMetadata(METADATA_KEYS.GUARDS, target) || [];\n}\n","import \"reflect-metadata\";\nimport { METADATA_KEYS } from \"./keys\";\n\nexport interface EventDefinition {\n methodName: string;\n event: string;\n type: \"on\" | \"once\";\n}\n\nfunction createEventDecorator(event: string, type: \"on\" | \"once\"): MethodDecorator {\n return (target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {\n const constructor = target.constructor;\n\n // Retrieve existing events or initialize a fresh array\n const existingEvents: EventDefinition[] = Reflect.getMetadata(METADATA_KEYS.EVENTS, constructor) || [];\n\n existingEvents.push({\n methodName: String(propertyKey),\n event,\n type,\n });\n\n Reflect.defineMetadata(METADATA_KEYS.EVENTS, existingEvents, constructor);\n\n return descriptor;\n };\n}\n\n/**\n * @On\n * Triggered on every occurrence of the event.\n * Marks a method to be executed whenever the specified client event is emitted.\n *\n * @example\n * ```ts\n * import { Stoat, On } from 'stoatx';\n * import { Message, Client } from 'stoat.js';\n *\n * @Stoat()\n * class BotEvents {\n * @On('messageCreate')\n * async onMessage(message: Message, client: Client) {\n * console.log('New message received:', message.content);\n * }\n * }\n * ```\n *\n * @param event The name of the client event to listen to\n */\nexport function On(event: string): MethodDecorator {\n return createEventDecorator(event, \"on\");\n}\n\n/**\n * @Once\n * Triggered only fully once.\n * Marks a method to be executed only the FIRST time the specified client event is emitted.\n *\n * @example\n * ```ts\n * import { Stoat, Once } from 'stoatx';\n * import { Client } from 'stoat.js';\n *\n * @Stoat()\n * class BotEvents {\n * @Once('ready')\n * async onReady(client: Client) {\n * console.log('Bot successfully started and logged in!');\n * }\n * }\n * ```\n *\n * @param event The name of the client event to listen to\n */\nexport function Once(event: string): MethodDecorator {\n return createEventDecorator(event, \"once\");\n}\n\n/**\n * Get all event definitions from a @Stoat class\n */\nexport function getEventsMetadata(target: Function): EventDefinition[] {\n return Reflect.getMetadata(METADATA_KEYS.EVENTS, target) || [];\n}\n","import type { CommandMetadata, SimpleCommandOptions } from \"../types\";\n\n/**\n * Build CommandMetadata from SimpleCommandOptions\n */\nexport function buildSimpleCommandMetadata(\n options: SimpleCommandOptions,\n methodName: string,\n category?: string,\n): CommandMetadata {\n return {\n name: options.name ?? methodName.toLowerCase(),\n description: options.description ?? \"No description provided\",\n aliases: options.aliases ?? [],\n permissions: options.permissions ?? [],\n category: options.category ?? category ?? \"uncategorized\",\n cooldown: options.cooldown ?? 0,\n ...(options.cooldownStorage !== undefined ? { cooldownStorage: options.cooldownStorage } : {}),\n nsfw: options.nsfw ?? false,\n ownerOnly: options.ownerOnly ?? false,\n };\n}\n","import * as path from \"node:path\";\nimport * as fs from \"node:fs/promises\";\nimport { pathToFileURL } from \"node:url\";\nimport { glob } from \"tinyglobby\";\nimport { buildSimpleCommandMetadata, getSimpleCommands, getEventsMetadata } from \"./decorators\";\nimport { decoratorStore } from \"./decorators/store\";\nimport type { CommandMetadata } from \"./types\";\n\ninterface AutoDiscoveryOptions {\n roots?: string[];\n include?: string[];\n ignore?: string[];\n}\n\n/**\n * Stored command entry from @Stoat/@SimpleCommand registration.\n */\nexport interface RegisteredCommand {\n /** Instance of the @Stoat class */\n instance: object;\n /** Command metadata */\n metadata: CommandMetadata;\n /** Method name to call */\n methodName: string;\n /** The original class constructor (for guard validation) */\n classConstructor: Function;\n}\n\n/**\n * Stored event entry from @On/@Once registration.\n */\nexport interface RegisteredEvent {\n instance: object;\n methodName: string;\n event: string;\n type: \"on\" | \"once\";\n}\n\n/**\n * CommandRegistry - Scans directories and stores commands in a Map\n *\n * @example\n * ```ts\n * const registry = new CommandRegistry();\n * await registry.loadFromDirectory('./src/commands');\n *\n * const ping = registry.get('ping');\n * const allCommands = registry.getAll();\n * ```\n */\nexport class CommandRegistry {\n private static readonly DEFAULT_AUTO_DISCOVERY_IGNORES = [\n \"**/node_modules/**\",\n \"**/.git/**\",\n \"**/*.d.ts\",\n \"**/*.test.*\",\n \"**/*.spec.*\",\n ];\n\n private readonly commands: Map<string, RegisteredCommand> = new Map();\n private readonly aliases: Map<string, string> = new Map();\n private readonly registeredEvents: RegisteredEvent[] = [];\n private readonly extensions: string[];\n private readonly processedStoatClasses: Set<Function> = new Set();\n\n constructor(extensions: string[] = [\".js\", \".mjs\", \".cjs\"]) {\n this.extensions = extensions;\n }\n\n /**\n * Get the number of registered commands\n */\n get size(): number {\n return this.commands.size;\n }\n\n /**\n * Load commands from a directory using glob pattern matching\n */\n async loadFromDirectory(directory: string): Promise<void> {\n const patterns = this.extensions.map((ext) => path.join(directory, \"**\", `*${ext}`).replace(/\\\\/g, \"/\"));\n\n for (const pattern of patterns) {\n const files = await glob(pattern, {\n ignore: [\"**/*.d.ts\", \"**/*.test.ts\", \"**/*.spec.ts\"],\n absolute: true,\n });\n\n for (const file of files) {\n await this.loadFile(file, directory);\n }\n }\n\n console.log(`[Stoatx] Loaded ${this.commands.size} command(s) and ${this.registeredEvents.length} event(s)`);\n }\n\n /**\n * Auto-discover command files across one or more roots.\n */\n async autoDiscover(options: AutoDiscoveryOptions = {}): Promise<void> {\n const roots = options.roots?.length ? options.roots : [process.cwd()];\n const includePatterns = options.include?.length ? options.include : this.getDefaultAutoDiscoveryPatterns();\n\n const patterns = roots.flatMap((root) =>\n includePatterns.map((pattern) => path.join(root, pattern).replace(/\\\\/g, \"/\")),\n );\n\n const files = await glob(patterns, {\n ignore: [...CommandRegistry.DEFAULT_AUTO_DISCOVERY_IGNORES, ...(options.ignore ?? [])],\n absolute: true,\n });\n\n const uniqueFiles = [...new Set(files)];\n let candidateFiles = 0;\n for (const file of uniqueFiles) {\n if (!(await this.isLikelyCommandModule(file))) {\n continue;\n }\n candidateFiles++;\n\n const baseDir =\n roots.find((root) => {\n const relative = path.relative(root, file);\n return relative && !relative.startsWith(\"..\") && !path.isAbsolute(relative);\n }) ?? roots[0]!;\n await this.loadFile(file, baseDir);\n }\n\n console.log(`[Stoatx] Loaded ${this.commands.size} command(s) and ${this.registeredEvents.length} event(s)`);\n }\n\n private getDefaultAutoDiscoveryPatterns(): string[] {\n // discordx-like default: scan broadly, then register only decorated classes\n return this.extensions.map((ext) => `**/*${ext}`);\n }\n\n private async isLikelyCommandModule(filePath: string): Promise<boolean> {\n try {\n const source = await fs.readFile(filePath, \"utf8\");\n return source.includes(\"Stoat\") || source.includes(\"SimpleCommand\") || source.includes(\"stoatx:command\");\n } catch {\n // If the file can't be pre-read, fall back to attempting import.\n return true;\n }\n }\n\n /**\n * Register a command instance\n */\n register(instance: object, metadata: CommandMetadata, classConstructor: Function, methodName: string): void {\n const name = metadata.name.toLowerCase();\n\n if (this.commands.has(name)) {\n console.warn(`[Stoatx] Duplicate command name: ${name}. Skipping...`);\n return;\n }\n\n this.validateGuards(classConstructor, metadata.name);\n\n this.commands.set(name, { instance, metadata, methodName, classConstructor });\n\n for (const alias of metadata.aliases) {\n const aliasLower = alias.toLowerCase();\n if (this.aliases.has(aliasLower) || this.commands.has(aliasLower)) {\n console.warn(`[Stoatx] Duplicate alias: ${aliasLower}. Skipping...`);\n continue;\n }\n this.aliases.set(aliasLower, name);\n }\n }\n\n /**\n * Get a command by name or alias\n */\n get(name: string): RegisteredCommand | undefined {\n const lowerName = name.toLowerCase();\n const resolvedName = this.aliases.get(lowerName) ?? lowerName;\n return this.commands.get(resolvedName);\n }\n\n /**\n * Check if a command exists\n */\n has(name: string): boolean {\n const lowerName = name.toLowerCase();\n return this.commands.has(lowerName) || this.aliases.has(lowerName);\n }\n\n /**\n * Get all registered commands\n */\n getAll(): RegisteredCommand[] {\n return Array.from(this.commands.values());\n }\n\n /**\n * Get all command metadata\n */\n getAllMetadata(): CommandMetadata[] {\n return this.getAll().map((c) => c.metadata);\n }\n\n /**\n * Get all registered events\n */\n getEvents(): RegisteredEvent[] {\n return this.registeredEvents;\n }\n\n /**\n * Get commands grouped by category\n */\n getByCategory(): Map<string, RegisteredCommand[]> {\n const categories = new Map<string, RegisteredCommand[]>();\n\n for (const cmd of this.commands.values()) {\n const category = cmd.metadata.category;\n const existing = categories.get(category) ?? [];\n existing.push(cmd);\n categories.set(category, existing);\n }\n\n return categories;\n }\n\n /**\n * Clear all commands\n */\n clear(): void {\n this.commands.clear();\n this.aliases.clear();\n this.registeredEvents.length = 0;\n this.processedStoatClasses.clear();\n }\n\n /**\n * Iterate over commands\n */\n [Symbol.iterator](): IterableIterator<[string, RegisteredCommand]> {\n return this.commands.entries();\n }\n\n /**\n * Iterate over command values\n */\n values(): IterableIterator<RegisteredCommand> {\n return this.commands.values();\n }\n\n /**\n * Iterate over command names\n */\n keys(): IterableIterator<string> {\n return this.commands.keys();\n }\n\n /**\n * Validate that all guards on a command implement the required methods\n * @param commandClass\n * @param commandName\n * @private\n */\n private validateGuards(commandClass: Function, commandName: string): void {\n const guards: Function[] = Reflect.getMetadata(\"stoatx:command:guards\", commandClass) || [];\n\n for (const GuardClass of guards) {\n const guardInstance = new (GuardClass as any)();\n\n if (typeof guardInstance.run !== \"function\") {\n console.error(\n `[Stoatx] FATAL: Guard \"${GuardClass.name}\" on command \"${commandName}\" does not have a run() method.`,\n );\n process.exit(1);\n }\n\n if (typeof guardInstance.guardFail !== \"function\") {\n console.error(\n `[Stoatx] FATAL: Guard \"${GuardClass.name}\" on command \"${commandName}\" does not have a guardFail() method.`,\n );\n console.error(`[Stoatx] All guards must implement guardFail() to handle failed checks.`);\n process.exit(1);\n }\n }\n }\n\n /**\n * Load commands from a single file\n */\n private async loadFile(filePath: string, baseDir: string): Promise<void> {\n try {\n const knownStoatClasses = new Set(decoratorStore.getStoatClasses().keys());\n const fileUrl = pathToFileURL(filePath).href;\n await import(fileUrl);\n\n const allStoatClasses = decoratorStore.getStoatClasses();\n for (const [stoatClass, stoatInstance] of allStoatClasses.entries()) {\n if (knownStoatClasses.has(stoatClass) || this.processedStoatClasses.has(stoatClass)) {\n continue;\n }\n this.registerStoatClassCommands(stoatClass, stoatInstance, filePath, baseDir);\n }\n } catch (error) {\n console.error(`[Stoatx] Failed to load command file: ${filePath}`, error);\n }\n }\n\n private registerStoatClassCommands(stoatClass: Function, instance: object, filePath: string, baseDir: string): void {\n const simpleCommands = getSimpleCommands(stoatClass);\n const events = getEventsMetadata(stoatClass);\n const category = this.getCategoryFromPath(filePath, baseDir);\n\n if (simpleCommands.length === 0 && events.length === 0) {\n console.warn(\n `[Stoatx] Class ${stoatClass.name} is decorated with @Stoat but has no @SimpleCommand, @On or @Once methods. Skipping...`,\n );\n this.processedStoatClasses.add(stoatClass);\n return;\n }\n\n for (const cmdDef of simpleCommands) {\n const method = (instance as any)[cmdDef.methodName];\n if (typeof method !== \"function\") {\n console.warn(`[Stoatx] Method ${cmdDef.methodName} not found on ${stoatClass.name}. Skipping...`);\n continue;\n }\n\n const metadata = buildSimpleCommandMetadata(cmdDef.options, cmdDef.methodName, category);\n this.register(instance, metadata, stoatClass, cmdDef.methodName);\n }\n\n for (const eventDef of events) {\n const method = (instance as any)[eventDef.methodName];\n if (typeof method !== \"function\") {\n console.warn(`[Stoatx] Method ${eventDef.methodName} not found on ${stoatClass.name}. Skipping...`);\n continue;\n }\n\n this.registeredEvents.push({\n instance,\n methodName: eventDef.methodName,\n event: eventDef.event,\n type: eventDef.type,\n });\n }\n\n this.processedStoatClasses.add(stoatClass);\n }\n\n /**\n * Derive category from file path relative to base directory\n */\n private getCategoryFromPath(filePath: string, baseDir: string): string | undefined {\n const relative = path.relative(baseDir, filePath);\n const parts = relative.split(path.sep);\n\n if (parts.length > 1) {\n return parts[0];\n }\n\n return undefined;\n }\n}\n","import \"reflect-metadata\";\nimport { CommandRegistry, RegisteredCommand } from \"./registry\";\nimport type {\n CommandContext,\n CommandMetadata,\n StoatxDiscoveryOptions,\n StoatxHandlerOptions,\n CooldownManager,\n} from \"./types\";\nimport { Client as StoatClient, ClientEvents, Message } from \"@stoatx/client\";\n\n/**\n * Default in-memory cooldown manager\n */\nexport class DefaultCooldownManager implements CooldownManager {\n private readonly cooldowns: Map<string, Map<string, number>> = new Map();\n\n check(ctx: CommandContext, metadata: CommandMetadata): boolean {\n if (metadata.cooldown <= 0) return true;\n\n const commandCooldowns = this.cooldowns.get(metadata.name);\n if (!commandCooldowns) return true;\n\n const expirationTime = commandCooldowns.get(ctx.authorId);\n if (!expirationTime) return true;\n\n if (Date.now() > expirationTime) {\n commandCooldowns.delete(ctx.authorId);\n return true;\n }\n\n return false;\n }\n\n getRemaining(ctx: CommandContext, metadata: CommandMetadata): number {\n const commandCooldowns = this.cooldowns.get(metadata.name);\n if (!commandCooldowns) return 0;\n\n const userCooldown = commandCooldowns.get(ctx.authorId);\n if (!userCooldown) return 0;\n\n return Math.max(0, userCooldown - Date.now());\n }\n\n set(ctx: CommandContext, metadata: CommandMetadata): void {\n if (!this.cooldowns.has(metadata.name)) {\n this.cooldowns.set(metadata.name, new Map());\n }\n\n const commandCooldowns = this.cooldowns.get(metadata.name)!;\n commandCooldowns.set(ctx.authorId, Date.now() + metadata.cooldown);\n }\n\n clear(): void {\n this.cooldowns.clear();\n }\n}\n\n/**\n * Client - An extended Client that integrates StoatxHandler directly\n *\n * @example\n * ```ts\n * import { Client } from 'stoatx';\n *\n * const client = new Client({\n * prefix: '!',\n * owners: ['owner-user-id'],\n * });\n *\n * await client.initCommands();\n * ```\n */\nexport class Client extends StoatClient {\n public readonly handler: StoatxHandler;\n\n constructor(options: Omit<StoatxHandlerOptions, \"client\">) {\n super();\n this.handler = new StoatxHandler({ ...options, client: this });\n\n // Automatically hook up the message handler\n this.on(\"messageCreate\", async (message) => {\n await this.handler.handle(message);\n });\n }\n\n /**\n * Initialize the StoatxHandler commands\n */\n async initCommands(): Promise<void> {\n await this.handler.init();\n }\n}\n\n/**\n * StoatxHandler - The execution engine for commands\n *\n * Handles message parsing, middleware execution, and command dispatching\n *\n * @internal This class is not intended to be instantiated directly. Use the `Client` from `stoatx` instead.\n */\nexport class StoatxHandler {\n private readonly commandsDir: string | undefined;\n private readonly discoveryOptions: StoatxDiscoveryOptions | undefined;\n private readonly prefixResolver: string | ((ctx: { serverId?: string | undefined }) => string | Promise<string>);\n private readonly owners: Set<string>;\n private readonly registry: CommandRegistry;\n private readonly cooldownManager: CooldownManager;\n private readonly disableMentionPrefix: boolean;\n private readonly client: StoatClient;\n constructor(options: StoatxHandlerOptions) {\n this.client = options.client;\n this.commandsDir = options.commandsDir;\n this.discoveryOptions = options.discovery;\n this.prefixResolver = options.prefix;\n this.owners = new Set(options.owners ?? []);\n this.registry = new CommandRegistry(options.extensions);\n this.disableMentionPrefix = options.disableMentionPrefix ?? false;\n this.cooldownManager = options.cooldownManager ?? new DefaultCooldownManager();\n }\n\n /**\n * Initialize the handler - load all commands\n */\n async init(): Promise<void> {\n if (this.commandsDir) {\n await this.registry.loadFromDirectory(this.commandsDir);\n } else {\n await this.registry.autoDiscover(this.discoveryOptions);\n }\n\n this.attachEvents();\n }\n\n /**\n * Attach registered events to the client\n */\n private attachEvents(): void {\n const events = this.registry.getEvents();\n\n for (const eventDef of events) {\n const handler = async (...args: any[]) => {\n try {\n await (eventDef.instance as any)[eventDef.methodName](...args, this.client);\n } catch (error) {\n console.error(\n `[Stoatx] Event Handler Error in @${eventDef.type === \"on\" ? \"On\" : \"Once\"}('${eventDef.event}'):`,\n error,\n );\n }\n };\n\n const eventName = eventDef.event as keyof ClientEvents;\n if (eventDef.type === \"once\") {\n this.client.once(eventName, handler);\n } else {\n this.client.on(eventName, handler);\n }\n }\n }\n\n /**\n * Parse a raw message into command context\n */\n async parseMessage(\n rawContent: string,\n message: Message,\n meta: {\n authorId: string;\n channelId: string;\n serverId?: string | undefined;\n reply: (content: string) => Promise<Message>;\n },\n ): Promise<CommandContext | null> {\n const prefix = await this.resolvePrefix(meta.serverId);\n let usedPrefix = prefix;\n let withoutPrefix = \"\";\n\n // Check for string prefix\n if (rawContent.startsWith(prefix)) {\n withoutPrefix = rawContent.slice(prefix.length).trim();\n usedPrefix = prefix;\n }\n // Check for mention prefix (e.g., \"<@bot-id> command\") - unless disabled\n else if (!this.disableMentionPrefix && rawContent.match(/^<@!?[\\w]+>/)) {\n const mentionMatch = rawContent.match(/^<@!?([\\w]+)>\\s*/);\n if (mentionMatch) {\n const mentionedId = mentionMatch[1];\n const botId = this.client.user?.id;\n\n // Only process if mentioned user is the bot\n if (botId && mentionedId === botId) {\n usedPrefix = mentionMatch[0];\n withoutPrefix = rawContent.slice(mentionMatch[0].length).trim();\n } else {\n }\n }\n }\n\n if (!withoutPrefix) {\n return null;\n }\n\n const [commandName, ...args] = withoutPrefix.split(/\\s+/);\n\n if (!commandName) {\n return null;\n }\n\n return {\n client: this.client,\n content: rawContent,\n authorId: meta.authorId,\n channelId: meta.channelId,\n serverId: meta.serverId,\n args,\n prefix: usedPrefix,\n commandName: commandName.toLowerCase(),\n reply: meta.reply,\n message,\n };\n }\n\n /**\n * Handle a message object using the configured message adapter\n *\n * @example\n * ```ts\n * // With message adapter configured\n * client.on('messageCreate', (message) => {\n * handler.handle(message);\n * });\n * ```\n */\n async handle(message: Message): Promise<boolean> {\n if (!message.channel || !message.author) {\n return false;\n }\n\n // Skip messages from bots\n if (message.author.bot) {\n return false;\n }\n\n const rawContent = message.content;\n const authorId = message.author.id;\n const channelId = message.channel.id;\n const serverId = message.server?.id;\n const reply = async (content: string) => {\n return await message.channel!.send(content);\n };\n\n // rawContent won't be null since a command will be invoked with a prefix\n await this.handleMessage(rawContent!, message, {\n authorId,\n channelId,\n serverId,\n reply,\n });\n\n return true;\n }\n\n /**\n * Handle a raw message string with metadata\n *\n * @example\n * ```ts\n * // Manual usage without message adapter\n * client.on('messageCreate', (message) => {\n * handler.handleMessage(message.content, message, {\n * authorId: message.author.id,\n * channelId: message.channel.id,\n * serverId: message.server?.id,\n * reply: (content) => message.channel.sendMessage(content),\n * });\n * });\n * ```\n */\n async handleMessage(\n rawContent: string,\n message: Message,\n meta: {\n authorId: string;\n channelId: string;\n serverId?: string | undefined;\n reply: (content: string) => Promise<Message>;\n },\n ): Promise<void> {\n const ctx = await this.parseMessage(rawContent, message, meta);\n\n if (!ctx) {\n return;\n }\n\n await this.execute(ctx);\n }\n\n /**\n * Execute a command with the given context\n */\n async execute(ctx: CommandContext): Promise<boolean> {\n const registered = this.registry.get(ctx.commandName);\n\n if (!registered) {\n return false;\n }\n\n const { instance, metadata, methodName, classConstructor } = registered;\n\n // Owner-only check\n if (metadata.ownerOnly && !this.owners.has(ctx.authorId)) {\n await ctx.reply(\"This command is owner-only.\");\n return false;\n }\n\n // Guard checks - use classConstructor for guard metadata\n const guards: Function[] = Reflect.getMetadata(\"stoatx:command:guards\", classConstructor) || [];\n for (const guardClass of guards) {\n const guardInstance = new (guardClass as any)();\n if (typeof guardInstance.run === \"function\") {\n const guardResult = await guardInstance.run(ctx);\n if (!guardResult) {\n if (typeof guardInstance.guardFail === \"function\") {\n await guardInstance.guardFail(ctx);\n } else {\n console.error(\"[Stoatx] Guard check failed but no guardFail method defined on\", guardClass.name);\n }\n return false;\n }\n }\n }\n\n // Cooldown check\n if (!(await this.cooldownManager.check(ctx, metadata))) {\n const remaining = await this.cooldownManager.getRemaining(ctx, metadata);\n\n // For method-based commands, check if instance has onCooldown\n if (typeof (instance as any).onCooldown === \"function\") {\n await (instance as any).onCooldown(ctx, remaining);\n } else {\n await ctx.reply(`Please wait ${(remaining / 1000).toFixed(1)} seconds before using this command again.`);\n }\n return false;\n }\n\n try {\n // Set cooldown before execution to prevent concurrent executions\n if (metadata.cooldown > 0) {\n await this.cooldownManager.set(ctx, metadata);\n }\n\n await (instance as any)[methodName](ctx);\n\n return true;\n } catch (error) {\n // Handle errors\n if (typeof (instance as any).onError === \"function\") {\n await (instance as any).onError(ctx, error as Error);\n } else {\n console.error(`[Stoatx] Error in command ${metadata.name}:`, error);\n }\n return false;\n }\n }\n\n /**\n * Get the command registry\n */\n getRegistry(): CommandRegistry {\n return this.registry;\n }\n\n /**\n * Get a command by name or alias\n */\n getCommand(name: string): RegisteredCommand | undefined {\n return this.registry.get(name);\n }\n\n /**\n * Get all commands\n */\n getCommands(): RegisteredCommand[] {\n return this.registry.getAll();\n }\n\n /**\n * Reload all commands\n */\n async reload(): Promise<void> {\n this.registry.clear();\n if (this.cooldownManager.clear) {\n await this.cooldownManager.clear();\n }\n if (this.commandsDir) {\n await this.registry.loadFromDirectory(this.commandsDir);\n return;\n }\n\n await this.registry.autoDiscover(this.discoveryOptions);\n }\n\n /**\n * Check if a user is an owner\n */\n isOwner(userId: string): boolean {\n return this.owners.has(userId);\n }\n\n /**\n * Add an owner\n */\n addOwner(userId: string): void {\n this.owners.add(userId);\n }\n\n /**\n * Remove an owner\n */\n removeOwner(userId: string): void {\n this.owners.delete(userId);\n }\n\n /**\n * Resolve the prefix for a context\n */\n private async resolvePrefix(serverId?: string | undefined): Promise<string> {\n if (typeof this.prefixResolver === \"function\") {\n return this.prefixResolver({ serverId });\n }\n return this.prefixResolver;\n }\n}\n","// Types\nexport * from \"./types\";\n\n// Decorators\nexport * from \"./decorators\";\n\n// Registry\nexport * from \"./registry\";\n\n// Handler\nexport { Client, DefaultCooldownManager } from \"./handler\";\nexport type { StoatxHandler } from \"./handler\";\nexport * from \"@stoatx/client\";\n"],"mappings":"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","names":["relative"]}
|