stoatx 0.7.7 → 1.0.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/README.md +3 -264
- package/dist/index.d.mts +204 -183
- package/dist/index.d.ts +204 -183
- package/dist/index.js +635 -236
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +621 -234
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
8
9
|
var __export = (target, all) => {
|
|
9
10
|
for (var name in all)
|
|
10
11
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -31,15 +32,29 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
32
|
// src/index.ts
|
|
32
33
|
var index_exports = {};
|
|
33
34
|
__export(index_exports, {
|
|
35
|
+
Arg: () => Arg,
|
|
34
36
|
Client: () => Client,
|
|
37
|
+
CommandGroup: () => CommandGroup,
|
|
35
38
|
CommandRegistry: () => CommandRegistry,
|
|
39
|
+
CommandValidationError: () => CommandValidationError,
|
|
36
40
|
DefaultCooldownManager: () => DefaultCooldownManager,
|
|
41
|
+
FetchFailedError: () => FetchFailedError,
|
|
37
42
|
Guard: () => Guard,
|
|
43
|
+
Injectable: () => Injectable,
|
|
44
|
+
InvalidMentionError: () => InvalidMentionError,
|
|
45
|
+
InvalidTypeError: () => InvalidTypeError,
|
|
38
46
|
METADATA_KEYS: () => METADATA_KEYS,
|
|
47
|
+
MissingArgumentError: () => MissingArgumentError,
|
|
48
|
+
MissingOptionError: () => MissingOptionError,
|
|
49
|
+
NoServerContextError: () => NoServerContextError,
|
|
39
50
|
On: () => On,
|
|
40
51
|
Once: () => Once,
|
|
52
|
+
Option: () => Option,
|
|
53
|
+
PARAM_TYPE_MAP: () => PARAM_TYPE_MAP,
|
|
41
54
|
SimpleCommand: () => SimpleCommand,
|
|
42
55
|
Stoat: () => Stoat,
|
|
56
|
+
StoatxError: () => StoatxError,
|
|
57
|
+
SubCommand: () => SubCommand,
|
|
43
58
|
buildSimpleCommandMetadata: () => buildSimpleCommandMetadata,
|
|
44
59
|
getEventsMetadata: () => getEventsMetadata,
|
|
45
60
|
getGuards: () => getGuards,
|
|
@@ -48,6 +63,35 @@ __export(index_exports, {
|
|
|
48
63
|
});
|
|
49
64
|
module.exports = __toCommonJS(index_exports);
|
|
50
65
|
|
|
66
|
+
// src/types.ts
|
|
67
|
+
var import_client = require("@stoatx/client");
|
|
68
|
+
var PARAM_TYPE_MAP = /* @__PURE__ */ new Map([
|
|
69
|
+
[
|
|
70
|
+
String,
|
|
71
|
+
"string"
|
|
72
|
+
],
|
|
73
|
+
[
|
|
74
|
+
Number,
|
|
75
|
+
"number"
|
|
76
|
+
],
|
|
77
|
+
[
|
|
78
|
+
Boolean,
|
|
79
|
+
"boolean"
|
|
80
|
+
],
|
|
81
|
+
[
|
|
82
|
+
import_client.User,
|
|
83
|
+
"user"
|
|
84
|
+
],
|
|
85
|
+
[
|
|
86
|
+
import_client.BaseChannel,
|
|
87
|
+
"channel"
|
|
88
|
+
],
|
|
89
|
+
[
|
|
90
|
+
import_client.Role,
|
|
91
|
+
"role"
|
|
92
|
+
]
|
|
93
|
+
]);
|
|
94
|
+
|
|
51
95
|
// src/decorators/Stoat.ts
|
|
52
96
|
var import_reflect_metadata = require("reflect-metadata");
|
|
53
97
|
|
|
@@ -56,14 +100,22 @@ var METADATA_KEYS = {
|
|
|
56
100
|
IS_STOAT_CLASS: /* @__PURE__ */ Symbol("stoatx:stoat:isClass"),
|
|
57
101
|
SIMPLE_COMMANDS: /* @__PURE__ */ Symbol("stoatx:stoat:simpleCommands"),
|
|
58
102
|
GUARDS: "stoatx:command:guards",
|
|
59
|
-
EVENTS: /* @__PURE__ */ Symbol("stoatx:stoat:events")
|
|
103
|
+
EVENTS: /* @__PURE__ */ Symbol("stoatx:stoat:events"),
|
|
104
|
+
ARGS: /* @__PURE__ */ Symbol("stoatx:param:args"),
|
|
105
|
+
OPTIONS: /* @__PURE__ */ Symbol("stoatx:param:options"),
|
|
106
|
+
INJECTABLE: /* @__PURE__ */ Symbol("stoatx:injectable"),
|
|
107
|
+
SUBCOMMAND: /* @__PURE__ */ Symbol("stoatx:subcommand"),
|
|
108
|
+
COMMAND_GROUP: /* @__PURE__ */ Symbol("stoatx:commandGroup")
|
|
60
109
|
};
|
|
61
110
|
|
|
62
111
|
// src/decorators/store.ts
|
|
63
112
|
var DecoratorStore = class _DecoratorStore {
|
|
113
|
+
static {
|
|
114
|
+
__name(this, "DecoratorStore");
|
|
115
|
+
}
|
|
64
116
|
static instance;
|
|
65
117
|
/** Stoat classes with their SimpleCommand methods */
|
|
66
|
-
stoatClasses = /* @__PURE__ */ new
|
|
118
|
+
stoatClasses = /* @__PURE__ */ new Set();
|
|
67
119
|
/** Registered commands from @Stoat/@SimpleCommand decorators */
|
|
68
120
|
commands = [];
|
|
69
121
|
/** Whether the store has been initialized */
|
|
@@ -77,49 +129,46 @@ var DecoratorStore = class _DecoratorStore {
|
|
|
77
129
|
return _DecoratorStore.instance;
|
|
78
130
|
}
|
|
79
131
|
/**
|
|
80
|
-
|
|
81
|
-
|
|
132
|
+
* Register a @Stoat decorated class
|
|
133
|
+
*/
|
|
82
134
|
registerStoatClass(classConstructor) {
|
|
83
|
-
|
|
84
|
-
const instance = new classConstructor();
|
|
85
|
-
this.stoatClasses.set(classConstructor, instance);
|
|
86
|
-
}
|
|
135
|
+
this.stoatClasses.add(classConstructor);
|
|
87
136
|
}
|
|
88
137
|
/**
|
|
89
|
-
|
|
90
|
-
|
|
138
|
+
* Get all registered Stoat classes with their instances
|
|
139
|
+
*/
|
|
91
140
|
getStoatClasses() {
|
|
92
141
|
return this.stoatClasses;
|
|
93
142
|
}
|
|
94
143
|
/**
|
|
95
|
-
|
|
96
|
-
|
|
144
|
+
* Add a registered command
|
|
145
|
+
*/
|
|
97
146
|
addCommand(command) {
|
|
98
147
|
this.commands.push(command);
|
|
99
148
|
}
|
|
100
149
|
/**
|
|
101
|
-
|
|
102
|
-
|
|
150
|
+
* Get all registered commands
|
|
151
|
+
*/
|
|
103
152
|
getCommands() {
|
|
104
153
|
return this.commands;
|
|
105
154
|
}
|
|
106
155
|
/**
|
|
107
|
-
|
|
108
|
-
|
|
156
|
+
* Clear all registered classes (useful for testing)
|
|
157
|
+
*/
|
|
109
158
|
clear() {
|
|
110
159
|
this.stoatClasses.clear();
|
|
111
160
|
this.commands = [];
|
|
112
161
|
this.initialized = false;
|
|
113
162
|
}
|
|
114
163
|
/**
|
|
115
|
-
|
|
116
|
-
|
|
164
|
+
* Mark as initialized
|
|
165
|
+
*/
|
|
117
166
|
markInitialized() {
|
|
118
167
|
this.initialized = true;
|
|
119
168
|
}
|
|
120
169
|
/**
|
|
121
|
-
|
|
122
|
-
|
|
170
|
+
* Check if initialized
|
|
171
|
+
*/
|
|
123
172
|
isInitialized() {
|
|
124
173
|
return this.initialized;
|
|
125
174
|
}
|
|
@@ -133,9 +182,11 @@ function Stoat() {
|
|
|
133
182
|
decoratorStore.registerStoatClass(target);
|
|
134
183
|
};
|
|
135
184
|
}
|
|
185
|
+
__name(Stoat, "Stoat");
|
|
136
186
|
function isStoatClass(target) {
|
|
137
187
|
return Reflect.getMetadata(METADATA_KEYS.IS_STOAT_CLASS, target) === true;
|
|
138
188
|
}
|
|
189
|
+
__name(isStoatClass, "isStoatClass");
|
|
139
190
|
|
|
140
191
|
// src/decorators/SimpleCommand.ts
|
|
141
192
|
var import_reflect_metadata2 = require("reflect-metadata");
|
|
@@ -151,22 +202,35 @@ function SimpleCommand(options = {}) {
|
|
|
151
202
|
return descriptor;
|
|
152
203
|
};
|
|
153
204
|
}
|
|
205
|
+
__name(SimpleCommand, "SimpleCommand");
|
|
154
206
|
function getSimpleCommands(target) {
|
|
155
207
|
return Reflect.getMetadata(METADATA_KEYS.SIMPLE_COMMANDS, target) || [];
|
|
156
208
|
}
|
|
209
|
+
__name(getSimpleCommands, "getSimpleCommands");
|
|
157
210
|
|
|
158
211
|
// src/decorators/Guard.ts
|
|
159
212
|
var import_reflect_metadata3 = require("reflect-metadata");
|
|
160
213
|
function Guard(guardClass) {
|
|
161
|
-
return (target) => {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
214
|
+
return (target, propertyKey) => {
|
|
215
|
+
if (propertyKey) {
|
|
216
|
+
const existingGuards = Reflect.getMetadata(METADATA_KEYS.GUARDS, target, propertyKey) || [];
|
|
217
|
+
existingGuards.push(guardClass);
|
|
218
|
+
Reflect.defineMetadata(METADATA_KEYS.GUARDS, existingGuards, target, propertyKey);
|
|
219
|
+
} else {
|
|
220
|
+
const existingGuards = Reflect.getMetadata(METADATA_KEYS.GUARDS, target) || [];
|
|
221
|
+
existingGuards.push(guardClass);
|
|
222
|
+
Reflect.defineMetadata(METADATA_KEYS.GUARDS, existingGuards, target);
|
|
223
|
+
}
|
|
165
224
|
};
|
|
166
225
|
}
|
|
167
|
-
|
|
226
|
+
__name(Guard, "Guard");
|
|
227
|
+
function getGuards(target, propertyKey) {
|
|
228
|
+
if (propertyKey) {
|
|
229
|
+
return Reflect.getMetadata(METADATA_KEYS.GUARDS, target.prototype, propertyKey) || [];
|
|
230
|
+
}
|
|
168
231
|
return Reflect.getMetadata(METADATA_KEYS.GUARDS, target) || [];
|
|
169
232
|
}
|
|
233
|
+
__name(getGuards, "getGuards");
|
|
170
234
|
|
|
171
235
|
// src/decorators/Events.ts
|
|
172
236
|
var import_reflect_metadata4 = require("reflect-metadata");
|
|
@@ -183,18 +247,88 @@ function createEventDecorator(event, type) {
|
|
|
183
247
|
return descriptor;
|
|
184
248
|
};
|
|
185
249
|
}
|
|
250
|
+
__name(createEventDecorator, "createEventDecorator");
|
|
186
251
|
function On(event) {
|
|
187
252
|
return createEventDecorator(event, "on");
|
|
188
253
|
}
|
|
254
|
+
__name(On, "On");
|
|
189
255
|
function Once(event) {
|
|
190
256
|
return createEventDecorator(event, "once");
|
|
191
257
|
}
|
|
258
|
+
__name(Once, "Once");
|
|
192
259
|
function getEventsMetadata(target) {
|
|
193
260
|
return Reflect.getMetadata(METADATA_KEYS.EVENTS, target) || [];
|
|
194
261
|
}
|
|
262
|
+
__name(getEventsMetadata, "getEventsMetadata");
|
|
263
|
+
|
|
264
|
+
// src/decorators/Arg.ts
|
|
265
|
+
var import_reflect_metadata5 = require("reflect-metadata");
|
|
266
|
+
function Arg(options = {}) {
|
|
267
|
+
return (target, propertyKey, parameterIndex) => {
|
|
268
|
+
const existing = Reflect.getMetadata(METADATA_KEYS.ARGS, target, propertyKey) || [];
|
|
269
|
+
existing.push({
|
|
270
|
+
...options,
|
|
271
|
+
index: parameterIndex
|
|
272
|
+
});
|
|
273
|
+
Reflect.defineMetadata(METADATA_KEYS.ARGS, existing, target, propertyKey);
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
__name(Arg, "Arg");
|
|
277
|
+
function getArgs(target, propertyKey) {
|
|
278
|
+
return Reflect.getMetadata(METADATA_KEYS.ARGS, target, propertyKey) || [];
|
|
279
|
+
}
|
|
280
|
+
__name(getArgs, "getArgs");
|
|
281
|
+
|
|
282
|
+
// src/decorators/Option.ts
|
|
283
|
+
var import_reflect_metadata6 = require("reflect-metadata");
|
|
284
|
+
function Option(options) {
|
|
285
|
+
return (target, propertyKey, parameterIndex) => {
|
|
286
|
+
const existing = Reflect.getMetadata(METADATA_KEYS.OPTIONS, target, propertyKey) || [];
|
|
287
|
+
existing.push({
|
|
288
|
+
...options,
|
|
289
|
+
index: parameterIndex
|
|
290
|
+
});
|
|
291
|
+
Reflect.defineMetadata(METADATA_KEYS.OPTIONS, existing, target, propertyKey);
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
__name(Option, "Option");
|
|
295
|
+
function getOptions(target, propertyKey) {
|
|
296
|
+
return Reflect.getMetadata(METADATA_KEYS.OPTIONS, target, propertyKey) || [];
|
|
297
|
+
}
|
|
298
|
+
__name(getOptions, "getOptions");
|
|
299
|
+
|
|
300
|
+
// src/decorators/Injectable.ts
|
|
301
|
+
var import_reflect_metadata7 = require("reflect-metadata");
|
|
302
|
+
function Injectable() {
|
|
303
|
+
return (target) => {
|
|
304
|
+
Reflect.defineMetadata(METADATA_KEYS.INJECTABLE, true, target);
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
__name(Injectable, "Injectable");
|
|
308
|
+
|
|
309
|
+
// src/decorators/CommandGroup.ts
|
|
310
|
+
var import_reflect_metadata8 = require("reflect-metadata");
|
|
311
|
+
function CommandGroup(name) {
|
|
312
|
+
return (target) => {
|
|
313
|
+
Reflect.defineMetadata(METADATA_KEYS.COMMAND_GROUP, name, target);
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
__name(CommandGroup, "CommandGroup");
|
|
317
|
+
|
|
318
|
+
// src/decorators/SubCommand.ts
|
|
319
|
+
var import_reflect_metadata9 = require("reflect-metadata");
|
|
320
|
+
function SubCommand(options) {
|
|
321
|
+
const opts = typeof options === "string" ? {
|
|
322
|
+
name: options
|
|
323
|
+
} : options;
|
|
324
|
+
return (target, propertyKey) => {
|
|
325
|
+
Reflect.defineMetadata(METADATA_KEYS.SUBCOMMAND, opts, target, propertyKey);
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
__name(SubCommand, "SubCommand");
|
|
195
329
|
|
|
196
330
|
// src/decorators/utils.ts
|
|
197
|
-
function buildSimpleCommandMetadata(options, methodName, category) {
|
|
331
|
+
function buildSimpleCommandMetadata(options, methodName, category, params) {
|
|
198
332
|
return {
|
|
199
333
|
name: options.name ?? methodName.toLowerCase(),
|
|
200
334
|
description: options.description ?? "No description provided",
|
|
@@ -202,11 +336,30 @@ function buildSimpleCommandMetadata(options, methodName, category) {
|
|
|
202
336
|
permissions: options.permissions ?? [],
|
|
203
337
|
category: options.category ?? category ?? "uncategorized",
|
|
204
338
|
cooldown: options.cooldown ?? 0,
|
|
205
|
-
...options.cooldownStorage !== void 0 ? {
|
|
339
|
+
...options.cooldownStorage !== void 0 ? {
|
|
340
|
+
cooldownStorage: options.cooldownStorage
|
|
341
|
+
} : {},
|
|
206
342
|
nsfw: options.nsfw ?? false,
|
|
207
|
-
ownerOnly: options.ownerOnly ?? false
|
|
343
|
+
ownerOnly: options.ownerOnly ?? false,
|
|
344
|
+
params
|
|
208
345
|
};
|
|
209
346
|
}
|
|
347
|
+
__name(buildSimpleCommandMetadata, "buildSimpleCommandMetadata");
|
|
348
|
+
function getSubCommands(target) {
|
|
349
|
+
const methods = Object.getOwnPropertyNames(target.prototype);
|
|
350
|
+
const subCommands = [];
|
|
351
|
+
for (const method of methods) {
|
|
352
|
+
const options = Reflect.getMetadata(METADATA_KEYS.SUBCOMMAND, target.prototype, method);
|
|
353
|
+
if (options) {
|
|
354
|
+
subCommands.push({
|
|
355
|
+
methodName: method,
|
|
356
|
+
options
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return subCommands;
|
|
361
|
+
}
|
|
362
|
+
__name(getSubCommands, "getSubCommands");
|
|
210
363
|
|
|
211
364
|
// src/registry.ts
|
|
212
365
|
var path = __toESM(require("path"));
|
|
@@ -214,6 +367,9 @@ var fs = __toESM(require("fs/promises"));
|
|
|
214
367
|
var import_node_url = require("url");
|
|
215
368
|
var import_tinyglobby = require("tinyglobby");
|
|
216
369
|
var CommandRegistry = class _CommandRegistry {
|
|
370
|
+
static {
|
|
371
|
+
__name(this, "CommandRegistry");
|
|
372
|
+
}
|
|
217
373
|
static DEFAULT_AUTO_DISCOVERY_IGNORES = [
|
|
218
374
|
"**/node_modules/**",
|
|
219
375
|
"**/.git/**",
|
|
@@ -226,23 +382,27 @@ var CommandRegistry = class _CommandRegistry {
|
|
|
226
382
|
registeredEvents = [];
|
|
227
383
|
extensions;
|
|
228
384
|
processedStoatClasses = /* @__PURE__ */ new Set();
|
|
229
|
-
|
|
385
|
+
container;
|
|
386
|
+
constructor(container, extensions = [
|
|
387
|
+
".js",
|
|
388
|
+
".mjs",
|
|
389
|
+
".cjs"
|
|
390
|
+
]) {
|
|
391
|
+
this.container = container;
|
|
230
392
|
this.extensions = extensions;
|
|
231
393
|
}
|
|
232
|
-
/**
|
|
233
|
-
* Get the number of registered commands
|
|
234
|
-
*/
|
|
235
394
|
get size() {
|
|
236
395
|
return this.commands.size;
|
|
237
396
|
}
|
|
238
|
-
/**
|
|
239
|
-
* Load commands from a directory using glob pattern matching
|
|
240
|
-
*/
|
|
241
397
|
async loadFromDirectory(directory) {
|
|
242
398
|
const patterns = this.extensions.map((ext) => path.join(directory, "**", `*${ext}`).replace(/\\/g, "/"));
|
|
243
399
|
for (const pattern of patterns) {
|
|
244
400
|
const files = await (0, import_tinyglobby.glob)(pattern, {
|
|
245
|
-
ignore: [
|
|
401
|
+
ignore: [
|
|
402
|
+
"**/*.d.ts",
|
|
403
|
+
"**/*.test.ts",
|
|
404
|
+
"**/*.spec.ts"
|
|
405
|
+
],
|
|
246
406
|
absolute: true
|
|
247
407
|
});
|
|
248
408
|
for (const file of files) {
|
|
@@ -251,26 +411,26 @@ var CommandRegistry = class _CommandRegistry {
|
|
|
251
411
|
}
|
|
252
412
|
console.log(`[Stoatx] Loaded ${this.commands.size} command(s) and ${this.registeredEvents.length} event(s)`);
|
|
253
413
|
}
|
|
254
|
-
/**
|
|
255
|
-
* Auto-discover command files across one or more roots.
|
|
256
|
-
*/
|
|
257
414
|
async autoDiscover(options = {}) {
|
|
258
|
-
const roots = options.roots?.length ? options.roots : [
|
|
415
|
+
const roots = options.roots?.length ? options.roots : [
|
|
416
|
+
process.cwd()
|
|
417
|
+
];
|
|
259
418
|
const includePatterns = options.include?.length ? options.include : this.getDefaultAutoDiscoveryPatterns();
|
|
260
|
-
const patterns = roots.flatMap(
|
|
261
|
-
(root) => includePatterns.map((pattern) => path.join(root, pattern).replace(/\\/g, "/"))
|
|
262
|
-
);
|
|
419
|
+
const patterns = roots.flatMap((root) => includePatterns.map((pattern) => path.join(root, pattern).replace(/\\/g, "/")));
|
|
263
420
|
const files = await (0, import_tinyglobby.glob)(patterns, {
|
|
264
|
-
ignore: [
|
|
421
|
+
ignore: [
|
|
422
|
+
..._CommandRegistry.DEFAULT_AUTO_DISCOVERY_IGNORES,
|
|
423
|
+
...options.ignore ?? []
|
|
424
|
+
],
|
|
265
425
|
absolute: true
|
|
266
426
|
});
|
|
267
|
-
const uniqueFiles = [
|
|
268
|
-
|
|
427
|
+
const uniqueFiles = [
|
|
428
|
+
...new Set(files)
|
|
429
|
+
];
|
|
269
430
|
for (const file of uniqueFiles) {
|
|
270
431
|
if (!await this.isLikelyCommandModule(file)) {
|
|
271
432
|
continue;
|
|
272
433
|
}
|
|
273
|
-
candidateFiles++;
|
|
274
434
|
const baseDir = roots.find((root) => {
|
|
275
435
|
const relative2 = path.relative(root, file);
|
|
276
436
|
return relative2 && !relative2.startsWith("..") && !path.isAbsolute(relative2);
|
|
@@ -290,17 +450,22 @@ var CommandRegistry = class _CommandRegistry {
|
|
|
290
450
|
return true;
|
|
291
451
|
}
|
|
292
452
|
}
|
|
293
|
-
/**
|
|
294
|
-
* Register a command instance
|
|
295
|
-
*/
|
|
296
453
|
register(instance, metadata, classConstructor, methodName) {
|
|
297
|
-
const
|
|
454
|
+
const groupOptions = Reflect.getMetadata(METADATA_KEYS.COMMAND_GROUP, classConstructor);
|
|
455
|
+
const subCommandOptions = Reflect.getMetadata(METADATA_KEYS.SUBCOMMAND, instance, methodName);
|
|
456
|
+
const subCommandName = subCommandOptions?.name;
|
|
457
|
+
const name = groupOptions && subCommandName ? `${groupOptions.name}:${subCommandName}`.toLowerCase() : metadata.name.toLowerCase();
|
|
298
458
|
if (this.commands.has(name)) {
|
|
299
459
|
console.warn(`[Stoatx] Duplicate command name: ${name}. Skipping...`);
|
|
300
460
|
return;
|
|
301
461
|
}
|
|
302
462
|
this.validateGuards(classConstructor, metadata.name);
|
|
303
|
-
this.commands.set(name, {
|
|
463
|
+
this.commands.set(name, {
|
|
464
|
+
instance,
|
|
465
|
+
metadata,
|
|
466
|
+
methodName,
|
|
467
|
+
classConstructor
|
|
468
|
+
});
|
|
304
469
|
for (const alias of metadata.aliases) {
|
|
305
470
|
const aliasLower = alias.toLowerCase();
|
|
306
471
|
if (this.aliases.has(aliasLower) || this.commands.has(aliasLower)) {
|
|
@@ -310,42 +475,24 @@ var CommandRegistry = class _CommandRegistry {
|
|
|
310
475
|
this.aliases.set(aliasLower, name);
|
|
311
476
|
}
|
|
312
477
|
}
|
|
313
|
-
/**
|
|
314
|
-
* Get a command by name or alias
|
|
315
|
-
*/
|
|
316
478
|
get(name) {
|
|
317
479
|
const lowerName = name.toLowerCase();
|
|
318
480
|
const resolvedName = this.aliases.get(lowerName) ?? lowerName;
|
|
319
481
|
return this.commands.get(resolvedName);
|
|
320
482
|
}
|
|
321
|
-
/**
|
|
322
|
-
* Check if a command exists
|
|
323
|
-
*/
|
|
324
483
|
has(name) {
|
|
325
484
|
const lowerName = name.toLowerCase();
|
|
326
485
|
return this.commands.has(lowerName) || this.aliases.has(lowerName);
|
|
327
486
|
}
|
|
328
|
-
/**
|
|
329
|
-
* Get all registered commands
|
|
330
|
-
*/
|
|
331
487
|
getAll() {
|
|
332
488
|
return Array.from(this.commands.values());
|
|
333
489
|
}
|
|
334
|
-
/**
|
|
335
|
-
* Get all command metadata
|
|
336
|
-
*/
|
|
337
490
|
getAllMetadata() {
|
|
338
491
|
return this.getAll().map((c) => c.metadata);
|
|
339
492
|
}
|
|
340
|
-
/**
|
|
341
|
-
* Get all registered events
|
|
342
|
-
*/
|
|
343
493
|
getEvents() {
|
|
344
494
|
return this.registeredEvents;
|
|
345
495
|
}
|
|
346
|
-
/**
|
|
347
|
-
* Get commands grouped by category
|
|
348
|
-
*/
|
|
349
496
|
getByCategory() {
|
|
350
497
|
const categories = /* @__PURE__ */ new Map();
|
|
351
498
|
for (const cmd of this.commands.values()) {
|
|
@@ -356,95 +503,70 @@ var CommandRegistry = class _CommandRegistry {
|
|
|
356
503
|
}
|
|
357
504
|
return categories;
|
|
358
505
|
}
|
|
359
|
-
/**
|
|
360
|
-
* Clear all commands
|
|
361
|
-
*/
|
|
362
506
|
clear() {
|
|
363
507
|
this.commands.clear();
|
|
364
508
|
this.aliases.clear();
|
|
365
509
|
this.registeredEvents.length = 0;
|
|
366
510
|
this.processedStoatClasses.clear();
|
|
367
511
|
}
|
|
368
|
-
/**
|
|
369
|
-
* Iterate over commands
|
|
370
|
-
*/
|
|
371
512
|
[Symbol.iterator]() {
|
|
372
513
|
return this.commands.entries();
|
|
373
514
|
}
|
|
374
|
-
/**
|
|
375
|
-
* Iterate over command values
|
|
376
|
-
*/
|
|
377
515
|
values() {
|
|
378
516
|
return this.commands.values();
|
|
379
517
|
}
|
|
380
|
-
/**
|
|
381
|
-
* Iterate over command names
|
|
382
|
-
*/
|
|
383
518
|
keys() {
|
|
384
519
|
return this.commands.keys();
|
|
385
520
|
}
|
|
386
|
-
/**
|
|
387
|
-
* Validate that all guards on a command implement the required methods
|
|
388
|
-
* @param commandClass
|
|
389
|
-
* @param commandName
|
|
390
|
-
* @private
|
|
391
|
-
*/
|
|
392
521
|
validateGuards(commandClass, commandName) {
|
|
393
|
-
const guards = Reflect.getMetadata(
|
|
522
|
+
const guards = Reflect.getMetadata(METADATA_KEYS.GUARDS, commandClass) || [];
|
|
394
523
|
for (const GuardClass of guards) {
|
|
395
|
-
const guardInstance =
|
|
524
|
+
const guardInstance = this.container.resolve(GuardClass);
|
|
396
525
|
if (typeof guardInstance.run !== "function") {
|
|
397
|
-
console.error(
|
|
398
|
-
`[Stoatx] FATAL: Guard "${GuardClass.name}" on command "${commandName}" does not have a run() method.`
|
|
399
|
-
);
|
|
526
|
+
console.error(`[Stoatx] FATAL: Guard "${GuardClass.name}" on command "${commandName}" does not have a run() method.`);
|
|
400
527
|
process.exit(1);
|
|
401
528
|
}
|
|
402
529
|
if (typeof guardInstance.guardFail !== "function") {
|
|
403
|
-
console.error(
|
|
404
|
-
`[Stoatx] FATAL: Guard "${GuardClass.name}" on command "${commandName}" does not have a guardFail() method.`
|
|
405
|
-
);
|
|
406
|
-
console.error(`[Stoatx] All guards must implement guardFail() to handle failed checks.`);
|
|
530
|
+
console.error(`[Stoatx] FATAL: Guard "${GuardClass.name}" on command "${commandName}" does not have a guardFail() method.`);
|
|
407
531
|
process.exit(1);
|
|
408
532
|
}
|
|
409
533
|
}
|
|
410
534
|
}
|
|
411
|
-
/**
|
|
412
|
-
* Load commands from a single file
|
|
413
|
-
*/
|
|
414
535
|
async loadFile(filePath, baseDir) {
|
|
415
536
|
try {
|
|
416
537
|
const knownStoatClasses = new Set(decoratorStore.getStoatClasses().keys());
|
|
417
538
|
const fileUrl = (0, import_node_url.pathToFileURL)(filePath).href;
|
|
418
539
|
await import(fileUrl);
|
|
419
540
|
const allStoatClasses = decoratorStore.getStoatClasses();
|
|
420
|
-
for (const [stoatClass
|
|
541
|
+
for (const [stoatClass] of allStoatClasses.entries()) {
|
|
421
542
|
if (knownStoatClasses.has(stoatClass) || this.processedStoatClasses.has(stoatClass)) {
|
|
422
543
|
continue;
|
|
423
544
|
}
|
|
424
|
-
this.registerStoatClassCommands(stoatClass,
|
|
545
|
+
this.registerStoatClassCommands(stoatClass, filePath, baseDir);
|
|
425
546
|
}
|
|
426
547
|
} catch (error) {
|
|
427
548
|
console.error(`[Stoatx] Failed to load command file: ${filePath}`, error);
|
|
428
549
|
}
|
|
429
550
|
}
|
|
430
|
-
registerStoatClassCommands(stoatClass,
|
|
551
|
+
registerStoatClassCommands(stoatClass, filePath, baseDir) {
|
|
552
|
+
const instance = this.container.resolve(stoatClass);
|
|
431
553
|
const simpleCommands = getSimpleCommands(stoatClass);
|
|
554
|
+
const subCommands = getSubCommands(stoatClass);
|
|
432
555
|
const events = getEventsMetadata(stoatClass);
|
|
433
556
|
const category = this.getCategoryFromPath(filePath, baseDir);
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
557
|
+
const allCommands = [
|
|
558
|
+
...simpleCommands,
|
|
559
|
+
...subCommands
|
|
560
|
+
];
|
|
561
|
+
if (allCommands.length === 0 && events.length === 0) {
|
|
438
562
|
this.processedStoatClasses.add(stoatClass);
|
|
439
563
|
return;
|
|
440
564
|
}
|
|
441
|
-
for (const cmdDef of
|
|
565
|
+
for (const cmdDef of allCommands) {
|
|
442
566
|
const method = instance[cmdDef.methodName];
|
|
443
|
-
if (typeof method !== "function")
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
}
|
|
447
|
-
const metadata = buildSimpleCommandMetadata(cmdDef.options, cmdDef.methodName, category);
|
|
567
|
+
if (typeof method !== "function") continue;
|
|
568
|
+
const params = this.buildParamSchema(stoatClass.prototype, cmdDef.methodName);
|
|
569
|
+
const metadata = buildSimpleCommandMetadata(cmdDef.options, cmdDef.methodName, category, params);
|
|
448
570
|
this.register(instance, metadata, stoatClass, cmdDef.methodName);
|
|
449
571
|
}
|
|
450
572
|
for (const eventDef of events) {
|
|
@@ -463,21 +585,180 @@ var CommandRegistry = class _CommandRegistry {
|
|
|
463
585
|
this.processedStoatClasses.add(stoatClass);
|
|
464
586
|
}
|
|
465
587
|
/**
|
|
466
|
-
|
|
467
|
-
|
|
588
|
+
* Build the parameter schema for a command method by combining
|
|
589
|
+
* reflect-metadata param types with @Arg/@Option decorator metadata.
|
|
590
|
+
*/
|
|
591
|
+
buildParamSchema(prototype, methodName) {
|
|
592
|
+
const paramTypes = Reflect.getMetadata("design:paramtypes", prototype, methodName) ?? [];
|
|
593
|
+
const argDefs = getArgs(prototype, methodName);
|
|
594
|
+
const optionDefs = getOptions(prototype, methodName);
|
|
595
|
+
const argByIndex = new Map(argDefs.map((a) => [
|
|
596
|
+
a.index,
|
|
597
|
+
a
|
|
598
|
+
]));
|
|
599
|
+
const optionByIndex = new Map(optionDefs.map((o) => [
|
|
600
|
+
o.index,
|
|
601
|
+
o
|
|
602
|
+
]));
|
|
603
|
+
const params = [];
|
|
604
|
+
for (let i = 0; i < paramTypes.length; i++) {
|
|
605
|
+
const reflectedType = paramTypes[i];
|
|
606
|
+
if (optionByIndex.has(i)) {
|
|
607
|
+
const optDef = optionByIndex.get(i);
|
|
608
|
+
const resolvedType = reflectedType ? PARAM_TYPE_MAP.get(reflectedType) ?? "string" : "string";
|
|
609
|
+
params.push({
|
|
610
|
+
index: i,
|
|
611
|
+
kind: "option",
|
|
612
|
+
resolvedType,
|
|
613
|
+
name: optDef.name,
|
|
614
|
+
required: optDef.required
|
|
615
|
+
});
|
|
616
|
+
continue;
|
|
617
|
+
}
|
|
618
|
+
if (argByIndex.has(i)) {
|
|
619
|
+
const argDef = argByIndex.get(i);
|
|
620
|
+
const resolvedType = reflectedType ? PARAM_TYPE_MAP.get(reflectedType) ?? "string" : "string";
|
|
621
|
+
params.push({
|
|
622
|
+
index: i,
|
|
623
|
+
kind: "arg",
|
|
624
|
+
resolvedType,
|
|
625
|
+
required: argDef.required,
|
|
626
|
+
fetch: argDef.fetch
|
|
627
|
+
});
|
|
628
|
+
continue;
|
|
629
|
+
}
|
|
630
|
+
params.push({
|
|
631
|
+
index: i,
|
|
632
|
+
kind: "ctx",
|
|
633
|
+
resolvedType: "ctx"
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
return params;
|
|
637
|
+
}
|
|
468
638
|
getCategoryFromPath(filePath, baseDir) {
|
|
469
639
|
const relative2 = path.relative(baseDir, filePath);
|
|
470
640
|
const parts = relative2.split(path.sep);
|
|
471
|
-
|
|
472
|
-
|
|
641
|
+
return parts.length > 1 ? parts[0] : void 0;
|
|
642
|
+
}
|
|
643
|
+
};
|
|
644
|
+
|
|
645
|
+
// src/handler.ts
|
|
646
|
+
var import_reflect_metadata11 = require("reflect-metadata");
|
|
647
|
+
|
|
648
|
+
// src/error.ts
|
|
649
|
+
var StoatxError = class extends Error {
|
|
650
|
+
static {
|
|
651
|
+
__name(this, "StoatxError");
|
|
652
|
+
}
|
|
653
|
+
constructor(message) {
|
|
654
|
+
super(message);
|
|
655
|
+
this.name = "StoatxError";
|
|
656
|
+
}
|
|
657
|
+
};
|
|
658
|
+
var CommandValidationError = class extends StoatxError {
|
|
659
|
+
static {
|
|
660
|
+
__name(this, "CommandValidationError");
|
|
661
|
+
}
|
|
662
|
+
paramName;
|
|
663
|
+
paramKind;
|
|
664
|
+
constructor(paramName, paramKind, message) {
|
|
665
|
+
super(message), this.paramName = paramName, this.paramKind = paramKind;
|
|
666
|
+
this.name = "CommandValidationError";
|
|
667
|
+
}
|
|
668
|
+
};
|
|
669
|
+
var MissingArgumentError = class extends CommandValidationError {
|
|
670
|
+
static {
|
|
671
|
+
__name(this, "MissingArgumentError");
|
|
672
|
+
}
|
|
673
|
+
constructor(paramName) {
|
|
674
|
+
super(paramName, "arg", `Missing required argument: \`<${paramName}>\``);
|
|
675
|
+
this.name = "MissingArgumentError";
|
|
676
|
+
}
|
|
677
|
+
};
|
|
678
|
+
var MissingOptionError = class extends CommandValidationError {
|
|
679
|
+
static {
|
|
680
|
+
__name(this, "MissingOptionError");
|
|
681
|
+
}
|
|
682
|
+
constructor(paramName, flagPrefix) {
|
|
683
|
+
super(paramName, "option", `Missing required option: \`${flagPrefix.repeat(2)}${paramName}\``);
|
|
684
|
+
this.name = "MissingOptionError";
|
|
685
|
+
}
|
|
686
|
+
};
|
|
687
|
+
var InvalidTypeError = class extends CommandValidationError {
|
|
688
|
+
static {
|
|
689
|
+
__name(this, "InvalidTypeError");
|
|
690
|
+
}
|
|
691
|
+
expected;
|
|
692
|
+
received;
|
|
693
|
+
constructor(paramName, paramKind, expected, received) {
|
|
694
|
+
super(paramName, paramKind, `Invalid value for \`${paramName}\`. Expected ${expected}, got \`${received}\`.`), this.expected = expected, this.received = received;
|
|
695
|
+
this.name = "InvalidTypeError";
|
|
696
|
+
}
|
|
697
|
+
};
|
|
698
|
+
var InvalidMentionError = class extends CommandValidationError {
|
|
699
|
+
static {
|
|
700
|
+
__name(this, "InvalidMentionError");
|
|
701
|
+
}
|
|
702
|
+
mentionKind;
|
|
703
|
+
rawValue;
|
|
704
|
+
constructor(paramName, paramKind, mentionKind, rawValue) {
|
|
705
|
+
super(paramName, paramKind, `Invalid ${mentionKind} mention for \`${paramName}\`.`), this.mentionKind = mentionKind, this.rawValue = rawValue;
|
|
706
|
+
this.name = "InvalidMentionError";
|
|
707
|
+
}
|
|
708
|
+
};
|
|
709
|
+
var FetchFailedError = class extends CommandValidationError {
|
|
710
|
+
static {
|
|
711
|
+
__name(this, "FetchFailedError");
|
|
712
|
+
}
|
|
713
|
+
mentionKind;
|
|
714
|
+
resolvedId;
|
|
715
|
+
constructor(paramName, paramKind, mentionKind, resolvedId) {
|
|
716
|
+
super(paramName, paramKind, `Could not fetch ${mentionKind} \`${resolvedId}\` for \`${paramName}\`.`), this.mentionKind = mentionKind, this.resolvedId = resolvedId;
|
|
717
|
+
this.name = "FetchFailedError";
|
|
718
|
+
}
|
|
719
|
+
};
|
|
720
|
+
var NoServerContextError = class extends CommandValidationError {
|
|
721
|
+
static {
|
|
722
|
+
__name(this, "NoServerContextError");
|
|
723
|
+
}
|
|
724
|
+
constructor(paramName, paramKind) {
|
|
725
|
+
super(paramName, paramKind, `Cannot fetch role for \`${paramName}\` outside of a server.`);
|
|
726
|
+
this.name = "NoServerContextError";
|
|
727
|
+
}
|
|
728
|
+
};
|
|
729
|
+
|
|
730
|
+
// src/di/Container.ts
|
|
731
|
+
var import_reflect_metadata10 = require("reflect-metadata");
|
|
732
|
+
var StoatxContainer = class {
|
|
733
|
+
static {
|
|
734
|
+
__name(this, "StoatxContainer");
|
|
735
|
+
}
|
|
736
|
+
instances = /* @__PURE__ */ new Map();
|
|
737
|
+
/**
|
|
738
|
+
* Resolves a class instance, injecting any required dependencies.
|
|
739
|
+
*/
|
|
740
|
+
resolve(target) {
|
|
741
|
+
if (this.instances.has(target)) {
|
|
742
|
+
return this.instances.get(target);
|
|
473
743
|
}
|
|
474
|
-
|
|
744
|
+
const paramTypes = Reflect.getMetadata("design:paramtypes", target) || [];
|
|
745
|
+
const injections = paramTypes.map((param) => {
|
|
746
|
+
if (!param) {
|
|
747
|
+
throw new Error(`[Stoatx DI] Cannot resolve dependency for ${target.name}. Ensure all injected services are decorated with @Injectable().`);
|
|
748
|
+
}
|
|
749
|
+
return this.resolve(param);
|
|
750
|
+
});
|
|
751
|
+
const instance = new target(...injections);
|
|
752
|
+
this.instances.set(target, instance);
|
|
753
|
+
return instance;
|
|
475
754
|
}
|
|
476
755
|
};
|
|
477
756
|
|
|
478
757
|
// src/handler.ts
|
|
479
|
-
var import_reflect_metadata5 = require("reflect-metadata");
|
|
480
758
|
var DefaultCooldownManager = class {
|
|
759
|
+
static {
|
|
760
|
+
__name(this, "DefaultCooldownManager");
|
|
761
|
+
}
|
|
481
762
|
cooldowns = /* @__PURE__ */ new Map();
|
|
482
763
|
check(ctx, metadata) {
|
|
483
764
|
if (metadata.cooldown <= 0) return true;
|
|
@@ -510,27 +791,33 @@ var DefaultCooldownManager = class {
|
|
|
510
791
|
}
|
|
511
792
|
};
|
|
512
793
|
var StoatxHandler = class {
|
|
794
|
+
static {
|
|
795
|
+
__name(this, "StoatxHandler");
|
|
796
|
+
}
|
|
513
797
|
commandsDir;
|
|
514
798
|
discoveryOptions;
|
|
799
|
+
// After
|
|
515
800
|
prefixResolver;
|
|
516
801
|
owners;
|
|
517
802
|
registry;
|
|
518
803
|
cooldownManager;
|
|
519
804
|
disableMentionPrefix;
|
|
520
805
|
client;
|
|
806
|
+
flagPrefix;
|
|
807
|
+
globalGuards;
|
|
808
|
+
container = new StoatxContainer();
|
|
521
809
|
constructor(options) {
|
|
522
810
|
this.client = options.client;
|
|
523
811
|
this.commandsDir = options.commandsDir;
|
|
524
812
|
this.discoveryOptions = options.discovery;
|
|
525
813
|
this.prefixResolver = options.prefix;
|
|
526
814
|
this.owners = new Set(options.owners ?? []);
|
|
527
|
-
this.registry = new CommandRegistry(options.extensions);
|
|
815
|
+
this.registry = new CommandRegistry(this.container, options.extensions);
|
|
528
816
|
this.disableMentionPrefix = options.disableMentionPrefix ?? false;
|
|
529
817
|
this.cooldownManager = options.cooldownManager ?? new DefaultCooldownManager();
|
|
818
|
+
this.flagPrefix = options.flagPrefix || "-";
|
|
819
|
+
this.globalGuards = options.globalGuards ?? [];
|
|
530
820
|
}
|
|
531
|
-
/**
|
|
532
|
-
* Initialize the handler - load all commands
|
|
533
|
-
*/
|
|
534
821
|
async init() {
|
|
535
822
|
if (this.commandsDir) {
|
|
536
823
|
await this.registry.loadFromDirectory(this.commandsDir);
|
|
@@ -539,22 +826,16 @@ var StoatxHandler = class {
|
|
|
539
826
|
}
|
|
540
827
|
this.attachEvents();
|
|
541
828
|
}
|
|
542
|
-
/**
|
|
543
|
-
* Attach registered events to the client
|
|
544
|
-
*/
|
|
545
829
|
attachEvents() {
|
|
546
830
|
const events = this.registry.getEvents();
|
|
547
831
|
for (const eventDef of events) {
|
|
548
|
-
const handler = async (...args) => {
|
|
832
|
+
const handler = /* @__PURE__ */ __name(async (...args) => {
|
|
549
833
|
try {
|
|
550
834
|
await eventDef.instance[eventDef.methodName](...args, this.client);
|
|
551
835
|
} catch (error) {
|
|
552
|
-
console.error(
|
|
553
|
-
`[Stoatx] Event Handler Error in @${eventDef.type === "on" ? "On" : "Once"}('${eventDef.event}'):`,
|
|
554
|
-
error
|
|
555
|
-
);
|
|
836
|
+
console.error(`[Stoatx] Event Handler Error in @${eventDef.type === "on" ? "On" : "Once"}('${eventDef.event}'):`, error);
|
|
556
837
|
}
|
|
557
|
-
};
|
|
838
|
+
}, "handler");
|
|
558
839
|
const eventName = eventDef.event;
|
|
559
840
|
if (eventDef.type === "once") {
|
|
560
841
|
this.client.once(eventName, handler);
|
|
@@ -563,73 +844,86 @@ var StoatxHandler = class {
|
|
|
563
844
|
}
|
|
564
845
|
}
|
|
565
846
|
}
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
847
|
+
parseRawInput(rawArgs) {
|
|
848
|
+
const args = [];
|
|
849
|
+
const flags = {};
|
|
850
|
+
for (let i = 0; i < rawArgs.length; i++) {
|
|
851
|
+
const arg = rawArgs[i];
|
|
852
|
+
if (arg === void 0) continue;
|
|
853
|
+
if (arg.startsWith(this.flagPrefix)) {
|
|
854
|
+
let key = arg;
|
|
855
|
+
while (key.startsWith(this.flagPrefix)) {
|
|
856
|
+
key = key.slice(this.flagPrefix.length);
|
|
857
|
+
}
|
|
858
|
+
const nextArg = rawArgs[i + 1];
|
|
859
|
+
if (nextArg !== void 0 && !nextArg.startsWith(this.flagPrefix)) {
|
|
860
|
+
flags[key] = nextArg;
|
|
861
|
+
i++;
|
|
862
|
+
} else {
|
|
863
|
+
flags[key] = true;
|
|
864
|
+
}
|
|
865
|
+
} else {
|
|
866
|
+
args.push(arg);
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
return {
|
|
870
|
+
args,
|
|
871
|
+
flags
|
|
872
|
+
};
|
|
873
|
+
}
|
|
569
874
|
async parseMessage(rawContent, message, meta) {
|
|
570
|
-
const
|
|
571
|
-
let usedPrefix =
|
|
875
|
+
const prefixes = await this.resolvePrefix(meta.serverId);
|
|
876
|
+
let usedPrefix = "";
|
|
572
877
|
let withoutPrefix = "";
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
usedPrefix =
|
|
576
|
-
|
|
577
|
-
|
|
878
|
+
const matchedPrefix = prefixes.find((p) => rawContent.startsWith(p));
|
|
879
|
+
if (matchedPrefix !== void 0) {
|
|
880
|
+
usedPrefix = matchedPrefix;
|
|
881
|
+
withoutPrefix = rawContent.slice(matchedPrefix.length).trim();
|
|
882
|
+
} else if (!this.disableMentionPrefix && rawContent.match(/^<@!?\w+>/)) {
|
|
883
|
+
const mentionMatch = rawContent.match(/^<@!?(\w+)>\s*/);
|
|
578
884
|
if (mentionMatch) {
|
|
579
885
|
const mentionedId = mentionMatch[1];
|
|
580
886
|
const botId = this.client.user?.id;
|
|
581
887
|
if (botId && mentionedId === botId) {
|
|
582
888
|
usedPrefix = mentionMatch[0];
|
|
583
889
|
withoutPrefix = rawContent.slice(mentionMatch[0].length).trim();
|
|
584
|
-
} else {
|
|
585
890
|
}
|
|
586
891
|
}
|
|
587
892
|
}
|
|
588
|
-
if (!withoutPrefix)
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
const
|
|
592
|
-
if (!
|
|
593
|
-
|
|
893
|
+
if (!withoutPrefix) return null;
|
|
894
|
+
const parts = withoutPrefix.split(/\s+/);
|
|
895
|
+
const part1 = parts[0];
|
|
896
|
+
const part2 = parts[1];
|
|
897
|
+
if (!part1) return null;
|
|
898
|
+
let commandKey = part1.toLowerCase();
|
|
899
|
+
let remainingParts = parts.slice(1);
|
|
900
|
+
if (part2 && this.registry.has(`${part1}:${part2}`)) {
|
|
901
|
+
commandKey = `${part1}:${part2}`.toLowerCase();
|
|
902
|
+
remainingParts = parts.slice(2);
|
|
594
903
|
}
|
|
904
|
+
const { args, flags } = this.parseRawInput(remainingParts);
|
|
595
905
|
return {
|
|
596
906
|
client: this.client,
|
|
597
907
|
content: rawContent,
|
|
598
908
|
authorId: meta.authorId,
|
|
599
909
|
channelId: meta.channelId,
|
|
600
910
|
serverId: meta.serverId,
|
|
601
|
-
args,
|
|
602
911
|
prefix: usedPrefix,
|
|
603
|
-
commandName:
|
|
912
|
+
commandName: commandKey,
|
|
604
913
|
reply: meta.reply,
|
|
605
|
-
message
|
|
914
|
+
message,
|
|
915
|
+
_rawArgs: args,
|
|
916
|
+
_rawFlags: flags
|
|
606
917
|
};
|
|
607
918
|
}
|
|
608
|
-
/**
|
|
609
|
-
* Handle a message object using the configured message adapter
|
|
610
|
-
*
|
|
611
|
-
* @example
|
|
612
|
-
* ```ts
|
|
613
|
-
* // With message adapter configured
|
|
614
|
-
* client.on('messageCreate', (message) => {
|
|
615
|
-
* handler.handle(message);
|
|
616
|
-
* });
|
|
617
|
-
* ```
|
|
618
|
-
*/
|
|
619
919
|
async handle(message) {
|
|
620
|
-
if (!message.channel || !message.author || !message.content)
|
|
621
|
-
|
|
622
|
-
}
|
|
623
|
-
if (message.author.bot) {
|
|
624
|
-
return false;
|
|
625
|
-
}
|
|
920
|
+
if (!message.channel || !message.author || !message.content) return false;
|
|
921
|
+
if (message.author.bot) return false;
|
|
626
922
|
const rawContent = message.content;
|
|
627
923
|
const authorId = message.author.id;
|
|
628
924
|
const channelId = message.channel.id;
|
|
629
925
|
const serverId = message.server?.id;
|
|
630
|
-
const reply = async (content) =>
|
|
631
|
-
return await message.channel.send(content);
|
|
632
|
-
};
|
|
926
|
+
const reply = /* @__PURE__ */ __name(async (content) => await message.channel.send(content), "reply");
|
|
633
927
|
await this.handleMessage(rawContent, message, {
|
|
634
928
|
authorId,
|
|
635
929
|
channelId,
|
|
@@ -638,43 +932,20 @@ var StoatxHandler = class {
|
|
|
638
932
|
});
|
|
639
933
|
return true;
|
|
640
934
|
}
|
|
641
|
-
/**
|
|
642
|
-
* Handle a raw message string with metadata
|
|
643
|
-
*
|
|
644
|
-
* @example
|
|
645
|
-
* ```ts
|
|
646
|
-
* // Manual usage without message adapter
|
|
647
|
-
* client.on('messageCreate', (message) => {
|
|
648
|
-
* handler.handleMessage(message.content, message, {
|
|
649
|
-
* authorId: message.author.id,
|
|
650
|
-
* channelId: message.channel.id,
|
|
651
|
-
* serverId: message.server?.id,
|
|
652
|
-
* reply: (content) => message.channel.sendMessage(content),
|
|
653
|
-
* });
|
|
654
|
-
* });
|
|
655
|
-
* ```
|
|
656
|
-
*/
|
|
657
935
|
async handleMessage(rawContent, message, meta) {
|
|
658
936
|
const ctx = await this.parseMessage(rawContent, message, meta);
|
|
659
|
-
if (!ctx)
|
|
660
|
-
return;
|
|
661
|
-
}
|
|
937
|
+
if (!ctx) return;
|
|
662
938
|
await this.execute(ctx);
|
|
663
939
|
}
|
|
664
|
-
/**
|
|
665
|
-
* Execute a command with the given context
|
|
666
|
-
*/
|
|
667
940
|
async execute(ctx) {
|
|
668
941
|
const registered = this.registry.get(ctx.commandName);
|
|
669
|
-
if (!registered)
|
|
670
|
-
return false;
|
|
671
|
-
}
|
|
942
|
+
if (!registered) return false;
|
|
672
943
|
const { instance, metadata, methodName, classConstructor } = registered;
|
|
673
944
|
if (metadata.ownerOnly && !this.owners.has(ctx.authorId)) {
|
|
674
945
|
await ctx.reply("This command is owner-only.");
|
|
675
946
|
return false;
|
|
676
947
|
}
|
|
677
|
-
if (metadata.permissions) {
|
|
948
|
+
if (metadata.permissions.length > 0) {
|
|
678
949
|
const server = ctx.message.server;
|
|
679
950
|
const member = server ? await server.members.fetch(ctx.authorId) : null;
|
|
680
951
|
if (!member || !member.permissions.has(metadata.permissions)) {
|
|
@@ -687,9 +958,16 @@ var StoatxHandler = class {
|
|
|
687
958
|
return false;
|
|
688
959
|
}
|
|
689
960
|
}
|
|
690
|
-
const
|
|
691
|
-
|
|
692
|
-
|
|
961
|
+
const globalGuards = this.globalGuards;
|
|
962
|
+
const classGuards = Reflect.getMetadata(METADATA_KEYS.GUARDS, classConstructor) || [];
|
|
963
|
+
const methodGuards = Reflect.getMetadata(METADATA_KEYS.GUARDS, instance, methodName) || [];
|
|
964
|
+
const allGuards = [
|
|
965
|
+
...globalGuards,
|
|
966
|
+
...classGuards,
|
|
967
|
+
...methodGuards
|
|
968
|
+
];
|
|
969
|
+
for (const guardClass of allGuards) {
|
|
970
|
+
const guardInstance = this.container.resolve(guardClass);
|
|
693
971
|
if (typeof guardInstance.run === "function") {
|
|
694
972
|
const guardResult = await guardInstance.run(ctx);
|
|
695
973
|
if (!guardResult) {
|
|
@@ -711,42 +989,152 @@ var StoatxHandler = class {
|
|
|
711
989
|
}
|
|
712
990
|
return false;
|
|
713
991
|
}
|
|
992
|
+
const resolvedParams = await this.resolveParams(metadata.params, ctx, instance);
|
|
993
|
+
if (resolvedParams === null) return false;
|
|
714
994
|
try {
|
|
715
995
|
if (metadata.cooldown > 0) {
|
|
716
996
|
await this.cooldownManager.set(ctx, metadata);
|
|
717
997
|
}
|
|
718
|
-
await instance[methodName](
|
|
998
|
+
await instance[methodName](...resolvedParams);
|
|
719
999
|
return true;
|
|
720
1000
|
} catch (error) {
|
|
721
1001
|
if (typeof instance.onError === "function") {
|
|
722
1002
|
await instance.onError(ctx, error);
|
|
723
1003
|
} else {
|
|
724
1004
|
console.error(`[Stoatx] Error in command ${metadata.name}:`, error);
|
|
1005
|
+
await ctx.reply("Something went wrong. Please try again later.");
|
|
725
1006
|
}
|
|
726
1007
|
return false;
|
|
727
1008
|
}
|
|
728
1009
|
}
|
|
729
1010
|
/**
|
|
730
|
-
|
|
731
|
-
|
|
1011
|
+
* Report a validation error to the instance via onValidationError → onError → default reply
|
|
1012
|
+
*/
|
|
1013
|
+
async reportValidationError(instance, ctx, error) {
|
|
1014
|
+
if (typeof instance.onValidationError === "function") {
|
|
1015
|
+
await instance.onValidationError(ctx, error);
|
|
1016
|
+
} else if (typeof instance.onError === "function") {
|
|
1017
|
+
await instance.onError(ctx, error);
|
|
1018
|
+
} else {
|
|
1019
|
+
await ctx.reply(error.message);
|
|
1020
|
+
}
|
|
1021
|
+
return null;
|
|
1022
|
+
}
|
|
1023
|
+
async resolveParams(params, ctx, instance) {
|
|
1024
|
+
const resolved = new Array(params.length);
|
|
1025
|
+
let argCursor = 0;
|
|
1026
|
+
for (const param of params) {
|
|
1027
|
+
if (param.kind === "ctx") {
|
|
1028
|
+
resolved[param.index] = ctx;
|
|
1029
|
+
continue;
|
|
1030
|
+
}
|
|
1031
|
+
if (param.kind === "arg") {
|
|
1032
|
+
const rawValue = ctx._rawArgs[argCursor++];
|
|
1033
|
+
if (rawValue === void 0) {
|
|
1034
|
+
if (param.required) {
|
|
1035
|
+
const paramName = param.name ?? `arg[${param.index}]`;
|
|
1036
|
+
return this.reportValidationError(instance, ctx, new MissingArgumentError(paramName));
|
|
1037
|
+
}
|
|
1038
|
+
resolved[param.index] = void 0;
|
|
1039
|
+
continue;
|
|
1040
|
+
}
|
|
1041
|
+
const value = await this.resolveValue(rawValue, param, ctx, instance, "arg");
|
|
1042
|
+
if (value === null) return null;
|
|
1043
|
+
resolved[param.index] = value;
|
|
1044
|
+
continue;
|
|
1045
|
+
}
|
|
1046
|
+
if (param.kind === "option") {
|
|
1047
|
+
const rawValue = ctx._rawFlags[param.name];
|
|
1048
|
+
if (rawValue === void 0) {
|
|
1049
|
+
if (param.required) {
|
|
1050
|
+
return this.reportValidationError(instance, ctx, new MissingOptionError(param.name, this.flagPrefix));
|
|
1051
|
+
}
|
|
1052
|
+
resolved[param.index] = void 0;
|
|
1053
|
+
continue;
|
|
1054
|
+
}
|
|
1055
|
+
const value = await this.resolveValue(String(rawValue), param, ctx, instance, "option");
|
|
1056
|
+
if (value === null) return null;
|
|
1057
|
+
resolved[param.index] = value;
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
return resolved;
|
|
1061
|
+
}
|
|
1062
|
+
async resolveValue(rawValue, param, ctx, instance, kind) {
|
|
1063
|
+
const paramName = kind === "arg" ? param.name ?? `arg[${param.index}]` : param.name;
|
|
1064
|
+
switch (param.resolvedType) {
|
|
1065
|
+
case "string":
|
|
1066
|
+
return String(rawValue);
|
|
1067
|
+
case "number": {
|
|
1068
|
+
const num = Number(rawValue);
|
|
1069
|
+
if (isNaN(num)) {
|
|
1070
|
+
return this.reportValidationError(instance, ctx, new InvalidTypeError(paramName, kind, "a number", rawValue));
|
|
1071
|
+
}
|
|
1072
|
+
return num;
|
|
1073
|
+
}
|
|
1074
|
+
case "boolean":
|
|
1075
|
+
return rawValue === "false" ? false : Boolean(rawValue);
|
|
1076
|
+
case "user": {
|
|
1077
|
+
const match = rawValue.match(/^(?:<@!?)?([0-7][0-9A-HJKMNP-TV-Z]{25})>?$/i);
|
|
1078
|
+
if (!match) {
|
|
1079
|
+
return this.reportValidationError(instance, ctx, new InvalidMentionError(paramName, kind, "user", rawValue));
|
|
1080
|
+
}
|
|
1081
|
+
const userId = match[1];
|
|
1082
|
+
if (param.fetch) {
|
|
1083
|
+
try {
|
|
1084
|
+
return await this.client.users.fetch(userId);
|
|
1085
|
+
} catch {
|
|
1086
|
+
return this.reportValidationError(instance, ctx, new FetchFailedError(paramName, kind, "user", userId));
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
return this.client.users.cache.get(userId) ?? userId;
|
|
1090
|
+
}
|
|
1091
|
+
case "channel": {
|
|
1092
|
+
const match = rawValue.match(/^(?:<#)?([0-7][0-9A-HJKMNP-TV-Z]{25})>?$/i);
|
|
1093
|
+
if (!match) {
|
|
1094
|
+
return this.reportValidationError(instance, ctx, new InvalidMentionError(paramName, kind, "channel", rawValue));
|
|
1095
|
+
}
|
|
1096
|
+
const channelId = match[1];
|
|
1097
|
+
if (param.fetch) {
|
|
1098
|
+
try {
|
|
1099
|
+
return await this.client.channels.fetch(channelId);
|
|
1100
|
+
} catch {
|
|
1101
|
+
return this.reportValidationError(instance, ctx, new FetchFailedError(paramName, kind, "channel", channelId));
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
return this.client.channels.cache.get(channelId) ?? channelId;
|
|
1105
|
+
}
|
|
1106
|
+
case "role": {
|
|
1107
|
+
const match = rawValue.match(/^(?:<@&)?([0-7][0-9A-HJKMNP-TV-Z]{25})>?$/i);
|
|
1108
|
+
if (!match) {
|
|
1109
|
+
return this.reportValidationError(instance, ctx, new InvalidMentionError(paramName, kind, "role", rawValue));
|
|
1110
|
+
}
|
|
1111
|
+
const roleId = match[1];
|
|
1112
|
+
if (param.fetch) {
|
|
1113
|
+
const server = ctx.message.server;
|
|
1114
|
+
if (!server) {
|
|
1115
|
+
return this.reportValidationError(instance, ctx, new NoServerContextError(paramName, kind));
|
|
1116
|
+
}
|
|
1117
|
+
try {
|
|
1118
|
+
return await server.roles.fetch(roleId);
|
|
1119
|
+
} catch {
|
|
1120
|
+
return this.reportValidationError(instance, ctx, new FetchFailedError(paramName, kind, "role", roleId));
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
return ctx.message.server?.roles.cache.get(roleId) ?? roleId;
|
|
1124
|
+
}
|
|
1125
|
+
default:
|
|
1126
|
+
return rawValue;
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
732
1129
|
getRegistry() {
|
|
733
1130
|
return this.registry;
|
|
734
1131
|
}
|
|
735
|
-
/**
|
|
736
|
-
* Get a command by name or alias
|
|
737
|
-
*/
|
|
738
1132
|
getCommand(name) {
|
|
739
1133
|
return this.registry.get(name);
|
|
740
1134
|
}
|
|
741
|
-
/**
|
|
742
|
-
* Get all commands
|
|
743
|
-
*/
|
|
744
1135
|
getCommands() {
|
|
745
1136
|
return this.registry.getAll();
|
|
746
1137
|
}
|
|
747
|
-
/**
|
|
748
|
-
* Reload all commands
|
|
749
|
-
*/
|
|
750
1138
|
async reload() {
|
|
751
1139
|
this.registry.clear();
|
|
752
1140
|
if (this.cooldownManager.clear) {
|
|
@@ -758,48 +1146,45 @@ var StoatxHandler = class {
|
|
|
758
1146
|
}
|
|
759
1147
|
await this.registry.autoDiscover(this.discoveryOptions);
|
|
760
1148
|
}
|
|
761
|
-
/**
|
|
762
|
-
* Check if a user is an owner
|
|
763
|
-
*/
|
|
764
1149
|
isOwner(userId) {
|
|
765
1150
|
return this.owners.has(userId);
|
|
766
1151
|
}
|
|
767
|
-
/**
|
|
768
|
-
* Add an owner
|
|
769
|
-
*/
|
|
770
1152
|
addOwner(userId) {
|
|
771
1153
|
this.owners.add(userId);
|
|
772
1154
|
}
|
|
773
|
-
/**
|
|
774
|
-
* Remove an owner
|
|
775
|
-
*/
|
|
776
1155
|
removeOwner(userId) {
|
|
777
1156
|
this.owners.delete(userId);
|
|
778
1157
|
}
|
|
779
|
-
/**
|
|
780
|
-
* Resolve the prefix for a context
|
|
781
|
-
*/
|
|
782
1158
|
async resolvePrefix(serverId) {
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
}
|
|
786
|
-
return
|
|
1159
|
+
const result = typeof this.prefixResolver === "function" ? await this.prefixResolver({
|
|
1160
|
+
serverId
|
|
1161
|
+
}) : this.prefixResolver;
|
|
1162
|
+
return Array.isArray(result) ? result : [
|
|
1163
|
+
result
|
|
1164
|
+
];
|
|
787
1165
|
}
|
|
788
1166
|
};
|
|
789
1167
|
|
|
790
1168
|
// src/client.ts
|
|
791
|
-
var
|
|
792
|
-
var Client = class extends
|
|
1169
|
+
var import_client2 = require("@stoatx/client");
|
|
1170
|
+
var Client = class extends import_client2.Client {
|
|
1171
|
+
static {
|
|
1172
|
+
__name(this, "Client");
|
|
1173
|
+
}
|
|
793
1174
|
handler;
|
|
794
1175
|
constructor(options) {
|
|
795
|
-
super();
|
|
796
|
-
this.handler = new StoatxHandler({
|
|
797
|
-
|
|
798
|
-
|
|
1176
|
+
super(options);
|
|
1177
|
+
this.handler = new StoatxHandler({
|
|
1178
|
+
...options,
|
|
1179
|
+
client: this
|
|
799
1180
|
});
|
|
800
1181
|
}
|
|
801
|
-
async
|
|
1182
|
+
async login(token) {
|
|
802
1183
|
await this.handler.init();
|
|
1184
|
+
return super.login(token);
|
|
1185
|
+
}
|
|
1186
|
+
async executeCommand(message) {
|
|
1187
|
+
await this.handler.handle(message);
|
|
803
1188
|
}
|
|
804
1189
|
};
|
|
805
1190
|
|
|
@@ -807,15 +1192,29 @@ var Client = class extends import_client.Client {
|
|
|
807
1192
|
__reExport(index_exports, require("@stoatx/client"), module.exports);
|
|
808
1193
|
// Annotate the CommonJS export names for ESM import in node:
|
|
809
1194
|
0 && (module.exports = {
|
|
1195
|
+
Arg,
|
|
810
1196
|
Client,
|
|
1197
|
+
CommandGroup,
|
|
811
1198
|
CommandRegistry,
|
|
1199
|
+
CommandValidationError,
|
|
812
1200
|
DefaultCooldownManager,
|
|
1201
|
+
FetchFailedError,
|
|
813
1202
|
Guard,
|
|
1203
|
+
Injectable,
|
|
1204
|
+
InvalidMentionError,
|
|
1205
|
+
InvalidTypeError,
|
|
814
1206
|
METADATA_KEYS,
|
|
1207
|
+
MissingArgumentError,
|
|
1208
|
+
MissingOptionError,
|
|
1209
|
+
NoServerContextError,
|
|
815
1210
|
On,
|
|
816
1211
|
Once,
|
|
1212
|
+
Option,
|
|
1213
|
+
PARAM_TYPE_MAP,
|
|
817
1214
|
SimpleCommand,
|
|
818
1215
|
Stoat,
|
|
1216
|
+
StoatxError,
|
|
1217
|
+
SubCommand,
|
|
819
1218
|
buildSimpleCommandMetadata,
|
|
820
1219
|
getEventsMetadata,
|
|
821
1220
|
getGuards,
|