bakit 1.0.0-beta.13 → 1.0.0-beta.15

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.
Files changed (3) hide show
  1. package/dist/index.d.ts +69 -32
  2. package/dist/index.js +595 -976
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,41 +1,25 @@
1
- // src/BakitClient.ts
2
- import {
3
- Client,
4
- codeBlock,
5
- Events
6
- } from "discord.js";
7
-
8
- // src/command/CommandRegistry.ts
9
- import {
10
- Collection as Collection2,
11
- SlashCommandBuilder,
12
- SlashCommandSubcommandBuilder,
13
- SlashCommandSubcommandGroupBuilder
14
- } from "discord.js";
15
- import glob from "tiny-glob";
16
- import { pathToFileURL } from "url";
1
+ import { Collection, SlashCommandBuilder, SlashCommandSubcommandGroupBuilder, SlashCommandSubcommandBuilder, ChatInputCommandInteraction, Message, codeBlock, Client, Events } from 'discord.js';
2
+ import { AsyncLocalStorage } from 'async_hooks';
3
+ import glob from 'tiny-glob';
4
+ import { pathToFileURL } from 'url';
17
5
 
18
- // src/command/CommandEntry.ts
19
- import { Collection } from "discord.js";
6
+ // src/BakitClient.ts
20
7
 
21
8
  // src/base/BaseEntry.ts
22
9
  var BaseEntry = class {
23
10
  target;
24
11
  hooks = {
25
- ["MAIN" /* Main */]: void 0,
26
- ["ERROR" /* Error */]: void 0,
27
- ["POST" /* Post */]: void 0,
28
- ["PRE" /* Pre */]: void 0
12
+ MAIN: void 0,
13
+ ERROR: void 0,
14
+ POST: void 0,
15
+ PRE: void 0
29
16
  };
30
17
  main;
31
18
  pre;
32
19
  post;
33
20
  error;
34
21
  constructor() {
35
- this.main = this.createMainHookDecorator("MAIN" /* Main */);
36
- this.pre = this.createMainHookDecorator("PRE" /* Pre */);
37
- this.post = this.createMainHookDecorator("POST" /* Post */);
38
- this.error = this.createMainHookDecorator("ERROR" /* Error */);
22
+ this.main = this.createMainHookDecorator("MAIN" /* Main */), this.pre = this.createMainHookDecorator("PRE" /* Pre */), this.post = this.createMainHookDecorator("POST" /* Post */), this.error = this.createMainHookDecorator("ERROR" /* Error */);
39
23
  }
40
24
  setTarget(target) {
41
25
  this.target = target;
@@ -51,14 +35,12 @@ var BaseEntry = class {
51
35
  };
52
36
  }
53
37
  addHook(state, target, descriptor) {
54
- if (this.target && this.target !== target.constructor) {
38
+ if (this.target && this.target !== target.constructor)
55
39
  throw new Error("Hook is used at wrong constructor.");
56
- }
57
- const { value: method } = descriptor;
58
- if (typeof method !== "function") {
40
+ let { value: method } = descriptor;
41
+ if (typeof method != "function")
59
42
  throw new Error("Invalid target method for hook.");
60
- }
61
- const hook = {
43
+ let hook = {
62
44
  state,
63
45
  method,
64
46
  entry: this
@@ -67,160 +49,216 @@ var BaseEntry = class {
67
49
  }
68
50
  };
69
51
 
70
- // src/command/CommandEntry.ts
71
- var BaseCommandEntry = class extends BaseEntry {
52
+ // src/listener/ListenerEntry.ts
53
+ var ListenerEntry = class extends BaseEntry {
72
54
  constructor(options) {
73
55
  super();
74
56
  this.options = options;
75
57
  }
76
58
  };
77
- var BaseCommandGroupEntry = class extends BaseCommandEntry {
78
- children = new Collection();
79
- subcommand(options) {
80
- const fullOptions = typeof options === "string" ? { name: options, description: `${options} command` } : { description: `${options.name} command`, ...options };
81
- if (this.children.has(fullOptions.name)) {
82
- throw new Error(`Entry "${fullOptions.name}" is already existed.`);
83
- }
84
- const subcommand = new SubcommandEntry(fullOptions, this);
85
- this.children.set(fullOptions.name, subcommand);
86
- return subcommand;
59
+
60
+ // src/listener/Listener.ts
61
+ var ListenerAPI;
62
+ ((ListenerAPI2) => {
63
+ let entries = /* @__PURE__ */ new WeakMap();
64
+ function use(entry) {
65
+ return (target) => {
66
+ entries.set(target, entry);
67
+ };
87
68
  }
88
- };
89
- var RootCommandEntry = class extends BaseCommandGroupEntry {
90
- group(options) {
91
- const fullOptions = typeof options === "string" ? { name: options, description: `${options} command` } : { description: `${options.name} command`, ...options };
92
- if (this.children.has(fullOptions.name)) {
93
- throw new Error(`Entry "${fullOptions.name}" is already existed.`);
94
- }
95
- const group = new CommandGroupEntry(fullOptions, this);
96
- this.children.set(fullOptions.name, group);
97
- return group;
69
+ ListenerAPI2.use = use;
70
+ function getEntry(target) {
71
+ return entries.get(target);
98
72
  }
99
- };
100
- var CommandGroupEntry = class extends BaseCommandGroupEntry {
101
- constructor(options, parent) {
102
- super(options);
103
- this.parent = parent;
73
+ ListenerAPI2.getEntry = getEntry;
74
+ })(ListenerAPI ||= {});
75
+ function ListenerFactory(options) {
76
+ let fullOptions = typeof options != "object" ? { name: options, once: false } : { once: false, ...options };
77
+ return new ListenerEntry(fullOptions);
78
+ }
79
+ var Listener = Object.assign(ListenerFactory, ListenerAPI);
80
+ var StateBox = class _StateBox {
81
+ static storage = new AsyncLocalStorage();
82
+ static getState() {
83
+ let state = this.storage.getStore();
84
+ if (!state)
85
+ throw new Error("No active context, did you forget to wrap it with StateBox.wrap()?");
86
+ return state;
104
87
  }
105
- };
106
- var SubcommandEntry = class extends BaseCommandEntry {
107
- constructor(options, parent) {
108
- super(options);
109
- this.parent = parent;
88
+ static run(fn, store2 = {}) {
89
+ return this.storage.run(store2, fn);
110
90
  }
111
- };
112
-
113
- // src/command/Command.ts
114
- var CommandAPI;
115
- ((CommandAPI2) => {
116
- const rootEntries = /* @__PURE__ */ new WeakMap();
117
- function use(root) {
118
- return (target) => {
119
- root.setTarget(target);
120
- rootEntries.set(target, root);
91
+ static wrap(fn) {
92
+ let currentStore = this.storage.getStore();
93
+ if (!currentStore)
94
+ throw new Error("No active context, cannot wrap function outside a StateBox.run()");
95
+ return () => this.run(fn, currentStore);
96
+ }
97
+ static use(defaultValue) {
98
+ return (target, key) => {
99
+ let generalKey = key;
100
+ Object.defineProperty(target, key, {
101
+ get() {
102
+ let states = _StateBox.getState();
103
+ return key in states || (states[generalKey] = defaultValue), states[generalKey];
104
+ },
105
+ set(value) {
106
+ let states = _StateBox.getState();
107
+ states[generalKey] = value;
108
+ },
109
+ enumerable: true,
110
+ configurable: true
111
+ });
121
112
  };
122
113
  }
123
- CommandAPI2.use = use;
124
- function getRoot(constructor) {
125
- return rootEntries.get(constructor);
114
+ };
115
+ var ListenerRegistry = class {
116
+ static client;
117
+ static constructors = /* @__PURE__ */ new Set();
118
+ static instances = /* @__PURE__ */ new WeakMap();
119
+ static executors = /* @__PURE__ */ new WeakMap();
120
+ /**
121
+ * Add and register a listener to the registry.
122
+ * If `options.emitter` is not provided, the registry will use the base `client` by default.
123
+ * @param constructor The listener class you want to add.
124
+ */
125
+ static add(constructor) {
126
+ let entry = Listener.getEntry(constructor);
127
+ if (!entry)
128
+ throw new Error(`No entry found for "${constructor.name}"`);
129
+ let { options } = entry;
130
+ if (!options.emitter) {
131
+ if (!this.client)
132
+ throw new Error("Client is not ready.");
133
+ options.emitter = this.client;
134
+ }
135
+ let instance = new constructor(), executor = this.createExecutor(constructor, instance);
136
+ this.constructors.add(constructor), this.instances.set(constructor, instance), this.executors.set(instance, executor), options.emitter[options.once ? "once" : "on"](options.name, (...args) => {
137
+ executor(...args);
138
+ });
126
139
  }
127
- CommandAPI2.getRoot = getRoot;
128
- })(CommandAPI || (CommandAPI = {}));
129
- function CommandFactory(options) {
130
- if (typeof options === "string") {
131
- options = { name: options };
140
+ /**
141
+ * Remove and unregister a listener from the registry.
142
+ * @param constructor The listener class you want to remove.
143
+ * @returns `boolean`, returns `true` if the listener is removed successfully.
144
+ */
145
+ static remove(constructor) {
146
+ let entry = Listener.getEntry(constructor);
147
+ if (!entry)
148
+ return false;
149
+ this.constructors.delete(constructor);
150
+ let instance = this.instances.get(constructor);
151
+ if (!instance)
152
+ return false;
153
+ this.instances.delete(constructor);
154
+ let executor = this.executors.get(instance);
155
+ if (!executor)
156
+ return false;
157
+ let { name, emitter } = entry.options;
158
+ return emitter?.removeListener(name, executor), this.executors.delete(instance), true;
132
159
  }
133
- if (!options.description) {
134
- options.description = options.name;
160
+ /**
161
+ * Remove and unregister all listeners from the registry.
162
+ * @returns Amount of removed listeners.
163
+ */
164
+ static removeAll() {
165
+ let removedAmount = 0;
166
+ for (let constructor of this.constructors)
167
+ this.remove(constructor) && removedAmount++;
168
+ return removedAmount;
135
169
  }
136
- return new RootCommandEntry(options);
137
- }
138
- var Command = Object.assign(CommandFactory, CommandAPI);
170
+ /**
171
+ * Set base client for the registry to fallback as default emitter. This should be used only by BakitClient and stay untouched.
172
+ * @param newClient base client to set for the registry.
173
+ */
174
+ static setClient(newClient) {
175
+ this.client = newClient;
176
+ }
177
+ static createExecutor(constructor, instance) {
178
+ let entry = Listener.getEntry(constructor);
179
+ if (!entry)
180
+ throw new Error("Missing listener entry");
181
+ let { hooks } = entry, execute = async (...args) => {
182
+ let mainHook = hooks.MAIN, preHook = hooks.PRE, postHook = hooks.POST, errorHook = hooks.ERROR;
183
+ if (mainHook)
184
+ try {
185
+ preHook && await preHook.method.call(instance, ...args), await mainHook.method.call(instance, ...args), postHook && await postHook.method.call(instance, ...args);
186
+ } catch (error) {
187
+ if (errorHook)
188
+ await errorHook.method.call(
189
+ instance,
190
+ error,
191
+ ...args
192
+ );
193
+ else
194
+ throw error;
195
+ }
196
+ };
197
+ return async (...args) => {
198
+ await StateBox.run(() => execute(...args));
199
+ };
200
+ }
201
+ /**
202
+ * Load and add all listeners which matched provided glob pattern to the registry.
203
+ * @param pattern glob pattern to load.
204
+ * @param parallel load all matched results in parallel, enabled by default.
205
+ * @returns All loaded listener constructors.
206
+ */
207
+ static async load(pattern, parallel = true) {
208
+ let loaders = (await glob(pattern)).map(async (file) => {
209
+ let fileURL = pathToFileURL(file).toString(), { default: constructor } = await import(fileURL);
210
+ return this.add(constructor), constructor;
211
+ });
212
+ if (parallel)
213
+ return Promise.all(loaders);
214
+ let result = [];
215
+ for (let loader of loaders)
216
+ result.push(await loader);
217
+ return result;
218
+ }
219
+ };
139
220
 
140
221
  // src/command/argument/Argument.ts
141
- var ArgumentType = /* @__PURE__ */ ((ArgumentType2) => {
142
- ArgumentType2["String"] = "string";
143
- ArgumentType2["Integer"] = "integer";
144
- ArgumentType2["Number"] = "number";
145
- ArgumentType2["User"] = "user";
146
- ArgumentType2["Member"] = "member";
147
- return ArgumentType2;
148
- })(ArgumentType || {});
222
+ var ArgumentType = /* @__PURE__ */ ((ArgumentType2) => (ArgumentType2.String = "string", ArgumentType2.Integer = "integer", ArgumentType2.Number = "number", ArgumentType2.User = "user", ArgumentType2.Member = "member", ArgumentType2.Literal = "literal", ArgumentType2))(ArgumentType || {});
149
223
 
150
224
  // src/command/argument/Arg.ts
151
225
  var store = /* @__PURE__ */ new WeakMap();
152
226
  function getMethodArguments(method, init = false) {
153
227
  let args = store.get(method);
154
- if (!args) {
155
- args = [];
156
- if (init) {
157
- store.set(method, args);
158
- }
159
- }
160
- return init ? args : Object.freeze([...args]);
228
+ return args || (args = [], init && store.set(method, args)), init ? args : Object.freeze([...args]);
161
229
  }
162
230
  function createArgument(type) {
163
231
  return function(options) {
164
- const objOptions = typeof options === "string" ? { name: options } : options;
165
- const fullOptions = { ...objOptions, type };
166
- if (!fullOptions.description) {
167
- fullOptions.description = fullOptions.name;
168
- }
169
- if (!("required" in fullOptions)) {
170
- fullOptions.required = true;
171
- }
172
- return function(target, key, _index) {
173
- const method = Object.getOwnPropertyDescriptor(target, key)?.value;
174
- if (!method) {
232
+ let fullOptions = { ...typeof options == "string" ? { name: options } : options, type };
233
+ return fullOptions.description || (fullOptions.description = fullOptions.name), "required" in fullOptions || (fullOptions.required = true), function(target, key, _index) {
234
+ let method = Object.getOwnPropertyDescriptor(target, key)?.value;
235
+ if (!method)
175
236
  throw new Error("No method found");
176
- }
177
- const args = getMethodArguments(method, true);
178
- args.unshift(fullOptions);
237
+ getMethodArguments(method, true).unshift(fullOptions);
179
238
  };
180
239
  };
181
240
  }
182
- var string = createArgument("string" /* String */);
183
- var integer = createArgument("integer" /* Integer */);
184
- var number = createArgument("number" /* Number */);
185
- var user = createArgument("user" /* User */);
186
- var member = createArgument("member" /* Member */);
241
+ var string = createArgument("string" /* String */), integer = createArgument("integer" /* Integer */), number = createArgument("number" /* Number */), user = createArgument("user" /* User */), member = createArgument("member" /* Member */);
187
242
  function describeArgumentExpectation(arg) {
188
- const parts = [arg.type];
243
+ let parts = [arg.type];
189
244
  switch (arg.type) {
190
245
  case "string" /* String */: {
191
- if (arg.minLength && !arg.maxLength) {
192
- parts.push(`\u2265 ${String(arg.minLength)}`);
193
- }
194
- if (!arg.minLength && arg.maxLength) {
195
- parts.push(`\u2264 ${String(arg.maxLength)}`);
196
- }
197
- if (arg.minLength && arg.maxLength) {
198
- parts.push(`${String(arg.minLength)} - ${String(arg.maxLength)}`);
199
- }
246
+ arg.minLength && !arg.maxLength && parts.push(`\u2265 ${String(arg.minLength)}`), !arg.minLength && arg.maxLength && parts.push(`\u2264 ${String(arg.maxLength)}`), arg.minLength && arg.maxLength && parts.push(`${String(arg.minLength)} - ${String(arg.maxLength)}`);
200
247
  break;
201
248
  }
202
249
  case "number" /* Number */:
203
250
  case "integer" /* Integer */: {
204
- if (arg.minValue !== void 0 && arg.maxValue === void 0) {
205
- parts.push(`\u2265 ${String(arg.minValue)}`);
206
- }
207
- if (arg.minValue === void 0 && arg.maxValue !== void 0) {
208
- parts.push(`\u2264 ${String(arg.maxValue)}`);
209
- }
210
- if (arg.minValue !== void 0 && arg.maxValue !== void 0) {
211
- parts.push(`${String(arg.minValue)} - ${String(arg.maxValue)}`);
212
- }
251
+ arg.minValue !== void 0 && arg.maxValue === void 0 && parts.push(`\u2265 ${String(arg.minValue)}`), arg.minValue === void 0 && arg.maxValue !== void 0 && parts.push(`\u2264 ${String(arg.maxValue)}`), arg.minValue !== void 0 && arg.maxValue !== void 0 && parts.push(`${String(arg.minValue)} - ${String(arg.maxValue)}`);
213
252
  break;
214
253
  }
215
254
  }
216
255
  return parts.join(", ");
217
256
  }
218
257
  function format(arg) {
219
- const { name, required, tuple } = arg;
220
- const opening = required ? "<" : "[";
221
- const closing = required ? ">" : "]";
222
- const prefix = tuple ? "..." : "";
223
- return `${opening}${prefix}${name}: ${describeArgumentExpectation(arg)}${closing}`;
258
+ if (arg.type === "literal" /* Literal */)
259
+ return arg.value;
260
+ let { required, tuple, name } = arg, opening = required ? "<" : "[", closing = required ? ">" : "]";
261
+ return `${opening}${tuple ? "..." : ""}${name}: ${describeArgumentExpectation(arg)}${closing}`;
224
262
  }
225
263
  var Arg = {
226
264
  getMethodArguments,
@@ -234,457 +272,141 @@ var Arg = {
234
272
  member
235
273
  };
236
274
 
237
- // src/command/CommandRegistry.ts
238
- var CommandRegistry = class _CommandRegistry {
239
- static constructors = new Collection2();
240
- static instances = new Collection2();
241
- /**
242
- * Add a command to the registry.
243
- * @param constructor The command class you want to add.
244
- */
245
- static add(constructor) {
246
- const root = Command.getRoot(constructor);
247
- if (!root) {
248
- throw new Error(`No root found for "${constructor.name}"`);
249
- }
250
- const { options } = root;
251
- this.constructors.set(options.name, constructor);
252
- this.instances.set(options.name, new constructor());
253
- }
254
- /**
255
- * Load and add all commands which matched provided glob pattern to the registry.
256
- * @param pattern glob pattern to load.
257
- * @param parallel load all matched results in parallel, enabled by default.
258
- * @returns All loaded command constructors.
259
- */
260
- static async load(pattern, parallel = true) {
261
- const files = await glob(pattern);
262
- const loaders = files.map(async (file) => {
263
- const fileURL = pathToFileURL(file).toString();
264
- const { default: constructor } = await import(fileURL);
265
- _CommandRegistry.add(constructor);
266
- return constructor;
267
- });
268
- if (parallel) {
269
- return await Promise.all(loaders);
270
- }
271
- const result = [];
272
- for (const loader of loaders) {
273
- result.push(await loader);
274
- }
275
- return result;
276
- }
277
- /**
278
- * Build a command into application command data.
279
- * @param constructor The command class you want to build.
280
- * @returns a REST JSON version of the application command data.
281
- */
282
- static buildSlashCommand(constructor) {
283
- const root = Command.getRoot(constructor);
284
- if (!root) {
285
- throw new Error(`No root found for "${constructor.name}"`);
286
- }
287
- const { options } = root;
288
- const builder = new SlashCommandBuilder().setName(options.name).setDescription(options.description).setNSFW(Boolean(options.nsfw));
289
- const args = this.getMainHookArguments(root);
290
- if (root.children.size) {
291
- this.buildSlashCommandSubcommands(builder, root, args);
292
- } else {
293
- this.buildSlashCommandOptions(builder, args);
294
- }
295
- return builder.toJSON();
296
- }
297
- static getMainHookArguments(entry) {
298
- const { hooks } = entry;
299
- const mainHook = hooks["MAIN" /* Main */];
300
- return mainHook ? Arg.getMethodArguments(mainHook.method) : [];
301
- }
302
- static buildSlashCommandSubcommands(parent, entry, inheritedArgs) {
303
- const { children } = entry;
304
- for (const child of children.values()) {
305
- if (child instanceof CommandGroupEntry && parent instanceof SlashCommandBuilder) {
306
- const { options } = child;
307
- const group = new SlashCommandSubcommandGroupBuilder().setName(options.name).setDescription(options.description);
308
- this.buildSlashCommandSubcommands(group, child, [
309
- ...inheritedArgs,
310
- ...this.getMainHookArguments(child)
311
- ]);
312
- parent.addSubcommandGroup(group);
313
- } else if (child instanceof SubcommandEntry) {
314
- const { options } = child;
315
- const subcommand = new SlashCommandSubcommandBuilder().setName(options.name).setDescription(options.description);
316
- this.buildSlashCommandOptions(subcommand, [
317
- ...inheritedArgs,
318
- ...this.getMainHookArguments(child)
319
- ]);
320
- parent.addSubcommand(subcommand);
321
- }
322
- }
323
- }
324
- static buildSlashCommandOptions(builder, args) {
325
- const argGroup = Object.groupBy(args, ({ required }) => required ? "required" : "optional");
326
- const orderedArgs = [...argGroup.required || [], ...argGroup.optional || []];
327
- for (const arg of orderedArgs) {
328
- this.attachSlashCommandOption(builder, arg);
329
- }
330
- }
331
- static attachSlashCommandOption(builder, arg) {
332
- const setupOption = (option) => {
333
- return option.setName(arg.name).setDescription(arg.description || arg.name).setRequired(Boolean(arg.required));
334
- };
335
- switch (arg.type) {
336
- case "string" /* String */: {
337
- builder.addStringOption((data) => {
338
- const option = setupOption(data);
339
- if (arg.maxLength) {
340
- option.setMaxLength(arg.maxLength);
341
- }
342
- if (arg.minLength) {
343
- option.setMinLength(arg.minLength);
344
- }
345
- return option;
346
- });
275
+ // src/utils/user.ts
276
+ function extractId(value) {
277
+ let idMatch = value.match(/^<@!?(\d+)>$/);
278
+ if (idMatch)
279
+ return idMatch[1];
280
+ let numericMatch = value.match(/^(\d{17,19})$/);
281
+ return numericMatch ? numericMatch[1] : null;
282
+ }
283
+
284
+ // src/errors/CommandSyntaxError.ts
285
+ var CommandSyntaxErrorType = /* @__PURE__ */ ((CommandSyntaxErrorType2) => (CommandSyntaxErrorType2.MissingRequireArgument = "MISSING_REQUIRE_ARGUMENT", CommandSyntaxErrorType2.InvalidArgument = "INVALID_ARGUMENT", CommandSyntaxErrorType2.InvalidVariadicArgumentValue = "INVALID_VARIADIC_ARGUMENT_VALUE", CommandSyntaxErrorType2))(CommandSyntaxErrorType || {}), CommandSyntaxError = class extends Error {
286
+ arg;
287
+ type;
288
+ expected;
289
+ received;
290
+ constructor(options) {
291
+ let message, { arg, type, received } = options, expected = Arg.describeArgumentExpectation(arg);
292
+ switch (type) {
293
+ case "MISSING_REQUIRE_ARGUMENT" /* MissingRequireArgument */: {
294
+ message = [`Missing required argument "${arg.name}"`, `> Expected: ${expected}`].join(`
295
+ `);
347
296
  break;
348
297
  }
349
- case "integer" /* Integer */: {
350
- builder.addIntegerOption((data) => {
351
- const option = setupOption(data);
352
- if (arg.maxValue) {
353
- option.setMaxValue(arg.maxValue);
354
- }
355
- if (arg.minValue) {
356
- option.setMinValue(arg.minValue);
357
- }
358
- return option;
359
- });
298
+ case "INVALID_ARGUMENT" /* InvalidArgument */: {
299
+ message = [
300
+ `Invalid value received for argument "${arg.name}"`,
301
+ `> Expected: ${expected}`,
302
+ `> Received: ${String(received)}`
303
+ ].join(`
304
+ `);
360
305
  break;
361
306
  }
362
- case "number" /* Number */: {
363
- builder.addNumberOption((data) => {
364
- const option = setupOption(data);
365
- if (arg.maxValue) {
366
- option.setMaxValue(arg.maxValue);
367
- }
368
- if (arg.minValue) {
369
- option.setMinValue(arg.minValue);
370
- }
371
- return option;
372
- });
307
+ case "INVALID_VARIADIC_ARGUMENT_VALUE" /* InvalidVariadicArgumentValue */: {
308
+ message = [
309
+ `Invalid value received for variadic argument "${arg.name}"`,
310
+ `> Expected: ${expected}`,
311
+ `> Received: ${String(received)}`
312
+ ].join(`
313
+ `);
373
314
  break;
374
315
  }
375
- case "user" /* User */:
376
- case "member" /* Member */: {
377
- builder.addUserOption((option) => setupOption(option));
316
+ default: {
317
+ message = "Unknown error";
378
318
  break;
379
319
  }
380
320
  }
321
+ super(message), this.arg = arg, this.type = type, this.expected = expected, this.received = received, Error.captureStackTrace(this, this.constructor);
322
+ }
323
+ get name() {
324
+ return `CommandSyntaxError[${this.type}]`;
381
325
  }
382
326
  };
383
327
 
384
- // src/command/Context.ts
385
- import {
386
- ChatInputCommandInteraction,
387
- Message
388
- } from "discord.js";
389
- var BaseContext = class {
390
- constructor(source) {
391
- this.source = source;
328
+ // src/command/argument/ArgumentResolver.ts
329
+ var ArgumentResolver = class _ArgumentResolver {
330
+ constructor(_values, prefix, message) {
331
+ this._values = _values;
332
+ this.prefix = prefix;
333
+ this.message = message;
392
334
  }
393
- get client() {
394
- return this.source.client;
335
+ _parsedValues = [];
336
+ _args = [];
337
+ get commandName() {
338
+ return this._values[0];
395
339
  }
396
- get channel() {
397
- return this.source.channel;
340
+ get values() {
341
+ return this._values;
398
342
  }
399
- get channelId() {
400
- return this.source.channelId;
343
+ get parsedValues() {
344
+ return this._parsedValues;
401
345
  }
402
- get guild() {
403
- return this.source.guild;
404
- }
405
- get guildId() {
406
- return this.source.guildId;
407
- }
408
- inGuild() {
409
- return Boolean(this.guildId);
410
- }
411
- inCachedGuild() {
412
- if (this.isChatInput()) {
413
- return this.source.inCachedGuild();
414
- } else if (this.isMessage()) {
415
- return this.source.inGuild();
416
- } else {
417
- throw new Error("Invalid source");
418
- }
419
- }
420
- get author() {
421
- if (this.isChatInput()) {
422
- return this.source.user;
423
- } else if (this.isMessage()) {
424
- return this.source.author;
425
- } else {
426
- throw new Error("Invalid source");
427
- }
428
- }
429
- isChatInput() {
430
- return this.source instanceof ChatInputCommandInteraction;
431
- }
432
- isMessage() {
433
- return this.source instanceof Message;
434
- }
435
- };
436
- var ChatInputContext = class extends BaseContext {
437
- async send(options) {
438
- if (typeof options === "string") {
439
- options = { content: options };
440
- }
441
- const sendOptions = {
442
- ...options,
443
- withResponse: true
444
- };
445
- if (this.source.deferred || this.source.replied) {
446
- return await this.source.followUp(sendOptions);
447
- }
448
- const response = await this.source.reply(sendOptions);
449
- return response.resource?.message;
450
- }
451
- };
452
- var MessageContext = class extends BaseContext {
453
- async send(options) {
454
- const { channel } = this;
455
- if (!channel?.isSendable()) {
456
- throw new Error("Invalid channel or channel is not sendable");
457
- }
458
- return await channel.send(options);
459
- }
460
- };
461
-
462
- // src/utils/user.ts
463
- function extractId(value) {
464
- const idMatch = value.match(/^<@!?(\d+)>$/);
465
- if (idMatch) {
466
- return idMatch[1];
467
- }
468
- const numericMatch = value.match(/^(\d{17,19})$/);
469
- if (numericMatch) {
470
- return numericMatch[1];
471
- }
472
- return null;
473
- }
474
-
475
- // src/errors/CommandSyntaxError.ts
476
- var CommandSyntaxErrorType = /* @__PURE__ */ ((CommandSyntaxErrorType2) => {
477
- CommandSyntaxErrorType2["MissingRequireArgument"] = "MISSING_REQUIRE_ARGUMENT";
478
- CommandSyntaxErrorType2["InvalidArgument"] = "INVALID_ARGUMENT";
479
- CommandSyntaxErrorType2["InvalidVariadicArgumentValue"] = "INVALID_VARIADIC_ARGUMENT_VALUE";
480
- return CommandSyntaxErrorType2;
481
- })(CommandSyntaxErrorType || {});
482
- var CommandSyntaxError = class extends Error {
483
- arg;
484
- type;
485
- expected;
486
- received;
487
- constructor(options) {
488
- let message;
489
- const { arg, type, received } = options;
490
- const expected = Arg.describeArgumentExpectation(arg);
491
- switch (type) {
492
- case "MISSING_REQUIRE_ARGUMENT" /* MissingRequireArgument */: {
493
- message = [`Missing required argument "${arg.name}"`, `> Expected: ${expected}`].join("\n");
494
- break;
495
- }
496
- case "INVALID_ARGUMENT" /* InvalidArgument */: {
497
- message = [
498
- `Invalid value received for argument "${arg.name}"`,
499
- `> Expected: ${expected}`,
500
- `> Received: ${String(received)}`
501
- ].join("\n");
502
- break;
503
- }
504
- case "INVALID_VARIADIC_ARGUMENT_VALUE" /* InvalidVariadicArgumentValue */: {
505
- message = [
506
- `Invalid value received for variadic argument "${arg.name}"`,
507
- `> Expected: ${expected}`,
508
- `> Received: ${String(received)}`
509
- ].join("\n");
510
- break;
511
- }
512
- default: {
513
- message = "Unknown error";
514
- break;
515
- }
516
- }
517
- super(message);
518
- this.arg = arg;
519
- this.type = type;
520
- this.expected = expected;
521
- this.received = received;
522
- Error.captureStackTrace(this, this.constructor);
523
- }
524
- get name() {
525
- return `CommandSyntaxError[${this.type}]`;
526
- }
527
- };
528
-
529
- // src/command/argument/ArgumentResolver.ts
530
- var ArgumentResolver = class _ArgumentResolver {
531
- constructor(options) {
532
- this.options = options;
533
- }
534
- parsedValues = [];
535
- /**
536
- * Get the first value as the command trigger.
537
- */
538
- get trigger() {
539
- return this.options.values[0];
540
- }
541
- /**
542
- * Get amount of specified argument values.
543
- */
544
- get specifiedAmount() {
545
- return this.options.values.length - this.options.startAt;
546
- }
547
- /**
548
- * Get parsed raw values from content.
549
- */
550
- get values() {
551
- return [...this.options.values];
346
+ get args() {
347
+ return this._args;
552
348
  }
553
349
  get client() {
554
- return this.options.message.client;
555
- }
556
- static create(message) {
557
- const client = message.client;
558
- const { enableMentionPrefix } = client.options;
559
- const prefixes = [
560
- // Custom prefixes specified in options
561
- ...client.options.prefixes ?? [],
562
- // Use bot mention as prefix if enabled
563
- ...enableMentionPrefix ? [client.user.toString()] : []
564
- ];
565
- const prefix = prefixes.find((p) => message.content.startsWith(p)) ?? null;
566
- if (!prefix) {
350
+ return this.message.client;
351
+ }
352
+ static async initialize(message) {
353
+ let prefix = (await message.client.dispatchers.command.getPrefixes(message)).find((p) => message.content.startsWith(p));
354
+ if (!prefix)
567
355
  return;
568
- }
569
- const values = message.content.slice(prefix.length).trim().split(/\s+/);
570
- return new _ArgumentResolver({
571
- message,
572
- startAt: 1,
573
- // Skip the command trigger
574
- values,
575
- args: [],
576
- prefix
577
- });
356
+ let values = message.content.slice(prefix.length).trim().split(/\s+/);
357
+ return new _ArgumentResolver(values, prefix, message);
578
358
  }
579
- async resolve(args) {
580
- const child = new _ArgumentResolver({
581
- prefix: this.options.prefix,
582
- message: this.options.message,
583
- values: this.options.values,
584
- args,
585
- startAt: this.options.startAt
586
- });
587
- child.parsedValues = [...this.parsedValues];
588
- if (!child.options.args.length) {
589
- return child;
359
+ async resolve(entry, at = 1) {
360
+ let mainHook = entry.hooks.MAIN, args = mainHook ? [...Arg.getMethodArguments(mainHook.method)] : [], nextAt = at;
361
+ if (args.length) {
362
+ let values = this.values.slice(at), parsedValues = args.length >= values.length ? await this.parseExact(args, values) : await this.parseFlexible(args, values);
363
+ nextAt += parsedValues.length, this._args.push(...args), this._parsedValues.push(...parsedValues);
590
364
  }
591
- if (this.specifiedAmount >= child.options.args.length) {
592
- await child.absoluteParse();
593
- } else {
594
- await child.dynamicParse();
595
- }
596
- return child;
597
- }
598
- async absoluteParse() {
599
- const { args, values, startAt } = this.options;
600
- let valueIndex = startAt;
601
- let argIndex = 0;
602
- while (valueIndex < values.length && argIndex < args.length) {
603
- const value = values[valueIndex];
604
- const arg = args[argIndex];
605
- if (arg.tuple) {
606
- this.parsedValues.push(...await this.resolveTuple(arg, valueIndex, argIndex));
607
- break;
608
- }
609
- const matchedValue = await this.matchValue(arg, value);
610
- if (matchedValue === null) {
365
+ let nextValue = this._values[nextAt];
366
+ if (!nextValue || !("children" in entry))
367
+ return;
368
+ let childEntry = entry.children.get(nextValue);
369
+ childEntry && (this._args.push({
370
+ type: "literal" /* Literal */,
371
+ value: nextValue
372
+ }), await this.resolve(childEntry, nextAt + 1));
373
+ }
374
+ async parseExact(args, values) {
375
+ let parsedValues = [];
376
+ for (let i = 0; i < args.length; i++) {
377
+ let arg = args[i], value = values[i], matchedValue = await this.matchValue(arg, value);
378
+ if (matchedValue === null)
611
379
  throw new CommandSyntaxError({
612
380
  arg,
613
381
  type: "INVALID_ARGUMENT" /* InvalidArgument */,
614
382
  received: value
615
383
  });
616
- }
617
- this.parsedValues.push(matchedValue);
618
- valueIndex++;
619
- argIndex++;
384
+ parsedValues.push(matchedValue);
620
385
  }
386
+ return parsedValues;
621
387
  }
622
- async dynamicParse() {
623
- const { args, values } = this.options;
624
- let argIndex = 0;
625
- let valueIndex = this.options.startAt + 1;
626
- while (valueIndex < values.length && argIndex < args.length) {
627
- const value = values[valueIndex];
628
- const arg = args[argIndex];
629
- if (arg.tuple) {
630
- this.parsedValues.push(...await this.resolveTuple(arg, valueIndex, argIndex));
631
- break;
632
- }
633
- const matchedValue = await this.matchValue(arg, value);
634
- if (matchedValue !== null) {
635
- this.parsedValues.push(matchedValue);
636
- valueIndex++;
637
- } else if (arg.required) {
638
- throw new CommandSyntaxError({
639
- arg,
640
- type: "MISSING_REQUIRE_ARGUMENT" /* MissingRequireArgument */,
641
- received: value
642
- });
643
- }
644
- argIndex++;
645
- }
646
- while (argIndex < args.length) {
647
- const arg = args[argIndex];
648
- if (arg.required) {
388
+ async parseFlexible(args, values) {
389
+ let argIndex = 0, valueIndex = 0, parsedValues = [];
390
+ for (; argIndex < args.length; ) {
391
+ let arg = args[argIndex], value = values[valueIndex], matchedValue = await this.matchValue(arg, value);
392
+ if (matchedValue !== null)
393
+ parsedValues.push(matchedValue), valueIndex++;
394
+ else if (arg.required)
649
395
  throw new CommandSyntaxError({
650
- arg,
651
396
  type: "MISSING_REQUIRE_ARGUMENT" /* MissingRequireArgument */,
652
- received: "nothing"
397
+ received: value,
398
+ arg
653
399
  });
654
- }
655
400
  argIndex++;
656
401
  }
657
- }
658
- async resolveTuple(arg, startIndex, argIndex) {
659
- const { args } = this.options;
660
- if (argIndex !== args.length - 1) {
661
- throw new SyntaxError("Tuple argument must be the last argument");
662
- }
663
- const values = [];
664
- for (const rest of this.values.slice(startIndex)) {
665
- const matchedValue = await this.matchValue(arg, rest);
666
- if (matchedValue === null) {
667
- throw new CommandSyntaxError({
668
- arg,
669
- type: "INVALID_VARIADIC_ARGUMENT_VALUE" /* InvalidVariadicArgumentValue */,
670
- received: rest
671
- });
672
- }
673
- values.push(matchedValue);
674
- }
675
- if (values.length === 0 && arg.required) {
676
- throw new CommandSyntaxError({
677
- arg,
678
- type: "MISSING_REQUIRE_ARGUMENT" /* MissingRequireArgument */,
679
- received: "nothing"
680
- });
681
- }
682
- return values;
402
+ return parsedValues;
683
403
  }
684
404
  async matchValue(arg, value) {
685
405
  switch (arg.type) {
686
406
  case "user" /* User */:
687
407
  return await this.matchUserValue(arg, value);
408
+ case "member" /* Member */:
409
+ return await this.matchMemberValue(arg, value);
688
410
  case "integer" /* Integer */:
689
411
  return this.matchIntegerValue(arg, value);
690
412
  case "number" /* Number */:
@@ -696,52 +418,29 @@ var ArgumentResolver = class _ArgumentResolver {
696
418
  }
697
419
  }
698
420
  async matchUserValue(arg, value) {
699
- const userId = extractId(value);
700
- if (!userId) {
701
- return null;
702
- }
703
- const user2 = await this.client.users.fetch(userId).catch(() => null);
704
- if (!user2) {
705
- return null;
706
- }
707
- return user2;
421
+ let userId = extractId(value);
422
+ return userId ? await this.client.users.fetch(userId).catch(() => null) : null;
423
+ }
424
+ async matchMemberValue(arg, value) {
425
+ let userId = extractId(value);
426
+ if (!userId)
427
+ return;
428
+ let { guild } = this.message;
429
+ if (guild)
430
+ return await guild.members.fetch(userId).catch(() => null);
708
431
  }
709
432
  matchIntegerValue(arg, value) {
710
- const intVal = parseInt(value, 10);
711
- if (isNaN(intVal)) {
712
- return null;
713
- }
714
- if (arg.minValue !== void 0 && intVal < arg.minValue) {
715
- return null;
716
- }
717
- if (arg.maxValue !== void 0 && intVal > arg.maxValue) {
718
- return null;
719
- }
720
- return intVal;
433
+ let intVal = parseInt(value, 10);
434
+ return isNaN(intVal) || arg.minValue !== void 0 && intVal < arg.minValue || arg.maxValue !== void 0 && intVal > arg.maxValue ? null : intVal;
721
435
  }
722
436
  matchNumberValue(arg, value) {
723
- const numVal = parseFloat(value);
724
- if (isNaN(numVal)) {
725
- return null;
726
- }
727
- if (arg.minValue !== void 0 && numVal < arg.minValue) {
728
- return null;
729
- }
730
- if (arg.maxValue !== void 0 && numVal > arg.maxValue) {
731
- return null;
732
- }
733
- return numVal;
437
+ let numVal = parseFloat(value);
438
+ return isNaN(numVal) || arg.minValue !== void 0 && numVal < arg.minValue || arg.maxValue !== void 0 && numVal > arg.maxValue ? null : numVal;
734
439
  }
735
440
  matchStringValue(arg, value) {
736
- if (arg.minLength !== void 0 && value.length < arg.minLength) {
737
- return null;
738
- }
739
- if (arg.maxLength !== void 0 && value.length > arg.maxLength) {
740
- return null;
741
- }
742
- return value;
441
+ return arg.minLength !== void 0 && value.length < arg.minLength || arg.maxLength !== void 0 && value.length > arg.maxLength ? null : value;
743
442
  }
744
- static resolveChatInput(interaction, arg) {
443
+ static resolveChatInputOption(interaction, arg) {
745
444
  switch (arg.type) {
746
445
  case "string" /* String */:
747
446
  return interaction.options.getString(arg.name, arg.required);
@@ -758,443 +457,363 @@ var ArgumentResolver = class _ArgumentResolver {
758
457
  }
759
458
  }
760
459
  };
761
-
762
- // src/libs/StateBox.ts
763
- import { AsyncLocalStorage } from "async_hooks";
764
- var StateBox = class _StateBox {
765
- static STATES_KEY = Symbol("states");
766
- static storage = new AsyncLocalStorage();
767
- static getState() {
768
- const state = this.storage.getStore();
769
- if (!state) {
770
- throw new Error("No active context, did you forget to wrap it with StateBox.wrap()?");
771
- }
772
- return state;
460
+ var BaseCommandEntry = class extends BaseEntry {
461
+ constructor(options) {
462
+ super();
463
+ this.options = options;
773
464
  }
774
- static run(fn, store2 = {}) {
775
- return this.storage.run(store2, fn);
465
+ }, BaseCommandGroupEntry = class extends BaseCommandEntry {
466
+ children = new Collection();
467
+ subcommand(options) {
468
+ let fullOptions = typeof options == "string" ? { name: options, description: `${options} command` } : { description: `${options.name} command`, ...options };
469
+ if (this.children.has(fullOptions.name))
470
+ throw new Error(`Entry "${fullOptions.name}" is already existed.`);
471
+ let subcommand = new SubcommandEntry(fullOptions, this);
472
+ return this.children.set(fullOptions.name, subcommand), subcommand;
776
473
  }
777
- static wrap(fn) {
778
- const currentStore = this.storage.getStore();
779
- if (!currentStore) {
780
- throw new Error("No active context, cannot wrap function outside a StateBox.run()");
781
- }
782
- return () => this.run(fn, currentStore);
474
+ }, RootCommandEntry = class extends BaseCommandGroupEntry {
475
+ group(options) {
476
+ let fullOptions = typeof options == "string" ? { name: options, description: `${options} command` } : { description: `${options.name} command`, ...options };
477
+ if (this.children.has(fullOptions.name))
478
+ throw new Error(`Entry "${fullOptions.name}" is already existed.`);
479
+ let group = new CommandGroupEntry(fullOptions, this);
480
+ return this.children.set(fullOptions.name, group), group;
783
481
  }
784
- static use(defaultValue) {
785
- return (target, key) => {
786
- Object.defineProperty(target, key, {
787
- get() {
788
- const states = _StateBox.getState();
789
- if (!(key in states)) {
790
- states[key] = defaultValue;
791
- }
792
- return states[key];
793
- },
794
- set(value) {
795
- const states = _StateBox.getState();
796
- states[key] = value;
797
- },
798
- enumerable: true,
799
- configurable: true
800
- });
801
- };
482
+ }, CommandGroupEntry = class extends BaseCommandGroupEntry {
483
+ constructor(options, parent) {
484
+ super(options);
485
+ this.parent = parent;
802
486
  }
803
- };
804
-
805
- // src/listener/ListenerEntry.ts
806
- var ListenerEntry = class extends BaseEntry {
807
- constructor(options) {
808
- super();
809
- this.options = options;
487
+ }, SubcommandEntry = class extends BaseCommandEntry {
488
+ constructor(options, parent) {
489
+ super(options);
490
+ this.parent = parent;
810
491
  }
811
492
  };
812
493
 
813
- // src/listener/Listener.ts
814
- var ListenerAPI;
815
- ((ListenerAPI2) => {
816
- const entries = /* @__PURE__ */ new WeakMap();
817
- function use(entry) {
494
+ // src/command/Command.ts
495
+ var CommandAPI;
496
+ ((CommandAPI2) => {
497
+ let rootEntries = /* @__PURE__ */ new WeakMap();
498
+ function use(root) {
818
499
  return (target) => {
819
- entries.set(target, entry);
500
+ root.setTarget(target), rootEntries.set(target, root);
820
501
  };
821
502
  }
822
- ListenerAPI2.use = use;
823
- function getEntry(target) {
824
- return entries.get(target);
503
+ CommandAPI2.use = use;
504
+ function getRoot(constructor) {
505
+ return rootEntries.get(constructor);
825
506
  }
826
- ListenerAPI2.getEntry = getEntry;
827
- })(ListenerAPI || (ListenerAPI = {}));
828
- function ListenerFactory(options) {
829
- const fullOptions = typeof options !== "object" ? { name: options, once: false } : { once: false, ...options };
830
- return new ListenerEntry(fullOptions);
507
+ CommandAPI2.getRoot = getRoot;
508
+ })(CommandAPI ||= {});
509
+ function CommandFactory(options) {
510
+ return typeof options == "string" && (options = { name: options }), options.description || (options.description = options.name), new RootCommandEntry(options);
831
511
  }
832
- var Listener = Object.assign(ListenerFactory, ListenerAPI);
833
-
834
- // src/listener/ListenerRegistry.ts
835
- import glob2 from "tiny-glob";
836
- import { pathToFileURL as pathToFileURL2 } from "url";
837
- var ListenerRegistry = class {
838
- static client;
839
- static constructors = /* @__PURE__ */ new Set();
840
- static instances = /* @__PURE__ */ new WeakMap();
841
- static executors = /* @__PURE__ */ new WeakMap();
512
+ var Command = Object.assign(CommandFactory, CommandAPI);
513
+ var CommandRegistry = class _CommandRegistry {
514
+ static constructors = new Collection();
515
+ static instances = new Collection();
842
516
  /**
843
- * Add and register a listener to the registry.
844
- * If `options.emitter` is not provided, the registry will use the base `client` by default.
845
- * @param constructor The listener class you want to add.
517
+ * Add a command to the registry.
518
+ * @param constructor The command class you want to add.
846
519
  */
847
520
  static add(constructor) {
848
- const entry = Listener.getEntry(constructor);
849
- if (!entry) {
850
- throw new Error(`No entry found for "${constructor.name}"`);
851
- }
852
- const { options } = entry;
853
- if (!options.emitter) {
854
- if (!this.client) {
855
- throw new Error("Client is not ready.");
856
- }
857
- options.emitter = this.client;
858
- }
859
- const instance = new constructor();
860
- const executor = this.createExecutor(constructor, instance);
861
- this.constructors.add(constructor);
862
- this.instances.set(constructor, instance);
863
- this.executors.set(instance, executor);
864
- options.emitter[options.once ? "once" : "on"](options.name, (...args) => {
865
- void executor(...args);
866
- });
521
+ let root = Command.getRoot(constructor);
522
+ if (!root)
523
+ throw new Error(`No root found for "${constructor.name}"`);
524
+ let { options } = root;
525
+ this.constructors.set(options.name, constructor), this.instances.set(options.name, new constructor());
867
526
  }
868
527
  /**
869
- * Remove and unregister a listener from the registry.
870
- * @param constructor The listener class you want to remove.
871
- * @returns `boolean`, returns `true` if the listener is removed successfully.
528
+ * Load and add all commands which matched provided glob pattern to the registry.
529
+ * @param pattern glob pattern to load.
530
+ * @param parallel load all matched results in parallel, enabled by default.
531
+ * @returns All loaded command constructors.
872
532
  */
873
- static remove(constructor) {
874
- const entry = Listener.getEntry(constructor);
875
- if (!entry) {
876
- return false;
877
- }
878
- this.constructors.delete(constructor);
879
- const instance = this.instances.get(constructor);
880
- if (!instance) {
881
- return false;
882
- }
883
- this.instances.delete(constructor);
884
- const executor = this.executors.get(instance);
885
- if (!executor) {
886
- return false;
887
- }
888
- const { name, emitter } = entry.options;
889
- emitter?.removeListener(name, executor);
890
- this.executors.delete(instance);
891
- return true;
533
+ static async load(pattern, parallel = true) {
534
+ let loaders = (await glob(pattern)).map(async (file) => {
535
+ let fileURL = pathToFileURL(file).toString(), { default: constructor } = await import(fileURL);
536
+ return _CommandRegistry.add(constructor), constructor;
537
+ });
538
+ if (parallel)
539
+ return await Promise.all(loaders);
540
+ let result = [];
541
+ for (let loader of loaders)
542
+ result.push(await loader);
543
+ return result;
892
544
  }
893
545
  /**
894
- * Remove and unregister all listeners from the registry.
895
- * @returns Amount of removed listeners.
546
+ * Build a command into application command data.
547
+ * @param constructor The command class you want to build.
548
+ * @returns a REST JSON version of the application command data.
896
549
  */
897
- static removeAll() {
898
- let removedAmount = 0;
899
- for (const constructor of this.constructors) {
900
- if (this.remove(constructor)) {
901
- removedAmount++;
550
+ static buildSlashCommand(constructor) {
551
+ let root = Command.getRoot(constructor);
552
+ if (!root)
553
+ throw new Error(`No root found for "${constructor.name}"`);
554
+ let { options } = root, builder = new SlashCommandBuilder().setName(options.name).setDescription(options.description).setNSFW(!!options.nsfw), args = this.getMainHookArguments(root);
555
+ return root.children.size ? this.buildSlashCommandSubcommands(builder, root, args) : this.buildSlashCommandOptions(builder, args), builder.toJSON();
556
+ }
557
+ static getMainHookArguments(entry) {
558
+ let { hooks } = entry, mainHook = hooks.MAIN;
559
+ return mainHook ? Arg.getMethodArguments(mainHook.method) : [];
560
+ }
561
+ static buildSlashCommandSubcommands(parent, entry, inheritedArgs) {
562
+ let { children } = entry;
563
+ for (let child of children.values())
564
+ if (child instanceof CommandGroupEntry && parent instanceof SlashCommandBuilder) {
565
+ let { options } = child, group = new SlashCommandSubcommandGroupBuilder().setName(options.name).setDescription(options.description);
566
+ this.buildSlashCommandSubcommands(group, child, [
567
+ ...inheritedArgs,
568
+ ...this.getMainHookArguments(child)
569
+ ]), parent.addSubcommandGroup(group);
570
+ } else if (child instanceof SubcommandEntry) {
571
+ let { options } = child, subcommand = new SlashCommandSubcommandBuilder().setName(options.name).setDescription(options.description);
572
+ this.buildSlashCommandOptions(subcommand, [
573
+ ...inheritedArgs,
574
+ ...this.getMainHookArguments(child)
575
+ ]), parent.addSubcommand(subcommand);
902
576
  }
903
- }
904
- return removedAmount;
905
577
  }
906
- /**
907
- * Set base client for the registry to fallback as default emitter. This should be used only by BakitClient and stay untouched.
908
- * @param newClient base client to set for the registry.
909
- */
910
- static setClient(newClient) {
911
- this.client = newClient;
578
+ static buildSlashCommandOptions(builder, args) {
579
+ let argGroup = Object.groupBy(args, ({ required }) => required ? "required" : "optional"), orderedArgs = [...argGroup.required || [], ...argGroup.optional || []];
580
+ for (let arg of orderedArgs)
581
+ this.attachSlashCommandOption(builder, arg);
912
582
  }
913
- static createExecutor(constructor, instance) {
914
- const entry = Listener.getEntry(constructor);
915
- if (!entry) {
916
- throw new Error("Missing listener entry");
917
- }
918
- const { hooks } = entry;
919
- return async function(...args) {
920
- const mainHook = hooks["MAIN" /* Main */];
921
- const preHook = hooks["PRE" /* Pre */];
922
- const postHook = hooks["POST" /* Post */];
923
- const errorHook = hooks["ERROR" /* Error */];
924
- if (!mainHook) {
925
- return;
583
+ static attachSlashCommandOption(builder, arg) {
584
+ let setupOption = (option) => option.setName(arg.name).setDescription(arg.description || arg.name).setRequired(!!arg.required);
585
+ switch (arg.type) {
586
+ case "string" /* String */: {
587
+ builder.addStringOption((data) => {
588
+ let option = setupOption(data);
589
+ return arg.maxLength && option.setMaxLength(arg.maxLength), arg.minLength && option.setMinLength(arg.minLength), option;
590
+ });
591
+ break;
926
592
  }
927
- try {
928
- if (preHook) {
929
- await preHook.method.call(instance, ...args);
930
- }
931
- await mainHook.method.call(instance, ...args);
932
- if (postHook) {
933
- await postHook.method.call(instance, ...args);
934
- }
935
- } catch (error) {
936
- if (errorHook) {
937
- await errorHook.method.call(
938
- instance,
939
- error,
940
- ...args
941
- );
942
- } else {
943
- throw error;
944
- }
593
+ case "integer" /* Integer */: {
594
+ builder.addIntegerOption((data) => {
595
+ let option = setupOption(data);
596
+ return arg.maxValue && option.setMaxValue(arg.maxValue), arg.minValue && option.setMinValue(arg.minValue), option;
597
+ });
598
+ break;
945
599
  }
600
+ case "number" /* Number */: {
601
+ builder.addNumberOption((data) => {
602
+ let option = setupOption(data);
603
+ return arg.maxValue && option.setMaxValue(arg.maxValue), arg.minValue && option.setMinValue(arg.minValue), option;
604
+ });
605
+ break;
606
+ }
607
+ case "user" /* User */:
608
+ case "member" /* Member */: {
609
+ builder.addUserOption((option) => setupOption(option));
610
+ break;
611
+ }
612
+ }
613
+ }
614
+ };
615
+ var BaseContext = class {
616
+ constructor(source) {
617
+ this.source = source;
618
+ }
619
+ get client() {
620
+ return this.source.client;
621
+ }
622
+ get channel() {
623
+ return this.source.channel;
624
+ }
625
+ get channelId() {
626
+ return this.source.channelId;
627
+ }
628
+ get guild() {
629
+ return this.source.guild;
630
+ }
631
+ get guildId() {
632
+ return this.source.guildId;
633
+ }
634
+ inGuild() {
635
+ return !!this.guildId;
636
+ }
637
+ inCachedGuild() {
638
+ if (this.isChatInput())
639
+ return this.source.inCachedGuild();
640
+ if (this.isMessage())
641
+ return this.source.inGuild();
642
+ throw new Error("Invalid source");
643
+ }
644
+ get author() {
645
+ if (this.isChatInput())
646
+ return this.source.user;
647
+ if (this.isMessage())
648
+ return this.source.author;
649
+ throw new Error("Invalid source");
650
+ }
651
+ isChatInput() {
652
+ return this.source instanceof ChatInputCommandInteraction;
653
+ }
654
+ isMessage() {
655
+ return this.source instanceof Message;
656
+ }
657
+ }, ChatInputContext = class extends BaseContext {
658
+ async send(options) {
659
+ typeof options == "string" && (options = { content: options });
660
+ let sendOptions = {
661
+ ...options,
662
+ withResponse: true
946
663
  };
664
+ return this.source.deferred || this.source.replied ? await this.source.followUp(sendOptions) : (await this.source.reply(sendOptions)).resource?.message;
947
665
  }
948
- /**
949
- * Load and add all listeners which matched provided glob pattern to the registry.
950
- * @param pattern glob pattern to load.
951
- * @param parallel load all matched results in parallel, enabled by default.
952
- * @returns All loaded listener constructors.
953
- */
954
- static async load(pattern, parallel = true) {
955
- const files = await glob2(pattern);
956
- const loaders = files.map(async (file) => {
957
- const fileURL = pathToFileURL2(file).toString();
958
- const { default: constructor } = await import(fileURL);
959
- this.add(constructor);
960
- return constructor;
961
- });
962
- if (parallel) {
963
- return Promise.all(loaders);
964
- }
965
- const result = [];
966
- for (const loader of loaders) {
967
- result.push(await loader);
968
- }
969
- return result;
666
+ }, MessageContext = class extends BaseContext {
667
+ async send(options) {
668
+ let { channel } = this;
669
+ if (!channel?.isSendable())
670
+ throw new Error("Invalid channel or channel is not sendable");
671
+ return await channel.send(options);
970
672
  }
971
673
  };
972
674
 
973
- // src/BakitClient.ts
974
- var BakitClient = class _BakitClient extends Client {
975
- constructor(options) {
976
- if (options.getSyntaxErrorMessage === void 0) {
977
- options.getSyntaxErrorMessage = _BakitClient.getSyntaxErrorMessage;
978
- }
979
- super(options);
980
- ListenerRegistry["setClient"](this);
981
- this.once(
982
- Events.ClientReady,
983
- (client) => void this.registerApplicationCommands(client)
984
- );
985
- this.on(Events.InteractionCreate, (interaction) => void this.handleInteraction(interaction));
986
- this.on(Events.MessageCreate, (message) => void this.handleMessage(message));
987
- }
988
- static getSyntaxErrorMessage = (command, error, context, args, prefix) => {
989
- const requiredSyntax = args.map((x) => Arg.format(x)).join(" ");
990
- const root = Command.getRoot(command.constructor);
991
- if (!root) {
992
- return;
993
- }
994
- const content = [
675
+ // src/utils/command.ts
676
+ var defaultGetSyntaxErrorMessage = (command, error, context, resolver) => {
677
+ let requiredSyntax = resolver.args.map((x) => Arg.format(x)).join(" "), root = Command.getRoot(command.constructor);
678
+ return root ? {
679
+ content: [
995
680
  codeBlock(error.message),
996
681
  "Required Syntax:",
997
- codeBlock(`${prefix}${root.options.name} ${requiredSyntax}`)
998
- ].join("\n");
999
- return {
1000
- content
1001
- };
1002
- };
1003
- async registerApplicationCommands(client) {
1004
- const commands = CommandRegistry.constructors.map((c) => CommandRegistry.buildSlashCommand(c));
1005
- await client.application.commands.set(commands);
1006
- }
1007
- async handleMessage(message) {
1008
- if (message.author.bot) {
1009
- return;
1010
- }
1011
- const context = new MessageContext(message);
1012
- const resolver = ArgumentResolver.create(message);
1013
- if (!resolver) {
682
+ codeBlock(`${resolver.prefix}${root.options.name} ${requiredSyntax}`)
683
+ ].join(`
684
+ `)
685
+ } : void 0;
686
+ };
687
+
688
+ // src/dispatchers/CommandDispatcher.ts
689
+ var CommandDispatcher = class {
690
+ constructor(client) {
691
+ this.client = client;
692
+ }
693
+ async getPrefixes(message) {
694
+ let { options, user: user2 } = this.client, results = await Promise.all(
695
+ (options.prefixes ?? []).map(async (prefix) => {
696
+ if (typeof prefix == "string")
697
+ return [prefix];
698
+ let result = await prefix(message);
699
+ return Array.isArray(result) ? result : [result];
700
+ })
701
+ ), prefixes = [...options.enableMentionPrefix && user2 ? [user2.toString()] : [], ...results.flat()];
702
+ return Array.from(new Set(prefixes));
703
+ }
704
+ async handleChatInput(interaction) {
705
+ let { commandName } = interaction, constructor = CommandRegistry.constructors.get(commandName), instance = CommandRegistry.instances.get(commandName);
706
+ if (!constructor || !instance)
1014
707
  return;
1015
- }
1016
- const { trigger } = resolver;
1017
- const command = CommandRegistry.instances.get(trigger);
1018
- if (!command) {
708
+ let root = Command.getRoot(constructor);
709
+ if (!root)
1019
710
  return;
1020
- }
1021
- await StateBox.run(() => this.handleMessageHooks(context, command, resolver));
711
+ let context = new ChatInputContext(interaction), triggerChain = this.getChatInputTriggerChain(interaction), chain = this.resolveCommandEntryChain(root, triggerChain), parsedValues = chain.flatMap((entry) => this.resolveChatInputEntry(interaction, entry));
712
+ await StateBox.run(() => this.executeChain(chain, context, instance, parsedValues));
1022
713
  }
1023
- async handleInteraction(interaction) {
1024
- if (!interaction.isChatInputCommand()) {
1025
- return;
1026
- }
1027
- const { commandName } = interaction;
1028
- const command = CommandRegistry.instances.get(commandName);
1029
- if (!command) {
1030
- return;
1031
- }
1032
- const context = new ChatInputContext(interaction);
1033
- await StateBox.run(() => this.handleChatInputHooks(context, command));
1034
- }
1035
- async handleChatInputHooks(context, instance) {
1036
- const targetHooks = this.getChatInputTargetHooks(context.source, instance);
1037
- let inheritedArgs = [];
1038
- for (const hooks of [targetHooks.root, targetHooks.group, targetHooks.subcommand]) {
1039
- if (!hooks) {
1040
- continue;
1041
- }
1042
- const newArgs = await this.runChatInputHooks(context, instance, hooks, inheritedArgs);
1043
- if (newArgs) {
1044
- inheritedArgs = newArgs;
1045
- }
1046
- }
714
+ getChatInputTriggerChain(interaction) {
715
+ let chain = [], subcommand = interaction.options.getSubcommand(false), subcommandGroup = interaction.options.getSubcommandGroup(false);
716
+ return subcommandGroup && chain.push(subcommandGroup), subcommand && chain.push(subcommand), chain;
1047
717
  }
1048
- async handleMessageHooks(context, instance, resolver) {
1049
- if (!resolver) {
1050
- return;
1051
- }
1052
- const root = Command.getRoot(instance.constructor);
1053
- if (!root) {
1054
- return;
1055
- }
1056
- resolver = await this.runMessageHooks(context, instance, root.hooks, resolver);
1057
- if (!resolver) {
1058
- return;
1059
- }
1060
- await this.handleChildMessageHooks(context, root, instance, resolver);
718
+ resolveChatInputEntry(interaction, entry) {
719
+ let mainHook = entry.hooks.MAIN;
720
+ return (mainHook ? Arg.getMethodArguments(mainHook.method) : []).map((arg) => ArgumentResolver.resolveChatInputOption(interaction, arg));
1061
721
  }
1062
- async handleChildMessageHooks(context, parent, instance, resolver, skip = 1) {
1063
- if (!resolver) {
722
+ async handleMessage(message) {
723
+ let resolver = await ArgumentResolver.initialize(message);
724
+ if (!resolver)
1064
725
  return;
1065
- }
1066
- const usedValues = resolver.parsedValues.length;
1067
- const nextTrigger = resolver.values[usedValues + skip];
1068
- const child = parent.children.get(nextTrigger);
1069
- if (!child) {
726
+ let constructor = CommandRegistry.constructors.get(resolver.commandName), instance = CommandRegistry.instances.get(resolver.commandName);
727
+ if (!constructor || !instance)
1070
728
  return;
1071
- }
1072
- resolver = await this.runMessageHooks(context, instance, child.hooks, resolver);
1073
- if (child instanceof CommandGroupEntry) {
1074
- await this.handleChildMessageHooks(context, child, instance, resolver, skip + 1);
1075
- }
1076
- }
1077
- async runMessageHooks(context, instance, hooks, resolver) {
1078
- const mainHook = hooks["MAIN" /* Main */];
1079
- if (!mainHook) {
1080
- return resolver;
1081
- }
1082
- const args = Arg.getMethodArguments(mainHook.method);
729
+ let root = Command.getRoot(constructor);
730
+ if (!root)
731
+ return;
732
+ let context = new MessageContext(message);
1083
733
  try {
1084
- resolver = await resolver.resolve(args);
734
+ await resolver.resolve(root);
1085
735
  } catch (error) {
1086
736
  if (error instanceof CommandSyntaxError) {
1087
- const errorContent = await this.options.getSyntaxErrorMessage?.(
737
+ let payload = await this.client.options.getSyntaxErrorMessage?.(
1088
738
  instance,
1089
739
  error,
1090
740
  context,
1091
- args,
1092
- resolver.options.prefix
741
+ resolver
1093
742
  );
1094
- if (errorContent) {
1095
- await context.send(errorContent);
1096
- }
1097
- return null;
1098
- }
1099
- throw error;
743
+ payload && await context.send(payload);
744
+ } else
745
+ throw error;
1100
746
  }
1101
- await this.runHooks(context, instance, hooks, resolver.parsedValues);
1102
- return resolver;
747
+ let literalTriggers = resolver.args.filter((arg) => arg.type === "literal" /* Literal */).map((arg) => arg.value), entryChain = this.resolveCommandEntryChain(root, literalTriggers), values = resolver.parsedValues;
748
+ await StateBox.run(() => this.executeChain(entryChain, context, instance, values));
1103
749
  }
1104
- async runChatInputHooks(context, instance, hooks, inheritedArgs) {
1105
- const mainHook = hooks["MAIN" /* Main */];
1106
- if (!mainHook) {
1107
- return;
1108
- }
1109
- const newArgs = Arg.getMethodArguments(mainHook.method).map(
1110
- (arg) => ArgumentResolver.resolveChatInput(context.source, arg)
750
+ resolveCommandEntryChain(root, triggers) {
751
+ return triggers.reduce(
752
+ (acc, trigger) => {
753
+ let parent = acc.at(-1);
754
+ if (parent && "children" in parent) {
755
+ let child = parent.children.get(trigger);
756
+ child && acc.push(child);
757
+ }
758
+ return acc;
759
+ },
760
+ [root]
1111
761
  );
1112
- const argValues = [...inheritedArgs, ...newArgs];
1113
- await this.runHooks(context, instance, hooks, argValues);
1114
- return argValues;
1115
- }
1116
- async runHooks(context, instance, hooks, args) {
1117
- const mainHook = hooks["MAIN" /* Main */];
1118
- const preHook = hooks["PRE" /* Pre */];
1119
- const postHook = hooks["POST" /* Post */];
1120
- const errorHook = hooks["ERROR" /* Error */];
1121
- if (!mainHook) {
762
+ }
763
+ async executeChain(chain, context, instance, values) {
764
+ for (let entry of chain)
765
+ await this.executeHooks(entry, context, instance, values);
766
+ }
767
+ async executeHooks(entry, context, instance, values) {
768
+ let { hooks } = entry;
769
+ if (!hooks.MAIN)
1122
770
  return;
1123
- }
1124
- const execute = async (hook, error) => {
1125
- if (!hook) {
1126
- return;
1127
- }
1128
- if (hook.state === "ERROR" /* Error */) {
1129
- await hook.method.call(instance, error, context, ...args);
1130
- } else {
1131
- await hook.method.call(instance, context, ...args);
771
+ let execute = async (hook, error) => {
772
+ if (hook) {
773
+ if (hook.state === "ERROR" /* Error */) {
774
+ await hook.method.call(instance, error, context, ...values);
775
+ return;
776
+ }
777
+ await hook.method.call(instance, context, ...values);
1132
778
  }
1133
779
  };
1134
780
  try {
1135
- await execute(preHook);
1136
- await execute(mainHook);
1137
- await execute(postHook);
781
+ await execute(hooks.PRE), await execute(hooks.MAIN), await execute(hooks.POST);
1138
782
  } catch (error) {
1139
- if (errorHook) {
1140
- await execute(errorHook, error);
1141
- } else {
783
+ if (hooks.ERROR)
784
+ await execute(hooks.ERROR, error);
785
+ else
1142
786
  throw error;
1143
- }
1144
787
  }
1145
788
  }
1146
- getChatInputTargetHooks(interaction, instance) {
1147
- const subcommandName = interaction.options.getSubcommand(false);
1148
- const groupName = interaction.options.getSubcommandGroup(false);
1149
- const root = Command.getRoot(instance.constructor);
1150
- if (!root) {
1151
- throw new Error("No root found.");
1152
- }
1153
- let group;
1154
- if (groupName) {
1155
- const child = root.children.get(groupName);
1156
- if (child instanceof CommandGroupEntry) {
1157
- group = child;
1158
- }
1159
- }
1160
- let subcommand;
1161
- if (subcommandName) {
1162
- const parent = group || root;
1163
- const child = parent.children.get(subcommandName);
1164
- if (child instanceof SubcommandEntry) {
1165
- subcommand = child;
1166
- }
1167
- }
1168
- return {
1169
- root: root.hooks,
1170
- group: group?.hooks,
1171
- subcommand: subcommand?.hooks
1172
- };
789
+ };
790
+
791
+ // src/dispatchers/DispatcherManager.ts
792
+ var DispatcherManager = class {
793
+ constructor(client) {
794
+ this.client = client;
795
+ this.command = new CommandDispatcher(client);
1173
796
  }
797
+ command;
1174
798
  };
1175
- export {
1176
- Arg,
1177
- ArgumentType,
1178
- BakitClient,
1179
- BaseCommandEntry,
1180
- BaseCommandGroupEntry,
1181
- BaseContext,
1182
- ChatInputContext,
1183
- Command,
1184
- CommandAPI,
1185
- CommandFactory,
1186
- CommandGroupEntry,
1187
- CommandRegistry,
1188
- CommandSyntaxError,
1189
- CommandSyntaxErrorType,
1190
- Listener,
1191
- ListenerAPI,
1192
- ListenerEntry,
1193
- ListenerFactory,
1194
- ListenerRegistry,
1195
- MessageContext,
1196
- RootCommandEntry,
1197
- StateBox,
1198
- SubcommandEntry,
1199
- extractId
799
+
800
+ // src/BakitClient.ts
801
+ var BakitClient = class extends Client {
802
+ dispatchers;
803
+ constructor(options) {
804
+ options.getSyntaxErrorMessage === void 0 && (options.getSyntaxErrorMessage = defaultGetSyntaxErrorMessage), super(options), ListenerRegistry.setClient(this), this.dispatchers = new DispatcherManager(this), this.initializeHandlers();
805
+ }
806
+ isReady() {
807
+ return super.isReady();
808
+ }
809
+ initializeHandlers() {
810
+ this.on(
811
+ Events.MessageCreate,
812
+ (message) => void this.dispatchers.command.handleMessage(message)
813
+ ), this.on(Events.InteractionCreate, (interaction) => {
814
+ interaction.isChatInputCommand() && this.dispatchers.command.handleChatInput(interaction);
815
+ });
816
+ }
1200
817
  };
818
+
819
+ export { Arg, ArgumentResolver, ArgumentType, BakitClient, BaseCommandEntry, BaseCommandGroupEntry, BaseContext, ChatInputContext, Command, CommandAPI, CommandFactory, CommandGroupEntry, CommandRegistry, CommandSyntaxError, CommandSyntaxErrorType, Listener, ListenerAPI, ListenerEntry, ListenerFactory, ListenerRegistry, MessageContext, RootCommandEntry, StateBox, SubcommandEntry, defaultGetSyntaxErrorMessage, extractId };