stoatx 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +467 -0
- package/dist/index.d.ts +467 -0
- package/dist/index.js +697 -0
- package/dist/index.mjs +651 -0
- package/package.json +6 -2
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
import { Client, Message } from 'stoat.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Permission types for commands
|
|
5
|
+
*/
|
|
6
|
+
type Permission = "SendMessages" | "ManageMessages" | "ManageChannels" | "ManageServer" | "KickMembers" | "BanMembers" | "Administrator" | (string & {});
|
|
7
|
+
/**
|
|
8
|
+
* Simple command options passed to @SimpleCommand decorator
|
|
9
|
+
* Used with @Stoat() decorated classes for method-based commands
|
|
10
|
+
*/
|
|
11
|
+
interface SimpleCommandOptions {
|
|
12
|
+
/** Command name (defaults to method name) */
|
|
13
|
+
name?: string;
|
|
14
|
+
/** Command description */
|
|
15
|
+
description?: string;
|
|
16
|
+
/** Command aliases */
|
|
17
|
+
aliases?: string[];
|
|
18
|
+
/** Required permissions to run the command */
|
|
19
|
+
permissions?: Permission[];
|
|
20
|
+
/** Command category (auto-detected from directory if not provided) */
|
|
21
|
+
category?: string;
|
|
22
|
+
/** Cooldown in milliseconds */
|
|
23
|
+
cooldown?: number;
|
|
24
|
+
/** Whether the command is NSFW only */
|
|
25
|
+
nsfw?: boolean;
|
|
26
|
+
/** Whether the command is owner only */
|
|
27
|
+
ownerOnly?: boolean;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Resolved command metadata with required fields
|
|
31
|
+
*/
|
|
32
|
+
interface CommandMetadata {
|
|
33
|
+
name: string;
|
|
34
|
+
description: string;
|
|
35
|
+
aliases: string[];
|
|
36
|
+
permissions: Permission[];
|
|
37
|
+
category: string;
|
|
38
|
+
cooldown: number;
|
|
39
|
+
nsfw: boolean;
|
|
40
|
+
ownerOnly: boolean;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Command execution context
|
|
44
|
+
*/
|
|
45
|
+
interface CommandContext {
|
|
46
|
+
/** The client instance */
|
|
47
|
+
client: Client;
|
|
48
|
+
/** The raw message content */
|
|
49
|
+
content: string;
|
|
50
|
+
/** The author ID */
|
|
51
|
+
authorId: string;
|
|
52
|
+
/** The channel ID */
|
|
53
|
+
channelId: string;
|
|
54
|
+
/** The server/guild ID (if applicable) */
|
|
55
|
+
serverId?: string;
|
|
56
|
+
/** Parsed command arguments */
|
|
57
|
+
args: string[];
|
|
58
|
+
/** The prefix used */
|
|
59
|
+
prefix: string;
|
|
60
|
+
/** The command name used (could be an alias) */
|
|
61
|
+
commandName: string;
|
|
62
|
+
/** Reply to the message */
|
|
63
|
+
reply: (content: string) => Promise<void>;
|
|
64
|
+
/** The original message object (platform-specific) */
|
|
65
|
+
message: Message;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Optional lifecycle hooks for @Stoat() class instances
|
|
69
|
+
*/
|
|
70
|
+
interface StoatLifecycle {
|
|
71
|
+
/** Optional: Called when an error occurs during command execution */
|
|
72
|
+
onError?(ctx: CommandContext, error: Error): Promise<void>;
|
|
73
|
+
/** Optional: Called when a cooldown is active */
|
|
74
|
+
onCooldown?(ctx: CommandContext, remaining: number): Promise<void>;
|
|
75
|
+
}
|
|
76
|
+
interface MallyGuard {
|
|
77
|
+
run(ctx: CommandContext): Promise<boolean> | boolean;
|
|
78
|
+
guardFail?(ctx: CommandContext): Promise<void> | void;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Discovery options for automatic command module loading
|
|
82
|
+
*/
|
|
83
|
+
interface MallyDiscoveryOptions {
|
|
84
|
+
/** Root directories to scan (default: [process.cwd()]) */
|
|
85
|
+
roots?: string[];
|
|
86
|
+
/** Glob patterns relative to each root */
|
|
87
|
+
include?: string[];
|
|
88
|
+
/** Additional ignore patterns */
|
|
89
|
+
ignore?: string[];
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Handler options
|
|
93
|
+
*/
|
|
94
|
+
interface MallyHandlerOptions {
|
|
95
|
+
/** The client instance */
|
|
96
|
+
client: Client;
|
|
97
|
+
/** Directory to scan for command modules (absolute path) */
|
|
98
|
+
commandsDir?: string;
|
|
99
|
+
/** Auto-discovery options used when commandsDir is not provided */
|
|
100
|
+
discovery?: MallyDiscoveryOptions;
|
|
101
|
+
/** Command prefix or prefix resolver function */
|
|
102
|
+
prefix: string | ((ctx: {
|
|
103
|
+
serverId?: string;
|
|
104
|
+
}) => string | Promise<string>);
|
|
105
|
+
/** Owner IDs for owner-only commands */
|
|
106
|
+
owners?: string[];
|
|
107
|
+
/** File extensions to load (default: ['.js', '.mjs', '.cjs']) */
|
|
108
|
+
extensions?: string[];
|
|
109
|
+
/** Disable mention prefix support (default: false) */
|
|
110
|
+
disableMentionPrefix?: boolean;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @Stoat
|
|
115
|
+
* Marks a class as a Stoat command container.
|
|
116
|
+
* Use this decorator on classes that contain @SimpleCommand methods.
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* ```ts
|
|
120
|
+
* import { Stoat, SimpleCommand, CommandContext } from 'stoatx';
|
|
121
|
+
*
|
|
122
|
+
* @Stoat()
|
|
123
|
+
* class ModerationCommands {
|
|
124
|
+
* @SimpleCommand({ name: 'ban', description: 'Ban a user' })
|
|
125
|
+
* async ban(ctx: CommandContext) {
|
|
126
|
+
* await ctx.reply('User banned!');
|
|
127
|
+
* }
|
|
128
|
+
*
|
|
129
|
+
* @SimpleCommand({ name: 'kick', description: 'Kick a user' })
|
|
130
|
+
* async kick(ctx: CommandContext) {
|
|
131
|
+
* await ctx.reply('User kicked!');
|
|
132
|
+
* }
|
|
133
|
+
* }
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
declare function Stoat(): ClassDecorator;
|
|
137
|
+
/**
|
|
138
|
+
* Check if a class is decorated with @Stoat
|
|
139
|
+
*/
|
|
140
|
+
declare function isStoatClass(target: Function): boolean;
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Stored simple command metadata from method decorator
|
|
144
|
+
*/
|
|
145
|
+
interface SimpleCommandDefinition {
|
|
146
|
+
methodName: string;
|
|
147
|
+
options: SimpleCommandOptions;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* @SimpleCommand
|
|
151
|
+
* Marks a method as a simple command within a @Stoat() decorated class.
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```ts
|
|
155
|
+
* @Stoat()
|
|
156
|
+
* class Example {
|
|
157
|
+
* @SimpleCommand({ name: 'ping', description: 'Replies with Pong!' })
|
|
158
|
+
* async ping(ctx: CommandContext) {
|
|
159
|
+
* await ctx.reply('Pong!');
|
|
160
|
+
* }
|
|
161
|
+
*
|
|
162
|
+
* @SimpleCommand({ aliases: ['perm'], name: 'permission' })
|
|
163
|
+
* async permission(ctx: CommandContext) {
|
|
164
|
+
* await ctx.reply('Access granted');
|
|
165
|
+
* }
|
|
166
|
+
* }
|
|
167
|
+
* ```
|
|
168
|
+
*/
|
|
169
|
+
declare function SimpleCommand(options?: SimpleCommandOptions): MethodDecorator;
|
|
170
|
+
/**
|
|
171
|
+
* Get all simple command definitions from a @Stoat class
|
|
172
|
+
*/
|
|
173
|
+
declare function getSimpleCommands(target: Function): SimpleCommandDefinition[];
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* @Guard
|
|
177
|
+
* Runs before a command to check if it should execute.
|
|
178
|
+
* Should return true to allow execution, false to block.
|
|
179
|
+
* Applied on @Stoat classes to guard all contained @SimpleCommand methods.
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* ```ts
|
|
183
|
+
* import { Guard, Stoat, SimpleCommand, CommandContext } from 'stoatx';
|
|
184
|
+
*
|
|
185
|
+
* // Define a guard
|
|
186
|
+
* class NotBot implements MallyGuard {
|
|
187
|
+
* run(ctx: CommandContext): boolean {
|
|
188
|
+
* return !ctx.message.author.bot;
|
|
189
|
+
* }
|
|
190
|
+
*
|
|
191
|
+
* guardFail(ctx: CommandContext): void {
|
|
192
|
+
* ctx.reply("Bots cannot use this command!");
|
|
193
|
+
* }
|
|
194
|
+
* }
|
|
195
|
+
*
|
|
196
|
+
* @Stoat()
|
|
197
|
+
* @Guard(NotBot)
|
|
198
|
+
* class AdminCommands {
|
|
199
|
+
* @SimpleCommand({ name: 'admin', description: 'Admin only command' })
|
|
200
|
+
* async admin(ctx: CommandContext) {
|
|
201
|
+
* ctx.reply("You passed the guard check!");
|
|
202
|
+
* }
|
|
203
|
+
* }
|
|
204
|
+
* ```
|
|
205
|
+
*/
|
|
206
|
+
declare function Guard(guardClass: Function): ClassDecorator;
|
|
207
|
+
/**
|
|
208
|
+
* Get all guards from a decorated class
|
|
209
|
+
*/
|
|
210
|
+
declare function getGuards(target: Function): Function[];
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Build CommandMetadata from SimpleCommandOptions
|
|
214
|
+
*/
|
|
215
|
+
declare function buildSimpleCommandMetadata(options: SimpleCommandOptions, methodName: string, category?: string): CommandMetadata;
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Metadata keys used by decorators
|
|
219
|
+
*/
|
|
220
|
+
declare const METADATA_KEYS: {
|
|
221
|
+
readonly IS_STOAT_CLASS: symbol;
|
|
222
|
+
readonly SIMPLE_COMMANDS: symbol;
|
|
223
|
+
readonly GUARDS: "mally:command:guards";
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
interface AutoDiscoveryOptions {
|
|
227
|
+
roots?: string[];
|
|
228
|
+
include?: string[];
|
|
229
|
+
ignore?: string[];
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Stored command entry from @Stoat/@SimpleCommand registration.
|
|
233
|
+
*/
|
|
234
|
+
interface RegisteredCommand {
|
|
235
|
+
/** Instance of the @Stoat class */
|
|
236
|
+
instance: object;
|
|
237
|
+
/** Command metadata */
|
|
238
|
+
metadata: CommandMetadata;
|
|
239
|
+
/** Method name to call */
|
|
240
|
+
methodName: string;
|
|
241
|
+
/** The original class constructor (for guard validation) */
|
|
242
|
+
classConstructor: Function;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* CommandRegistry - Scans directories and stores commands in a Map
|
|
246
|
+
*
|
|
247
|
+
* @example
|
|
248
|
+
* ```ts
|
|
249
|
+
* const registry = new CommandRegistry();
|
|
250
|
+
* await registry.loadFromDirectory('./src/commands');
|
|
251
|
+
*
|
|
252
|
+
* const ping = registry.get('ping');
|
|
253
|
+
* const allCommands = registry.getAll();
|
|
254
|
+
* ```
|
|
255
|
+
*/
|
|
256
|
+
declare class CommandRegistry {
|
|
257
|
+
private static readonly DEFAULT_AUTO_DISCOVERY_IGNORES;
|
|
258
|
+
private readonly commands;
|
|
259
|
+
private readonly aliases;
|
|
260
|
+
private readonly extensions;
|
|
261
|
+
private readonly processedStoatClasses;
|
|
262
|
+
constructor(extensions?: string[]);
|
|
263
|
+
/**
|
|
264
|
+
* Get the number of registered commands
|
|
265
|
+
*/
|
|
266
|
+
get size(): number;
|
|
267
|
+
/**
|
|
268
|
+
* Load commands from a directory using glob pattern matching
|
|
269
|
+
*/
|
|
270
|
+
loadFromDirectory(directory: string): Promise<void>;
|
|
271
|
+
/**
|
|
272
|
+
* Auto-discover command files across one or more roots.
|
|
273
|
+
*/
|
|
274
|
+
autoDiscover(options?: AutoDiscoveryOptions): Promise<void>;
|
|
275
|
+
private getDefaultAutoDiscoveryPatterns;
|
|
276
|
+
private isLikelyCommandModule;
|
|
277
|
+
/**
|
|
278
|
+
* Register a command instance
|
|
279
|
+
*/
|
|
280
|
+
register(instance: object, metadata: CommandMetadata, classConstructor: Function, methodName: string): void;
|
|
281
|
+
/**
|
|
282
|
+
* Get a command by name or alias
|
|
283
|
+
*/
|
|
284
|
+
get(name: string): RegisteredCommand | undefined;
|
|
285
|
+
/**
|
|
286
|
+
* Check if a command exists
|
|
287
|
+
*/
|
|
288
|
+
has(name: string): boolean;
|
|
289
|
+
/**
|
|
290
|
+
* Get all registered commands
|
|
291
|
+
*/
|
|
292
|
+
getAll(): RegisteredCommand[];
|
|
293
|
+
/**
|
|
294
|
+
* Get all command metadata
|
|
295
|
+
*/
|
|
296
|
+
getAllMetadata(): CommandMetadata[];
|
|
297
|
+
/**
|
|
298
|
+
* Get commands grouped by category
|
|
299
|
+
*/
|
|
300
|
+
getByCategory(): Map<string, RegisteredCommand[]>;
|
|
301
|
+
/**
|
|
302
|
+
* Clear all commands
|
|
303
|
+
*/
|
|
304
|
+
clear(): void;
|
|
305
|
+
/**
|
|
306
|
+
* Iterate over commands
|
|
307
|
+
*/
|
|
308
|
+
[Symbol.iterator](): IterableIterator<[string, RegisteredCommand]>;
|
|
309
|
+
/**
|
|
310
|
+
* Iterate over command values
|
|
311
|
+
*/
|
|
312
|
+
values(): IterableIterator<RegisteredCommand>;
|
|
313
|
+
/**
|
|
314
|
+
* Iterate over command names
|
|
315
|
+
*/
|
|
316
|
+
keys(): IterableIterator<string>;
|
|
317
|
+
/**
|
|
318
|
+
* Validate that all guards on a command implement the required methods
|
|
319
|
+
* @param commandClass
|
|
320
|
+
* @param commandName
|
|
321
|
+
* @private
|
|
322
|
+
*/
|
|
323
|
+
private validateGuards;
|
|
324
|
+
/**
|
|
325
|
+
* Load commands from a single file
|
|
326
|
+
*/
|
|
327
|
+
private loadFile;
|
|
328
|
+
private registerStoatClassCommands;
|
|
329
|
+
/**
|
|
330
|
+
* Derive category from file path relative to base directory
|
|
331
|
+
*/
|
|
332
|
+
private getCategoryFromPath;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* MallyHandler - The execution engine for commands
|
|
337
|
+
*
|
|
338
|
+
* Handles message parsing, middleware execution, and command dispatching
|
|
339
|
+
*
|
|
340
|
+
* @example
|
|
341
|
+
* ```ts
|
|
342
|
+
* import { MallyHandler } from 'stoatx';
|
|
343
|
+
* import { Client } from 'stoat.js';
|
|
344
|
+
*
|
|
345
|
+
* const client = new Client();
|
|
346
|
+
*
|
|
347
|
+
* const handler = new MallyHandler({
|
|
348
|
+
* client,
|
|
349
|
+
* prefix: '!',
|
|
350
|
+
* owners: ['owner-user-id'],
|
|
351
|
+
* });
|
|
352
|
+
*
|
|
353
|
+
* await handler.init();
|
|
354
|
+
*
|
|
355
|
+
* client.on('message', (message) => {
|
|
356
|
+
* handler.handleMessage(message);
|
|
357
|
+
* });
|
|
358
|
+
* ```
|
|
359
|
+
*/
|
|
360
|
+
declare class MallyHandler {
|
|
361
|
+
private readonly commandsDir?;
|
|
362
|
+
private readonly discoveryOptions?;
|
|
363
|
+
private readonly prefixResolver;
|
|
364
|
+
private readonly owners;
|
|
365
|
+
private readonly registry;
|
|
366
|
+
private readonly cooldowns;
|
|
367
|
+
private readonly disableMentionPrefix;
|
|
368
|
+
private readonly client;
|
|
369
|
+
constructor(options: MallyHandlerOptions);
|
|
370
|
+
/**
|
|
371
|
+
* Initialize the handler - load all commands
|
|
372
|
+
*/
|
|
373
|
+
init(): Promise<void>;
|
|
374
|
+
/**
|
|
375
|
+
* Parse a raw message into command context
|
|
376
|
+
*/
|
|
377
|
+
parseMessage(rawContent: string, message: Message, meta: {
|
|
378
|
+
authorId: string;
|
|
379
|
+
channelId: string;
|
|
380
|
+
serverId?: string;
|
|
381
|
+
reply: (content: string) => Promise<void>;
|
|
382
|
+
}): Promise<CommandContext | null>;
|
|
383
|
+
/**
|
|
384
|
+
* Handle a message object using the configured message adapter
|
|
385
|
+
*
|
|
386
|
+
* @example
|
|
387
|
+
* ```ts
|
|
388
|
+
* // With message adapter configured
|
|
389
|
+
* client.on('messageCreate', (message) => {
|
|
390
|
+
* handler.handle(message);
|
|
391
|
+
* });
|
|
392
|
+
* ```
|
|
393
|
+
*/
|
|
394
|
+
handle(message: any): Promise<boolean>;
|
|
395
|
+
/**
|
|
396
|
+
* Handle a raw message string with metadata
|
|
397
|
+
*
|
|
398
|
+
* @example
|
|
399
|
+
* ```ts
|
|
400
|
+
* // Manual usage without message adapter
|
|
401
|
+
* client.on('messageCreate', (message) => {
|
|
402
|
+
* handler.handleMessage(message.content, message, {
|
|
403
|
+
* authorId: message.author.id,
|
|
404
|
+
* channelId: message.channel.id,
|
|
405
|
+
* serverId: message.server?.id,
|
|
406
|
+
* reply: (content) => message.channel.sendMessage(content),
|
|
407
|
+
* });
|
|
408
|
+
* });
|
|
409
|
+
* ```
|
|
410
|
+
*/
|
|
411
|
+
handleMessage(rawContent: string, message: Message, meta: {
|
|
412
|
+
authorId: string;
|
|
413
|
+
channelId: string;
|
|
414
|
+
serverId?: string;
|
|
415
|
+
reply: (content: string) => Promise<void>;
|
|
416
|
+
}): Promise<boolean>;
|
|
417
|
+
/**
|
|
418
|
+
* Execute a command with the given context
|
|
419
|
+
*/
|
|
420
|
+
execute(ctx: CommandContext): Promise<boolean>;
|
|
421
|
+
/**
|
|
422
|
+
* Get the command registry
|
|
423
|
+
*/
|
|
424
|
+
getRegistry(): CommandRegistry;
|
|
425
|
+
/**
|
|
426
|
+
* Get a command by name or alias
|
|
427
|
+
*/
|
|
428
|
+
getCommand(name: string): RegisteredCommand | undefined;
|
|
429
|
+
/**
|
|
430
|
+
* Get all commands
|
|
431
|
+
*/
|
|
432
|
+
getCommands(): RegisteredCommand[];
|
|
433
|
+
/**
|
|
434
|
+
* Reload all commands
|
|
435
|
+
*/
|
|
436
|
+
reload(): Promise<void>;
|
|
437
|
+
/**
|
|
438
|
+
* Check if a user is an owner
|
|
439
|
+
*/
|
|
440
|
+
isOwner(userId: string): boolean;
|
|
441
|
+
/**
|
|
442
|
+
* Add an owner
|
|
443
|
+
*/
|
|
444
|
+
addOwner(userId: string): void;
|
|
445
|
+
/**
|
|
446
|
+
* Remove an owner
|
|
447
|
+
*/
|
|
448
|
+
removeOwner(userId: string): void;
|
|
449
|
+
/**
|
|
450
|
+
* Resolve the prefix for a context
|
|
451
|
+
*/
|
|
452
|
+
private resolvePrefix;
|
|
453
|
+
/**
|
|
454
|
+
* Check if user is on cooldown
|
|
455
|
+
*/
|
|
456
|
+
private checkCooldown;
|
|
457
|
+
/**
|
|
458
|
+
* Get remaining cooldown time in ms
|
|
459
|
+
*/
|
|
460
|
+
private getRemainingCooldown;
|
|
461
|
+
/**
|
|
462
|
+
* Set cooldown for a user
|
|
463
|
+
*/
|
|
464
|
+
private setCooldown;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
export { type CommandContext, type CommandMetadata, CommandRegistry, type CommandContext as Context, Guard, METADATA_KEYS, type MallyDiscoveryOptions, type MallyGuard, MallyHandler, type MallyHandlerOptions, type Permission, type RegisteredCommand, SimpleCommand, type SimpleCommandDefinition, type SimpleCommandOptions, Stoat, type StoatLifecycle, buildSimpleCommandMetadata, getGuards, getSimpleCommands, isStoatClass };
|