bakit 1.0.0-beta.1 → 1.0.0-beta.11

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.js CHANGED
@@ -1,3 +1,6 @@
1
+ // src/index.ts
2
+ import "reflect-metadata";
3
+
1
4
  // src/BakitClient.ts
2
5
  import {
3
6
  Client,
@@ -18,249 +21,236 @@ import { pathToFileURL } from "url";
18
21
  // src/command/CommandEntry.ts
19
22
  import { Collection } from "discord.js";
20
23
 
21
- // src/command/argument/Argument.ts
22
- var ArgumentType = /* @__PURE__ */ ((ArgumentType2) => {
23
- ArgumentType2["String"] = "string";
24
- ArgumentType2["Integer"] = "integer";
25
- ArgumentType2["Number"] = "number";
26
- ArgumentType2["User"] = "user";
27
- ArgumentType2["Member"] = "member";
28
- return ArgumentType2;
29
- })(ArgumentType || {});
30
-
31
- // src/command/argument/Arg.ts
32
- var ARGS_KEY = Symbol("args");
33
- var Arg = class _Arg {
34
- static cache = /* @__PURE__ */ new WeakMap();
35
- static string = _Arg.createArgument("string" /* String */);
36
- static integer = _Arg.createArgument("integer" /* Integer */);
37
- static number = _Arg.createArgument("number" /* Number */);
38
- static user = _Arg.createArgument("user" /* User */);
39
- static member = _Arg.createArgument("member" /* Member */);
40
- static getMethodArguments(method, init = false) {
41
- let args = this.cache.get(method) ?? Reflect.getMetadata(ARGS_KEY, method);
42
- if (!args) {
43
- args = [];
44
- if (init) {
45
- Reflect.defineMetadata(ARGS_KEY, args, method);
46
- this.cache.set(method, args);
47
- }
48
- }
49
- return init ? args : Object.freeze([...args]);
50
- }
51
- static format(arg) {
52
- const { name, required, tuple } = arg;
53
- const opening = required ? "<" : "[";
54
- const closing = required ? ">" : "]";
55
- const prefix = tuple ? "..." : "";
56
- return `${opening}${prefix}${name}: ${this.describeArgumentExpectation(arg)}${closing}`;
57
- }
58
- static describeArgumentExpectation(arg) {
59
- const parts = [arg.type];
60
- switch (arg.type) {
61
- case "string" /* String */: {
62
- if (arg.minLength && !arg.maxLength) {
63
- parts.push(`\u2265 ${String(arg.minLength)}`);
64
- }
65
- if (!arg.minLength && arg.maxLength) {
66
- parts.push(`\u2264 ${String(arg.maxLength)}`);
67
- }
68
- if (arg.minLength && arg.maxLength) {
69
- parts.push(`${String(arg.minLength)} - ${String(arg.maxLength)}`);
70
- }
71
- break;
72
- }
73
- case "number" /* Number */:
74
- case "integer" /* Integer */: {
75
- if (arg.minValue !== void 0 && arg.maxValue === void 0) {
76
- parts.push(`\u2265 ${String(arg.minValue)}`);
77
- }
78
- if (arg.minValue === void 0 && arg.maxValue !== void 0) {
79
- parts.push(`\u2264 ${String(arg.maxValue)}`);
80
- }
81
- if (arg.minValue !== void 0 && arg.maxValue !== void 0) {
82
- parts.push(`${String(arg.minValue)} - ${String(arg.maxValue)}`);
83
- }
84
- break;
85
- }
86
- case "user" /* User */:
87
- case "member" /* Member */: {
88
- break;
89
- }
90
- }
91
- return parts.join(", ");
92
- }
93
- static createArgument(type) {
94
- return function(options) {
95
- const objOptions = typeof options === "string" ? { name: options } : options;
96
- const fullOptions = { ...objOptions, type };
97
- if (!fullOptions.description) {
98
- fullOptions.description = fullOptions.name;
99
- }
100
- if (!("required" in fullOptions)) {
101
- fullOptions.required = true;
102
- }
103
- return function(target, key, _index) {
104
- const method = Object.getOwnPropertyDescriptor(target, key)?.value;
105
- if (!method) {
106
- throw new Error("No method found");
107
- }
108
- const args = _Arg.getMethodArguments(method, true);
109
- args.unshift(fullOptions);
110
- };
111
- };
112
- }
113
- };
114
-
115
- // src/command/CommandEntry.ts
116
- var HookExecutionState = /* @__PURE__ */ ((HookExecutionState2) => {
117
- HookExecutionState2["Main"] = "main";
118
- HookExecutionState2["Pre"] = "pre";
119
- HookExecutionState2["Post"] = "post";
120
- HookExecutionState2["Error"] = "error";
121
- return HookExecutionState2;
122
- })(HookExecutionState || {});
123
- var HOOKS_KEY = Symbol("hooks");
124
- var BaseCommandEntry = class _BaseCommandEntry {
125
- constructor(options) {
126
- this.options = options;
127
- }
128
- static cache = /* @__PURE__ */ new WeakMap();
129
- main = _BaseCommandEntry.createMainHookDecorator("main" /* Main */, this);
130
- pre = _BaseCommandEntry.createMainHookDecorator("pre" /* Pre */, this);
131
- post = _BaseCommandEntry.createMainHookDecorator("post" /* Post */, this);
132
- error = _BaseCommandEntry.createErrorHookDecorator("error" /* Error */, this);
133
- static getHooks(constructor, init = false) {
134
- let hooks = this.cache.get(constructor) ?? Reflect.getMetadata(HOOKS_KEY, constructor);
135
- if (!hooks) {
136
- hooks = [];
137
- if (init) {
138
- Reflect.defineMetadata(HOOKS_KEY, hooks, constructor);
139
- this.cache.set(constructor, hooks);
140
- }
141
- }
142
- return init ? hooks : Object.freeze([...hooks]);
143
- }
144
- static createMainHookDecorator(state, entry) {
24
+ // src/base/BaseEntry.ts
25
+ var BaseEntry = class {
26
+ target;
27
+ hooks = {
28
+ ["MAIN" /* Main */]: void 0,
29
+ ["ERROR" /* Error */]: void 0,
30
+ ["POST" /* Post */]: void 0,
31
+ ["PRE" /* Pre */]: void 0
32
+ };
33
+ main;
34
+ pre;
35
+ post;
36
+ error;
37
+ constructor() {
38
+ this.main = this.createMainHookDecorator("MAIN" /* Main */);
39
+ this.pre = this.createMainHookDecorator("PRE" /* Pre */);
40
+ this.post = this.createMainHookDecorator("POST" /* Post */);
41
+ this.error = this.createMainHookDecorator("ERROR" /* Error */);
42
+ }
43
+ setTarget(target) {
44
+ this.target = target;
45
+ }
46
+ createMainHookDecorator(state) {
145
47
  return (target, _key, descriptor) => {
146
- this.addHook(target, state, descriptor.value, entry);
48
+ this.addHook(state, target, descriptor);
147
49
  };
148
50
  }
149
- static createErrorHookDecorator(state, entry) {
51
+ createErrorHookDecorator(state) {
150
52
  return (target, _key, descriptor) => {
151
- this.addHook(target, state, descriptor.value, entry);
53
+ this.addHook(state, target, descriptor);
152
54
  };
153
55
  }
154
- static addHook(target, state, method, entry) {
155
- const { constructor } = target;
156
- const hooks = _BaseCommandEntry.getHooks(constructor, true);
56
+ addHook(state, target, descriptor) {
57
+ if (this.target && this.target !== target.constructor) {
58
+ throw new Error("Hook is used at wrong constructor.");
59
+ }
60
+ const { value: method } = descriptor;
157
61
  if (typeof method !== "function") {
158
- throw new Error("CommandEntry decorator must be used with a class method.");
159
- }
160
- if (hooks.some((hook) => hook.state === state && hook.entry === entry)) {
161
- throw new Error(`Hook "${state}" is already defined for entry "${entry.options.name}".`);
162
- }
163
- if ("parent" in entry) {
164
- const parentHook = hooks.find(
165
- (hook) => hook.entry === entry.parent && hook.state === "main" /* Main */
166
- );
167
- if (parentHook) {
168
- const parentArgs = Arg.getMethodArguments(parentHook.method);
169
- if (parentArgs.at(-1)?.tuple) {
170
- throw new Error(
171
- `Cannot add hook "${state}" to entry "${entry.options.name}" because its parent "${entry.parent.options.name}" has a tuple argument.`
172
- );
173
- }
174
- }
62
+ throw new Error("Invalid target method for hook.");
175
63
  }
176
- hooks.push({
64
+ const hook = {
177
65
  state,
178
- entry,
179
- method
180
- });
66
+ method,
67
+ entry: this
68
+ };
69
+ this.hooks[state] = hook;
70
+ }
71
+ };
72
+
73
+ // src/command/CommandEntry.ts
74
+ var BaseCommandEntry = class extends BaseEntry {
75
+ constructor(options) {
76
+ super();
77
+ this.options = options;
181
78
  }
182
79
  };
183
80
  var BaseCommandGroupEntry = class extends BaseCommandEntry {
184
81
  children = new Collection();
185
82
  subcommand(options) {
186
- if (typeof options === "string") {
187
- options = { name: options };
83
+ const fullOptions = typeof options === "string" ? { name: options, description: `${options} command` } : { description: `${options.name} command`, ...options };
84
+ if (this.children.has(fullOptions.name)) {
85
+ throw new Error(`Entry "${fullOptions.name}" is already existed.`);
188
86
  }
189
- if (!options.description) {
190
- options.description = options.name;
191
- }
192
- if (this.children.has(options.name)) {
193
- throw new Error(`Entry with name "${options.name}" already exists.`);
194
- }
195
- if (!(this instanceof CommandGroupEntry) && !(this instanceof RootCommandEntry)) {
196
- throw new Error(`Invalid parent "${this.constructor.name}"`);
87
+ const subcommand = new SubcommandEntry(fullOptions, this);
88
+ this.children.set(fullOptions.name, subcommand);
89
+ return subcommand;
90
+ }
91
+ };
92
+ var RootCommandEntry = class extends BaseCommandGroupEntry {
93
+ group(options) {
94
+ const fullOptions = typeof options === "string" ? { name: options, description: `${options} command` } : { description: `${options.name} command`, ...options };
95
+ if (this.children.has(fullOptions.name)) {
96
+ throw new Error(`Entry "${fullOptions.name}" is already existed.`);
197
97
  }
198
- const entry = new SubcommandEntry(options, this);
199
- this.children.set(entry.options.name, entry);
200
- return entry;
98
+ const group = new CommandGroupEntry(fullOptions, this);
99
+ this.children.set(fullOptions.name, group);
100
+ return group;
201
101
  }
202
102
  };
203
103
  var CommandGroupEntry = class extends BaseCommandGroupEntry {
204
104
  constructor(options, parent) {
205
105
  super(options);
206
- this.options = options;
207
106
  this.parent = parent;
208
107
  }
209
108
  };
210
- var RootCommandEntry = class extends BaseCommandGroupEntry {
211
- constructor(options) {
212
- super(options);
213
- this.options = options;
214
- }
215
- group(options) {
216
- if (typeof options === "string") {
217
- options = { name: options };
218
- }
219
- if (!options.description) {
220
- options.description = options.name;
221
- }
222
- if (this.children.has(options.name)) {
223
- throw new Error(`Entry with name "${options.name}" already exists.`);
224
- }
225
- const entry = new CommandGroupEntry(options, this);
226
- this.children.set(entry.options.name, entry);
227
- return entry;
228
- }
229
- };
230
109
  var SubcommandEntry = class extends BaseCommandEntry {
231
110
  constructor(options, parent) {
232
111
  super(options);
233
- this.options = options;
234
112
  this.parent = parent;
235
113
  }
236
114
  };
237
115
 
238
116
  // src/command/Command.ts
239
- var ROOT_KEY = Symbol("root");
240
- var Command = class {
241
- static create(options) {
242
- if (typeof options === "string") {
243
- options = { name: options };
244
- }
245
- if (!options.description) {
246
- options.description = options.name;
247
- }
248
- return new RootCommandEntry(options);
249
- }
250
- static use(command) {
117
+ var CommandAPI;
118
+ ((CommandAPI2) => {
119
+ const rootEntries = /* @__PURE__ */ new WeakMap();
120
+ function use(root) {
251
121
  return (target) => {
252
- Reflect.defineMetadata(ROOT_KEY, command, target);
122
+ root.setTarget(target);
123
+ rootEntries.set(target, root);
253
124
  };
254
125
  }
255
- static getRoot(constructor) {
256
- return Reflect.getMetadata(ROOT_KEY, constructor);
126
+ CommandAPI2.use = use;
127
+ function getRoot(constructor) {
128
+ return rootEntries.get(constructor);
129
+ }
130
+ CommandAPI2.getRoot = getRoot;
131
+ })(CommandAPI || (CommandAPI = {}));
132
+ function CommandFactory(options) {
133
+ if (typeof options === "string") {
134
+ options = { name: options };
257
135
  }
136
+ if (!options.description) {
137
+ options.description = options.name;
138
+ }
139
+ return new RootCommandEntry(options);
140
+ }
141
+ var Command = Object.assign(CommandFactory, CommandAPI);
142
+
143
+ // src/command/argument/Argument.ts
144
+ var ArgumentType = /* @__PURE__ */ ((ArgumentType2) => {
145
+ ArgumentType2["String"] = "string";
146
+ ArgumentType2["Integer"] = "integer";
147
+ ArgumentType2["Number"] = "number";
148
+ ArgumentType2["User"] = "user";
149
+ ArgumentType2["Member"] = "member";
150
+ return ArgumentType2;
151
+ })(ArgumentType || {});
152
+
153
+ // src/command/argument/Arg.ts
154
+ var ARGS_KEY = Symbol("args");
155
+ var cache = /* @__PURE__ */ new WeakMap();
156
+ function getMethodArguments(method, init = false) {
157
+ let args = cache.get(method) ?? Reflect.getMetadata(ARGS_KEY, method);
158
+ if (!args) {
159
+ args = [];
160
+ if (init) {
161
+ Reflect.defineMetadata(ARGS_KEY, args, method);
162
+ cache.set(method, args);
163
+ }
164
+ }
165
+ return init ? args : Object.freeze([...args]);
166
+ }
167
+ function createArgument(type) {
168
+ return function(options) {
169
+ const objOptions = typeof options === "string" ? { name: options } : options;
170
+ const fullOptions = { ...objOptions, type };
171
+ if (!fullOptions.description) {
172
+ fullOptions.description = fullOptions.name;
173
+ }
174
+ if (!("required" in fullOptions)) {
175
+ fullOptions.required = true;
176
+ }
177
+ return function(target, key, _index) {
178
+ const method = Object.getOwnPropertyDescriptor(target, key)?.value;
179
+ if (!method) {
180
+ throw new Error("No method found");
181
+ }
182
+ const args = getMethodArguments(method, true);
183
+ args.unshift(fullOptions);
184
+ };
185
+ };
186
+ }
187
+ var string = createArgument("string" /* String */);
188
+ var integer = createArgument("integer" /* Integer */);
189
+ var number = createArgument("number" /* Number */);
190
+ var user = createArgument("user" /* User */);
191
+ var member = createArgument("member" /* Member */);
192
+ function describeArgumentExpectation(arg) {
193
+ const parts = [arg.type];
194
+ switch (arg.type) {
195
+ case "string" /* String */: {
196
+ if (arg.minLength && !arg.maxLength) {
197
+ parts.push(`\u2265 ${String(arg.minLength)}`);
198
+ }
199
+ if (!arg.minLength && arg.maxLength) {
200
+ parts.push(`\u2264 ${String(arg.maxLength)}`);
201
+ }
202
+ if (arg.minLength && arg.maxLength) {
203
+ parts.push(`${String(arg.minLength)} - ${String(arg.maxLength)}`);
204
+ }
205
+ break;
206
+ }
207
+ case "number" /* Number */:
208
+ case "integer" /* Integer */: {
209
+ if (arg.minValue !== void 0 && arg.maxValue === void 0) {
210
+ parts.push(`\u2265 ${String(arg.minValue)}`);
211
+ }
212
+ if (arg.minValue === void 0 && arg.maxValue !== void 0) {
213
+ parts.push(`\u2264 ${String(arg.maxValue)}`);
214
+ }
215
+ if (arg.minValue !== void 0 && arg.maxValue !== void 0) {
216
+ parts.push(`${String(arg.minValue)} - ${String(arg.maxValue)}`);
217
+ }
218
+ break;
219
+ }
220
+ case "user" /* User */:
221
+ case "member" /* Member */: {
222
+ break;
223
+ }
224
+ }
225
+ return parts.join(", ");
226
+ }
227
+ function format(arg) {
228
+ const { name, required, tuple } = arg;
229
+ const opening = required ? "<" : "[";
230
+ const closing = required ? ">" : "]";
231
+ const prefix = tuple ? "..." : "";
232
+ return `${opening}${prefix}${name}: ${describeArgumentExpectation(arg)}${closing}`;
233
+ }
234
+ var Arg = {
235
+ getMethodArguments,
236
+ createArgument,
237
+ describeArgumentExpectation,
238
+ format,
239
+ string,
240
+ number,
241
+ integer,
242
+ user,
243
+ member
258
244
  };
259
245
 
260
246
  // src/command/CommandRegistry.ts
261
247
  var CommandRegistry = class _CommandRegistry {
262
248
  static constructors = new Collection2();
263
249
  static instances = new Collection2();
250
+ /**
251
+ * Add a command to the registry.
252
+ * @param constructor The command class you want to add.
253
+ */
264
254
  static add(constructor) {
265
255
  const root = Command.getRoot(constructor);
266
256
  if (!root) {
@@ -270,20 +260,13 @@ var CommandRegistry = class _CommandRegistry {
270
260
  this.constructors.set(options.name, constructor);
271
261
  this.instances.set(options.name, new constructor());
272
262
  }
273
- static buildSlashCommand(constructor) {
274
- const builder = new SlashCommandBuilder();
275
- const root = Command.getRoot(constructor);
276
- if (!root) {
277
- throw new Error(`No root found for "${constructor.name}"`);
278
- }
279
- const { options } = root;
280
- builder.setName(options.name);
281
- builder.setDescription(options.description || "");
282
- builder.setNSFW(Boolean(options.nsfw));
283
- this.buildSlashCommandOptions(builder, constructor);
284
- return builder.toJSON();
285
- }
286
- static async loadDirectory(pattern, parallel = true) {
263
+ /**
264
+ * Load and add all commands which matched provided glob pattern to the registry.
265
+ * @param pattern glob pattern to load.
266
+ * @param parallel load all matched results in parallel, enabled by default.
267
+ * @returns All loaded command constructors.
268
+ */
269
+ static async load(pattern, parallel = true) {
287
270
  const files = await glob(pattern);
288
271
  const loaders = files.map(async (file) => {
289
272
  const fileURL = pathToFileURL(file).toString();
@@ -300,72 +283,102 @@ var CommandRegistry = class _CommandRegistry {
300
283
  }
301
284
  return result;
302
285
  }
303
- static buildSlashCommandOptions(builder, constructor) {
286
+ /**
287
+ * Build a command into application command data.
288
+ * @param constructor The command class you want to build.
289
+ * @returns a REST JSON version of the application command data.
290
+ */
291
+ static buildSlashCommand(constructor) {
304
292
  const root = Command.getRoot(constructor);
305
293
  if (!root) {
306
294
  throw new Error(`No root found for "${constructor.name}"`);
307
295
  }
308
- const hooks = new Collection2(
309
- BaseCommandEntry.getHooks(constructor).filter((hook) => hook.state === "main" /* Main */).map((hook) => [hook.entry, hook])
310
- );
311
- const rootHook = hooks.get(root);
312
- if (!root.children.size && hooks.size) {
313
- if (!rootHook) {
314
- return;
315
- }
316
- for (const arg of Arg.getMethodArguments(rootHook.method)) {
317
- this.buildSlashCommandOption(builder, arg);
318
- }
319
- return;
296
+ const { options } = root;
297
+ const builder = new SlashCommandBuilder().setName(options.name).setDescription(options.description).setNSFW(Boolean(options.nsfw));
298
+ const args = this.getMainHookArguments(root);
299
+ if (root.children.size) {
300
+ this.buildSlashCommandSubcommands(builder, root, args);
301
+ } else {
302
+ this.buildSlashCommandOptions(builder, args);
320
303
  }
321
- for (const child of root.children.values()) {
322
- const hook = hooks.get(child);
323
- const inheritedArgs = [];
324
- if (rootHook) {
325
- inheritedArgs.push(...Arg.getMethodArguments(rootHook.method));
326
- }
327
- if (child instanceof SubcommandEntry) {
328
- this.buildSubcommand(builder, child, hook, inheritedArgs);
329
- } else if (child instanceof CommandGroupEntry) {
330
- if (hook) {
331
- inheritedArgs.push(...Arg.getMethodArguments(hook.method));
332
- }
304
+ return builder.toJSON();
305
+ }
306
+ static getMainHookArguments(entry) {
307
+ const { hooks } = entry;
308
+ const mainHook = hooks["MAIN" /* Main */];
309
+ return mainHook ? Arg.getMethodArguments(mainHook.method) : [];
310
+ }
311
+ static buildSlashCommandSubcommands(parent, entry, inheritedArgs) {
312
+ const { children } = entry;
313
+ for (const child of children.values()) {
314
+ if (child instanceof CommandGroupEntry && parent instanceof SlashCommandBuilder) {
333
315
  const { options } = child;
334
316
  const group = new SlashCommandSubcommandGroupBuilder().setName(options.name).setDescription(options.description);
335
- for (const subChild of child.children.values()) {
336
- this.buildSubcommand(group, subChild, hooks.get(subChild), inheritedArgs);
337
- }
338
- builder.addSubcommandGroup(group);
317
+ this.buildSlashCommandSubcommands(group, child, [
318
+ ...inheritedArgs,
319
+ ...this.getMainHookArguments(child)
320
+ ]);
321
+ parent.addSubcommandGroup(group);
322
+ } else if (child instanceof SubcommandEntry) {
323
+ const { options } = child;
324
+ const subcommand = new SlashCommandSubcommandBuilder().setName(options.name).setDescription(options.description);
325
+ this.buildSlashCommandOptions(subcommand, [
326
+ ...inheritedArgs,
327
+ ...this.getMainHookArguments(child)
328
+ ]);
329
+ parent.addSubcommand(subcommand);
339
330
  }
340
331
  }
341
332
  }
342
- static buildSubcommand(parent, entry, hook, inheritedArgs) {
343
- const { options } = entry;
344
- const subcommand = new SlashCommandSubcommandBuilder().setName(options.name).setDescription(options.description);
345
- const args = [...inheritedArgs];
346
- if (hook) {
347
- args.push(...Arg.getMethodArguments(hook.method));
333
+ static buildSlashCommandOptions(builder, args) {
334
+ const argGroup = Object.groupBy(args, ({ required }) => required ? "required" : "optional");
335
+ const orderedArgs = [...argGroup.required || [], ...argGroup.optional || []];
336
+ for (const arg of orderedArgs) {
337
+ this.attachSlashCommandOption(builder, arg);
348
338
  }
349
- for (const arg of args) {
350
- this.buildSlashCommandOption(subcommand, arg);
351
- }
352
- parent.addSubcommand(subcommand);
353
339
  }
354
- static buildSlashCommandOption(builder, arg) {
340
+ static attachSlashCommandOption(builder, arg) {
355
341
  const setupOption = (option) => {
356
342
  return option.setName(arg.name).setDescription(arg.description || arg.name).setRequired(Boolean(arg.required));
357
343
  };
358
344
  switch (arg.type) {
359
345
  case "string" /* String */: {
360
- builder.addStringOption((option) => setupOption(option));
346
+ builder.addStringOption((data) => {
347
+ const option = setupOption(data);
348
+ if (arg.maxLength) {
349
+ option.setMaxLength(arg.maxLength);
350
+ }
351
+ if (arg.minLength) {
352
+ option.setMinLength(arg.minLength);
353
+ }
354
+ return option;
355
+ });
361
356
  break;
362
357
  }
363
358
  case "integer" /* Integer */: {
364
- builder.addIntegerOption((option) => setupOption(option));
359
+ builder.addIntegerOption((data) => {
360
+ const option = setupOption(data);
361
+ if (arg.maxValue) {
362
+ option.setMaxValue(arg.maxValue);
363
+ }
364
+ if (arg.minValue) {
365
+ option.setMinValue(arg.minValue);
366
+ }
367
+ return option;
368
+ });
365
369
  break;
366
370
  }
367
371
  case "number" /* Number */: {
368
- builder.addNumberOption((option) => setupOption(option));
372
+ builder.addNumberOption((data) => {
373
+ const option = setupOption(data);
374
+ if (arg.maxValue) {
375
+ option.setMaxValue(arg.maxValue);
376
+ }
377
+ if (arg.minValue) {
378
+ option.setMinValue(arg.minValue);
379
+ }
380
+ return option;
381
+ });
369
382
  break;
370
383
  }
371
384
  case "user" /* User */:
@@ -377,9 +390,6 @@ var CommandRegistry = class _CommandRegistry {
377
390
  }
378
391
  };
379
392
 
380
- // src/command/index.ts
381
- import "reflect-metadata";
382
-
383
393
  // src/command/Context.ts
384
394
  import {
385
395
  ChatInputCommandInteraction,
@@ -699,11 +709,11 @@ var ArgumentResolver = class _ArgumentResolver {
699
709
  if (!userId) {
700
710
  return null;
701
711
  }
702
- const user = await this.client.users.fetch(userId).catch(() => null);
703
- if (!user) {
712
+ const user2 = await this.client.users.fetch(userId).catch(() => null);
713
+ if (!user2) {
704
714
  return null;
705
715
  }
706
- return user;
716
+ return user2;
707
717
  }
708
718
  matchIntegerValue(arg, value) {
709
719
  const intVal = parseInt(value, 10);
@@ -801,6 +811,174 @@ var StateBox = class _StateBox {
801
811
  }
802
812
  };
803
813
 
814
+ // src/listener/ListenerEntry.ts
815
+ var ListenerEntry = class extends BaseEntry {
816
+ constructor(options) {
817
+ super();
818
+ this.options = options;
819
+ }
820
+ };
821
+
822
+ // src/listener/Listener.ts
823
+ var ListenerAPI;
824
+ ((ListenerAPI2) => {
825
+ ListenerAPI2.ENTRY_KEY = Symbol("entry");
826
+ function use(entry) {
827
+ return (target) => {
828
+ Reflect.defineMetadata(ListenerAPI2.ENTRY_KEY, entry, target);
829
+ };
830
+ }
831
+ ListenerAPI2.use = use;
832
+ function getEntry(target) {
833
+ return Reflect.getMetadata(ListenerAPI2.ENTRY_KEY, target);
834
+ }
835
+ ListenerAPI2.getEntry = getEntry;
836
+ })(ListenerAPI || (ListenerAPI = {}));
837
+ function ListenerFactory(options) {
838
+ const fullOptions = typeof options !== "object" ? { name: options, once: false } : { once: false, ...options };
839
+ return new ListenerEntry(fullOptions);
840
+ }
841
+ var Listener = Object.assign(ListenerFactory, ListenerAPI);
842
+
843
+ // src/listener/ListenerRegistry.ts
844
+ import glob2 from "tiny-glob";
845
+ import { pathToFileURL as pathToFileURL2 } from "url";
846
+ var ListenerRegistry = class {
847
+ static client;
848
+ static constructors = /* @__PURE__ */ new Set();
849
+ static instances = /* @__PURE__ */ new WeakMap();
850
+ static executors = /* @__PURE__ */ new WeakMap();
851
+ /**
852
+ * Add and register a listener to the registry.
853
+ * If `options.emitter` is not provided, the registry will use the base `client` by default.
854
+ * @param constructor The listener class you want to add.
855
+ */
856
+ static add(constructor) {
857
+ const entry = Listener.getEntry(constructor);
858
+ if (!entry) {
859
+ throw new Error(`No entry found for "${constructor.name}"`);
860
+ }
861
+ const { options } = entry;
862
+ if (!options.emitter) {
863
+ if (!this.client) {
864
+ throw new Error("Client is not ready.");
865
+ }
866
+ options.emitter = this.client;
867
+ }
868
+ const instance = new constructor();
869
+ const executor = this.createExecutor(constructor, instance);
870
+ this.constructors.add(constructor);
871
+ this.instances.set(constructor, instance);
872
+ this.executors.set(instance, executor);
873
+ options.emitter[options.once ? "once" : "on"](options.name, (...args) => {
874
+ void executor(...args);
875
+ });
876
+ }
877
+ /**
878
+ * Remove and unregister a listener from the registry.
879
+ * @param constructor The listener class you want to remove.
880
+ * @returns `boolean`, returns `true` if the listener is removed successfully.
881
+ */
882
+ static remove(constructor) {
883
+ const entry = Listener.getEntry(constructor);
884
+ if (!entry) {
885
+ return false;
886
+ }
887
+ this.constructors.delete(constructor);
888
+ const instance = this.instances.get(constructor);
889
+ if (!instance) {
890
+ return false;
891
+ }
892
+ this.instances.delete(constructor);
893
+ const executor = this.executors.get(instance);
894
+ if (!executor) {
895
+ return false;
896
+ }
897
+ const { name, emitter } = entry.options;
898
+ emitter?.removeListener(name, executor);
899
+ this.executors.delete(instance);
900
+ return true;
901
+ }
902
+ /**
903
+ * Remove and unregister all listeners from the registry.
904
+ * @returns Amount of removed listeners.
905
+ */
906
+ static removeAll() {
907
+ let removedAmount = 0;
908
+ for (const constructor of this.constructors) {
909
+ if (this.remove(constructor)) {
910
+ removedAmount++;
911
+ }
912
+ }
913
+ return removedAmount;
914
+ }
915
+ /**
916
+ * Set base client for the registry to fallback as default emitter. This should be used only by BakitClient and stay untouched.
917
+ * @param newClient base client to set for the registry.
918
+ */
919
+ static setClient(newClient) {
920
+ this.client = newClient;
921
+ }
922
+ static createExecutor(constructor, instance) {
923
+ const entry = Listener.getEntry(constructor);
924
+ if (!entry) {
925
+ throw new Error("Missing listener entry");
926
+ }
927
+ const { hooks } = entry;
928
+ return async function(...args) {
929
+ const mainHook = hooks["MAIN" /* Main */];
930
+ const preHook = hooks["PRE" /* Pre */];
931
+ const postHook = hooks["POST" /* Post */];
932
+ const errorHook = hooks["ERROR" /* Error */];
933
+ if (!mainHook) {
934
+ return;
935
+ }
936
+ try {
937
+ if (preHook) {
938
+ await preHook.method.call(instance, ...args);
939
+ }
940
+ await mainHook.method.call(instance, ...args);
941
+ if (postHook) {
942
+ await postHook.method.call(instance, ...args);
943
+ }
944
+ } catch (error) {
945
+ if (errorHook) {
946
+ await errorHook.method.call(
947
+ instance,
948
+ error,
949
+ ...args
950
+ );
951
+ } else {
952
+ throw error;
953
+ }
954
+ }
955
+ };
956
+ }
957
+ /**
958
+ * Load and add all listeners which matched provided glob pattern to the registry.
959
+ * @param pattern glob pattern to load.
960
+ * @param parallel load all matched results in parallel, enabled by default.
961
+ * @returns All loaded listener constructors.
962
+ */
963
+ static async load(pattern, parallel = true) {
964
+ const files = await glob2(pattern);
965
+ const loaders = files.map(async (file) => {
966
+ const fileURL = pathToFileURL2(file).toString();
967
+ const { default: constructor } = await import(fileURL);
968
+ this.add(constructor);
969
+ return constructor;
970
+ });
971
+ if (parallel) {
972
+ return Promise.all(loaders);
973
+ }
974
+ const result = [];
975
+ for (const loader of loaders) {
976
+ result.push(await loader);
977
+ }
978
+ return result;
979
+ }
980
+ };
981
+
804
982
  // src/BakitClient.ts
805
983
  var BakitClient = class _BakitClient extends Client {
806
984
  constructor(options) {
@@ -808,6 +986,7 @@ var BakitClient = class _BakitClient extends Client {
808
986
  options.getSyntaxErrorMessage = _BakitClient.getSyntaxErrorMessage;
809
987
  }
810
988
  super(options);
989
+ ListenerRegistry["setClient"](this);
811
990
  this.once(
812
991
  Events.ClientReady,
813
992
  (client) => void this.registerApplicationCommands(client)
@@ -838,6 +1017,7 @@ var BakitClient = class _BakitClient extends Client {
838
1017
  if (message.author.bot) {
839
1018
  return;
840
1019
  }
1020
+ const context = new MessageContext(message);
841
1021
  const resolver = ArgumentResolver.create(message);
842
1022
  if (!resolver) {
843
1023
  return;
@@ -847,9 +1027,7 @@ var BakitClient = class _BakitClient extends Client {
847
1027
  if (!command) {
848
1028
  return;
849
1029
  }
850
- const hooks = BaseCommandEntry.getHooks(command.constructor);
851
- const context = new MessageContext(message);
852
- await StateBox.run(() => this.handleMessageHooks(context, hooks, command, resolver));
1030
+ await StateBox.run(() => this.handleMessageHooks(context, command, resolver));
853
1031
  }
854
1032
  async handleInteraction(interaction) {
855
1033
  if (!interaction.isChatInputCommand()) {
@@ -860,54 +1038,57 @@ var BakitClient = class _BakitClient extends Client {
860
1038
  if (!command) {
861
1039
  return;
862
1040
  }
863
- const hooks = BaseCommandEntry.getHooks(command.constructor);
864
1041
  const context = new ChatInputContext(interaction);
865
- await StateBox.run(() => this.handleChatInputHooks(context, hooks, command));
1042
+ await StateBox.run(() => this.handleChatInputHooks(context, command));
866
1043
  }
867
- async handleChatInputHooks(context, hooks, instance) {
868
- const targetHooks = this.getChatInputTargetHooks(context.source, hooks);
1044
+ async handleChatInputHooks(context, instance) {
1045
+ const targetHooks = this.getChatInputTargetHooks(context.source, instance);
869
1046
  let inheritedArgs = [];
870
- for (const record of [targetHooks.root, targetHooks.group, targetHooks.subcommand]) {
871
- const newArgs = await this.runChatInputHooks(context, instance, record, inheritedArgs);
1047
+ for (const hooks of [targetHooks.root, targetHooks.group, targetHooks.subcommand]) {
1048
+ if (!hooks) {
1049
+ continue;
1050
+ }
1051
+ const newArgs = await this.runChatInputHooks(context, instance, hooks, inheritedArgs);
872
1052
  if (newArgs) {
873
1053
  inheritedArgs = newArgs;
874
1054
  }
875
1055
  }
876
1056
  }
877
- async handleMessageHooks(context, hooks, instance, resolver) {
1057
+ async handleMessageHooks(context, instance, resolver) {
878
1058
  if (!resolver) {
879
1059
  return;
880
1060
  }
881
- const rootEntry = Command.getRoot(instance.constructor);
882
- if (!rootEntry) return;
883
- const rootRecord = this.createHookRecord(hooks.filter((hook) => hook.entry === rootEntry));
884
- resolver = await this.runMessageHooks(context, instance, rootRecord, resolver);
1061
+ const root = Command.getRoot(instance.constructor);
1062
+ if (!root) {
1063
+ return;
1064
+ }
1065
+ resolver = await this.runMessageHooks(context, instance, root.hooks, resolver);
885
1066
  if (!resolver) {
886
1067
  return;
887
1068
  }
888
- const usedValues = resolver.parsedValues.length;
889
- const nextTrigger = resolver.values[usedValues + 1];
890
- const nextHook = hooks.find((hook) => hook.entry.options.name === nextTrigger);
891
- const nextRecord = this.createHookRecord(nextHook ? [nextHook] : []);
892
- resolver = await this.runMessageHooks(context, instance, nextRecord, resolver);
1069
+ await this.handleChildMessageHooks(context, root, instance, resolver);
1070
+ }
1071
+ async handleChildMessageHooks(context, parent, instance, resolver, skip = 1) {
893
1072
  if (!resolver) {
894
1073
  return;
895
1074
  }
896
- if (nextRecord.main?.entry instanceof CommandGroupEntry) {
897
- const groupEntry = nextRecord.main.entry;
898
- const subTrigger = resolver.values[usedValues + 2];
899
- const subHook = hooks.find(
900
- (h) => h.entry instanceof SubcommandEntry && h.entry.options.name === subTrigger && h.entry.parent === groupEntry
901
- );
902
- const subRecord = this.createHookRecord(subHook ? [subHook] : []);
903
- resolver = await this.runMessageHooks(context, instance, subRecord, resolver);
1075
+ const usedValues = resolver.parsedValues.length;
1076
+ const nextTrigger = resolver.values[usedValues + skip];
1077
+ const child = parent.children.get(nextTrigger);
1078
+ if (!child) {
1079
+ return;
1080
+ }
1081
+ resolver = await this.runMessageHooks(context, instance, child.hooks, resolver);
1082
+ if (child instanceof CommandGroupEntry) {
1083
+ await this.handleChildMessageHooks(context, child, instance, resolver, skip + 1);
904
1084
  }
905
1085
  }
906
- async runMessageHooks(context, instance, record, resolver) {
907
- if (!record.main) {
1086
+ async runMessageHooks(context, instance, hooks, resolver) {
1087
+ const mainHook = hooks["MAIN" /* Main */];
1088
+ if (!mainHook) {
908
1089
  return resolver;
909
1090
  }
910
- const args = Arg.getMethodArguments(record.main.method);
1091
+ const args = Arg.getMethodArguments(mainHook.method);
911
1092
  try {
912
1093
  resolver = await resolver.resolve(args);
913
1094
  } catch (error) {
@@ -926,75 +1107,78 @@ var BakitClient = class _BakitClient extends Client {
926
1107
  }
927
1108
  throw error;
928
1109
  }
929
- try {
930
- await record.pre?.method.call(instance, context, ...resolver.parsedValues);
931
- await record.main.method.call(instance, context, ...resolver.parsedValues);
932
- await record.post?.method.call(instance, context, ...resolver.parsedValues);
933
- } catch (error) {
934
- if (record.error) {
935
- await record.error.method.call(instance, context, error, ...resolver.parsedValues);
936
- } else {
937
- throw error;
938
- }
939
- }
1110
+ await this.runHooks(context, instance, hooks, resolver.parsedValues);
940
1111
  return resolver;
941
1112
  }
942
- async runChatInputHooks(context, instance, record, inheritedArgs) {
943
- if (!record.main) {
1113
+ async runChatInputHooks(context, instance, hooks, inheritedArgs) {
1114
+ const mainHook = hooks["MAIN" /* Main */];
1115
+ if (!mainHook) {
944
1116
  return;
945
1117
  }
946
- const newArgs = Arg.getMethodArguments(record.main.method).map(
1118
+ const newArgs = Arg.getMethodArguments(mainHook.method).map(
947
1119
  (arg) => ArgumentResolver.resolveChatInput(context.source, arg)
948
1120
  );
949
1121
  const argValues = [...inheritedArgs, ...newArgs];
1122
+ await this.runHooks(context, instance, hooks, argValues);
1123
+ return argValues;
1124
+ }
1125
+ async runHooks(context, instance, hooks, args) {
1126
+ const mainHook = hooks["MAIN" /* Main */];
1127
+ const preHook = hooks["PRE" /* Pre */];
1128
+ const postHook = hooks["POST" /* Post */];
1129
+ const errorHook = hooks["ERROR" /* Error */];
1130
+ if (!mainHook) {
1131
+ return;
1132
+ }
1133
+ const execute = async (hook, error) => {
1134
+ if (!hook) {
1135
+ return;
1136
+ }
1137
+ if (hook.state === "ERROR" /* Error */) {
1138
+ await hook.method.call(instance, error, context, ...args);
1139
+ } else {
1140
+ await hook.method.call(instance, context, ...args);
1141
+ }
1142
+ };
950
1143
  try {
951
- await record.pre?.method.call(instance, context, ...argValues);
952
- await record.main.method.call(instance, context, ...argValues);
953
- await record.post?.method.call(instance, context, ...argValues);
1144
+ await execute(preHook);
1145
+ await execute(mainHook);
1146
+ await execute(postHook);
954
1147
  } catch (error) {
955
- if (record.error) {
956
- await record.error.method.call(instance, context, error, ...argValues);
1148
+ if (errorHook) {
1149
+ await execute(errorHook, error);
957
1150
  } else {
958
1151
  throw error;
959
1152
  }
960
1153
  }
961
- return argValues;
962
1154
  }
963
- getChatInputTargetHooks(interaction, hooks) {
964
- const subcommand = interaction.options.getSubcommand(false);
965
- const subcommandGroup = interaction.options.getSubcommandGroup(false);
966
- const root = this.createHookRecord(
967
- hooks.filter((hook) => hook.entry instanceof RootCommandEntry)
968
- );
969
- const group = this.createHookRecord(
970
- hooks.filter(({ entry }) => {
971
- return entry instanceof CommandGroupEntry && entry.options.name === subcommandGroup;
972
- })
973
- );
974
- const sub = this.createHookRecord(
975
- hooks.filter(({ entry }) => {
976
- if (!(entry instanceof SubcommandEntry) || !(entry.options.name === subcommand)) {
977
- return false;
978
- }
979
- if (subcommandGroup) {
980
- const { parent } = entry;
981
- if (!(parent instanceof CommandGroupEntry) || parent.options.name !== subcommandGroup) {
982
- return false;
983
- }
984
- }
985
- return true;
986
- })
987
- );
988
- return { root, group, subcommand: sub };
989
- }
990
- createHookRecord(hooks) {
991
- return Object.values(HookExecutionState).reduce(
992
- (acc, state) => {
993
- acc[state] = hooks.find((h) => h.state === state);
994
- return acc;
995
- },
996
- {}
997
- );
1155
+ getChatInputTargetHooks(interaction, instance) {
1156
+ const subcommandName = interaction.options.getSubcommand(false);
1157
+ const groupName = interaction.options.getSubcommandGroup(false);
1158
+ const root = Command.getRoot(instance.constructor);
1159
+ if (!root) {
1160
+ throw new Error("No root found.");
1161
+ }
1162
+ let group;
1163
+ if (groupName) {
1164
+ const child = root.children.get(groupName);
1165
+ if (child instanceof CommandGroupEntry) {
1166
+ group = child;
1167
+ }
1168
+ }
1169
+ let subcommand;
1170
+ if (subcommandName) {
1171
+ const parent = group || root;
1172
+ const child = parent.children.get(subcommandName);
1173
+ if (child instanceof SubcommandEntry) {
1174
+ subcommand = child;
1175
+ }
1176
+ }
1177
+ return {
1178
+ root: root.hooks,
1179
+ group: group?.hooks,
1180
+ subcommand: subcommand?.hooks
1181
+ };
998
1182
  }
999
1183
  };
1000
1184
  export {
@@ -1006,12 +1190,17 @@ export {
1006
1190
  BaseContext,
1007
1191
  ChatInputContext,
1008
1192
  Command,
1193
+ CommandAPI,
1194
+ CommandFactory,
1009
1195
  CommandGroupEntry,
1010
1196
  CommandRegistry,
1011
1197
  CommandSyntaxError,
1012
1198
  CommandSyntaxErrorType,
1013
- HOOKS_KEY,
1014
- HookExecutionState,
1199
+ Listener,
1200
+ ListenerAPI,
1201
+ ListenerEntry,
1202
+ ListenerFactory,
1203
+ ListenerRegistry,
1015
1204
  MessageContext,
1016
1205
  RootCommandEntry,
1017
1206
  StateBox,