bakit 2.0.0-alpha.32 → 2.0.0-alpha.33

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,1196 +1 @@
1
- import { GatewayIntentBits, Events, Collection, Client, IntentsBitField, SlashCommandBuilder, SlashCommandStringOption, SlashCommandNumberOption, SlashCommandUserOption, ChatInputCommandInteraction, Message } from 'discord.js';
2
- import { inspect } from 'util';
3
- import { pathToFileURL, fileURLToPath } from 'url';
4
- import { join, resolve, relative, sep, dirname } from 'path';
5
- import glob from 'tiny-glob';
6
- import z2 from 'zod';
7
- import { register } from 'module';
8
- import { MessageChannel } from 'worker_threads';
9
- import { randomUUID, createHash } from 'crypto';
10
- import EventEmitter from 'events';
11
- import { existsSync, mkdirSync, rmSync } from 'fs';
12
- import { mkdir, writeFile, readFile, rm } from 'fs/promises';
13
1
 
14
- var __defProp = Object.defineProperty;
15
- var __export = (target, all) => {
16
- for (var name in all)
17
- __defProp(target, name, { get: all[name], enumerable: true });
18
- };
19
- var ParamUserType = /* @__PURE__ */ ((ParamUserType2) => (ParamUserType2.Bot = "bot", ParamUserType2.Normal = "normal", ParamUserType2.Any = "any", ParamUserType2))(ParamUserType || {}), BaseParamSchema = z2.object({
20
- name: z2.string(),
21
- description: z2.string().optional(),
22
- required: z2.boolean().default(true)
23
- }), StringParamSchema = BaseParamSchema.extend({
24
- maxLength: z2.number().min(1).optional(),
25
- minLength: z2.number().min(1).optional()
26
- }), NumberParamSchema = BaseParamSchema.extend({
27
- maxValue: z2.number().optional(),
28
- minValue: z2.number().optional()
29
- }), UserParamSchema = BaseParamSchema.extend({});
30
- var INTENT_GROUPS = {
31
- [GatewayIntentBits.Guilds]: [
32
- Events.GuildCreate,
33
- Events.GuildDelete,
34
- Events.GuildUpdate,
35
- Events.GuildUnavailable,
36
- Events.GuildRoleCreate,
37
- Events.GuildRoleDelete,
38
- Events.GuildRoleUpdate,
39
- Events.ChannelCreate,
40
- Events.ChannelDelete,
41
- Events.ChannelUpdate,
42
- Events.ChannelPinsUpdate,
43
- Events.ThreadCreate,
44
- Events.ThreadDelete,
45
- Events.ThreadUpdate,
46
- Events.ThreadListSync,
47
- Events.ThreadMemberUpdate,
48
- Events.ThreadMembersUpdate,
49
- Events.StageInstanceCreate,
50
- Events.StageInstanceUpdate,
51
- Events.StageInstanceDelete
52
- ],
53
- [GatewayIntentBits.GuildMembers]: [
54
- Events.GuildMemberAdd,
55
- Events.GuildMemberUpdate,
56
- Events.GuildMemberRemove,
57
- Events.ThreadMembersUpdate
58
- ],
59
- [GatewayIntentBits.GuildModeration]: [Events.GuildAuditLogEntryCreate, Events.GuildBanAdd, Events.GuildBanRemove],
60
- [GatewayIntentBits.GuildExpressions]: [
61
- Events.GuildEmojiCreate,
62
- Events.GuildEmojiDelete,
63
- Events.GuildEmojiUpdate,
64
- Events.GuildStickerCreate,
65
- Events.GuildStickerDelete,
66
- Events.GuildStickerUpdate,
67
- Events.GuildSoundboardSoundCreate,
68
- Events.GuildSoundboardSoundUpdate,
69
- Events.GuildSoundboardSoundDelete,
70
- Events.GuildSoundboardSoundsUpdate
71
- ],
72
- [GatewayIntentBits.GuildIntegrations]: [Events.GuildIntegrationsUpdate],
73
- [GatewayIntentBits.GuildWebhooks]: [Events.WebhooksUpdate],
74
- [GatewayIntentBits.GuildInvites]: [Events.InviteCreate, Events.InviteDelete],
75
- [GatewayIntentBits.GuildVoiceStates]: [Events.VoiceStateUpdate],
76
- [GatewayIntentBits.GuildPresences]: [Events.PresenceUpdate],
77
- [GatewayIntentBits.GuildMessages]: [
78
- Events.MessageCreate,
79
- Events.MessageUpdate,
80
- Events.MessageDelete,
81
- Events.MessageBulkDelete
82
- ],
83
- [GatewayIntentBits.GuildMessageReactions]: [
84
- Events.MessageReactionAdd,
85
- Events.MessageReactionRemove,
86
- Events.MessageReactionRemoveAll,
87
- Events.MessageReactionRemoveEmoji
88
- ],
89
- [GatewayIntentBits.GuildMessageTyping]: [Events.TypingStart],
90
- [GatewayIntentBits.DirectMessages]: [
91
- Events.MessageCreate,
92
- Events.MessageUpdate,
93
- Events.MessageDelete,
94
- Events.ChannelPinsUpdate
95
- ],
96
- [GatewayIntentBits.DirectMessageReactions]: [
97
- Events.MessageReactionAdd,
98
- Events.MessageReactionRemove,
99
- Events.MessageReactionRemoveAll,
100
- Events.MessageReactionRemoveEmoji
101
- ],
102
- [GatewayIntentBits.DirectMessageTyping]: [Events.TypingStart],
103
- [GatewayIntentBits.MessageContent]: [Events.MessageCreate, Events.MessageUpdate],
104
- [GatewayIntentBits.GuildScheduledEvents]: [
105
- Events.GuildScheduledEventCreate,
106
- Events.GuildScheduledEventDelete,
107
- Events.GuildScheduledEventUpdate,
108
- Events.GuildScheduledEventUserAdd,
109
- Events.GuildScheduledEventUserRemove
110
- ],
111
- [GatewayIntentBits.AutoModerationConfiguration]: [
112
- Events.AutoModerationRuleCreate,
113
- Events.AutoModerationRuleDelete,
114
- Events.AutoModerationRuleUpdate
115
- ],
116
- [GatewayIntentBits.AutoModerationExecution]: [Events.AutoModerationActionExecution],
117
- [GatewayIntentBits.GuildMessagePolls]: [Events.MessagePollVoteAdd, Events.MessagePollVoteRemove],
118
- [GatewayIntentBits.DirectMessagePolls]: [Events.MessagePollVoteAdd, Events.MessagePollVoteRemove]
119
- }, EVENT_INTENT_MAPPING = {};
120
- for (let [intentStr, events] of Object.entries(INTENT_GROUPS)) {
121
- let intent = Number(intentStr);
122
- for (let event of events)
123
- EVENT_INTENT_MAPPING[event] ??= [], EVENT_INTENT_MAPPING[event].includes(intent) || EVENT_INTENT_MAPPING[event].push(intent);
124
- }
125
-
126
- // src/lib/errors/BakitError.ts
127
- var BakitError = class extends Error {
128
- constructor(message) {
129
- super(message), this.name = this.constructor.name, Object.setPrototypeOf(this, new.target.prototype);
130
- }
131
- };
132
-
133
- // src/lib/errors/ArgumentError.ts
134
- var ArgumentError = class extends BakitError {
135
- constructor(target, reason) {
136
- super(`Invalid argument for '${target}': ${reason}`);
137
- this.target = target;
138
- this.reason = reason;
139
- }
140
- };
141
-
142
- // src/lib/utils/string.ts
143
- function tokenize(content) {
144
- let args = [], current = "", quoteChar = null, isEscaped = false;
145
- for (let i = 0; i < content.length; i++) {
146
- let char = content[i];
147
- if (char === void 0)
148
- break;
149
- if (isEscaped) {
150
- current += char, isEscaped = false;
151
- continue;
152
- }
153
- if (char === "\\") {
154
- isEscaped = true;
155
- continue;
156
- }
157
- quoteChar ? char === quoteChar ? quoteChar = null : current += char : char === '"' ? quoteChar = char : /\s/.test(char) ? current.length > 0 && (args.push(current), current = "") : current += char;
158
- }
159
- return current.length > 0 && args.push(current), args;
160
- }
161
- function extractSnowflakeId(input) {
162
- if (!input) return null;
163
- let mentionMatch = /^<@!?(\d{17,20})>$/.exec(input);
164
- if (mentionMatch?.[1])
165
- return mentionMatch[1];
166
- let idMatch = /^(\d{17,20})$/.exec(input);
167
- return idMatch?.[1] ? idMatch[1] : null;
168
- }
169
- var CONFIG_EXTENSIONS = ["ts", "js"], ProjectConfigSchema = z2.object({
170
- /**
171
- * The gateway intents to use for the Discord client.
172
- *
173
- * - `auto` — automatically determine the required intents.
174
- * - bigint — a raw bitfield value representing the combined intents.
175
- * - array — a list of individual intent flags from `GatewayIntentBits`.
176
- *
177
- * @defaultvalue `auto`
178
- */
179
- intents: z2.union([z2.literal("auto"), z2.bigint(), z2.array(z2.enum(GatewayIntentBits))]).default("auto"),
180
- /**
181
- * Optional custom client options for Discord.js (excluding `intents`).
182
- *
183
- * These are passed directly to the `Client` constructor when initializing the bot.
184
- *
185
- * @see {@link https://discord.js.org/docs/packages/discord.js/main/ClientOptions:Interface}
186
- */
187
- clientOptions: z2.custom().optional(),
188
- /**
189
- * Your bot prefixes to trigger the commands.
190
- */
191
- prefixes: z2.array(z2.string()).default([]),
192
- /**
193
- * Your Discord bot token.
194
- */
195
- token: z2.string(),
196
- /**
197
- * The main source directory.
198
- * @defaultvalue `src`
199
- */
200
- entryDirectory: z2.string().default("src")
201
- });
202
- function defineConfig(config) {
203
- return config;
204
- }
205
- var _config;
206
- async function loadConfig(cwd = process.cwd()) {
207
- if (_config)
208
- return console.warn("loadConfig() was called more than once. This shouldn't happen."), _config;
209
- let globPattern = `bakit.config.{${CONFIG_EXTENSIONS.join(",")}}`, [configPath, other] = await glob(globPattern, {
210
- cwd: cwd.replace(/\\/g, "/"),
211
- // ensure the path uses `/` instead of `\` on Windows
212
- absolute: true
213
- });
214
- if (!configPath)
215
- throw new Error("Missing config file");
216
- other && console.warn(`Multiple config files found in ${cwd}. Using ${configPath}.`);
217
- let config = (await import(pathToFileURL(configPath).href)).default;
218
- return _config = Object.freeze(await ProjectConfigSchema.parseAsync(config)), _config;
219
- }
220
- function getConfig() {
221
- if (!_config)
222
- throw new Error("Project config is not loaded.");
223
- return _config;
224
- }
225
- function getTopLevelDirectory(path, entryDir) {
226
- return relative(entryDir, path).split(sep)[0] ?? null;
227
- }
228
- function getEntryDirectory() {
229
- return resolve(getConfig().entryDirectory);
230
- }
231
- function getEntryFile() {
232
- return process.env.BAKIT_ENTRY_FILE;
233
- }
234
-
235
- // src/lib/loader/loader.ts
236
- var loader_exports = {};
237
- __export(loader_exports, {
238
- addHotReloader: () => addHotReloader,
239
- containsEntryFile: () => containsEntryFile,
240
- containsHotModule: () => containsHotModule,
241
- getDependencyChain: () => getDependencyChain,
242
- getImporters: () => getImporters,
243
- getImports: () => getImports,
244
- hotReloaders: () => hotReloaders,
245
- importsAny: () => importsAny,
246
- init: () => init,
247
- isEntryFile: () => isEntryFile,
248
- isImported: () => isImported,
249
- isImportedBy: () => isImportedBy,
250
- isInHotDirectory: () => isInHotDirectory,
251
- restartProcess: () => restartProcess,
252
- unload: () => unload
253
- });
254
- var RPC_RESPONSE_MARK = "$DONE:", RPC_RESPONSE_TIMEOUT = 5e3, RPC = class extends EventEmitter {
255
- constructor(transport) {
256
- super();
257
- this.transport = transport;
258
- this.transport.on("message", (message) => this.onMessage(message));
259
- }
260
- requests = /* @__PURE__ */ new Map();
261
- postMessage(message) {
262
- let { transport } = this;
263
- "send" in transport ? transport.send(message) : "postMessage" in transport ? transport.postMessage(message) : console.warn(`${transport.constructor.name} doesn't support IPC`);
264
- }
265
- onMessage(message) {
266
- if (message.id.startsWith(RPC_RESPONSE_MARK)) {
267
- this.handleResponseMessage(message);
268
- return;
269
- }
270
- if ("type" in message) {
271
- this.handleRequestMessage(message);
272
- return;
273
- }
274
- }
275
- handleResponseMessage(message) {
276
- let id = message.id.slice(RPC_RESPONSE_MARK.length), request = this.requests.get(id);
277
- if (!request)
278
- return;
279
- let { reject, resolve: resolve6, timeout } = request;
280
- this.requests.delete(id), clearTimeout(timeout), "data" in message ? resolve6(message.data) : reject(new Error(message.error));
281
- }
282
- handleRequestMessage(message) {
283
- this.emit("message", message), this.emit(message.type, message.id, message.data);
284
- }
285
- send(type, data = {}, id = randomUUID()) {
286
- let message = {
287
- id,
288
- type,
289
- data
290
- };
291
- this.postMessage(message);
292
- }
293
- success(id, data) {
294
- let message = {
295
- id: `${RPC_RESPONSE_MARK}${id}`,
296
- data
297
- };
298
- this.postMessage(message);
299
- }
300
- error(id, error) {
301
- let message = {
302
- id: `${RPC_RESPONSE_MARK}${id}`,
303
- error
304
- };
305
- this.postMessage(message);
306
- }
307
- request(type, data, id = randomUUID()) {
308
- return new Promise((resolve6, reject) => {
309
- let timeout = setTimeout(() => {
310
- this.requests.delete(id) && reject(new Error("Request timed out"));
311
- }, RPC_RESPONSE_TIMEOUT);
312
- this.requests.set(id, {
313
- resolve: resolve6,
314
- reject,
315
- timeout
316
- }), this.send(type, data, id);
317
- });
318
- }
319
- };
320
-
321
- // src/lib/loader/loader.ts
322
- var hooksRPC, processRPC, hotReloaders = new Collection(), reverseDependencyGraph = new Collection(), forwardDependencyGraph = new Collection();
323
- function init() {
324
- initProcess(), initHooks();
325
- }
326
- function initProcess() {
327
- processRPC = new RPC(process), processRPC.on("fileChange", (_, path) => onFileChange(path)), processRPC.on("fileRemove", (_, path) => onFileRemove(path));
328
- }
329
- function initHooks() {
330
- let { port1, port2 } = new MessageChannel(), hookPath = new URL("./hooks.js", import.meta.url).href;
331
- register(hookPath, import.meta.url, {
332
- data: { port: port1 },
333
- transferList: [port1]
334
- }), hooksRPC = new RPC(port2), hooksRPC.on("dependencyAdd", (_, data) => onDependencyAdd(data)), port2.unref();
335
- }
336
- function addHotReloader(reloader) {
337
- hotReloaders.set(reloader.entryDirectory, reloader);
338
- }
339
- function unload(path) {
340
- if (!hooksRPC)
341
- throw new Error("Loader isn't initialized");
342
- return hooksRPC.request("unload", resolve(path));
343
- }
344
- function getImporters(path, createNew = false) {
345
- path = resolve(path);
346
- let entry = reverseDependencyGraph.get(path);
347
- return createNew && !entry && (entry = /* @__PURE__ */ new Set(), reverseDependencyGraph.set(path, entry)), entry;
348
- }
349
- function getImports(path) {
350
- path = resolve(path);
351
- let imports = [];
352
- for (let [target, importers] of reverseDependencyGraph)
353
- importers.has(path) && imports.push(target);
354
- return imports;
355
- }
356
- function getDependencyChain(path) {
357
- path = resolve(path);
358
- let queue = [path], visited = /* @__PURE__ */ new Set();
359
- for (; queue.length > 0; ) {
360
- let current = queue.shift();
361
- if (!current || visited.has(current) || (visited.add(current), current.includes("/node_modules/")))
362
- continue;
363
- let parents = getImporters(current);
364
- if (!parents)
365
- continue;
366
- for (let parent of parents)
367
- visited.has(parent) || queue.push(parent);
368
- let children = getImports(current);
369
- for (let child of children)
370
- visited.has(child) || importsAny(child, visited) && queue.push(child);
371
- }
372
- return Array.from(visited);
373
- }
374
- function importsAny(path, targets) {
375
- return getImports(path).some((imp) => targets.has(imp));
376
- }
377
- function isImported(path) {
378
- return !!getImporters(path)?.size;
379
- }
380
- function isImportedBy(path, matcher) {
381
- return getDependencyChain(path).slice(1).some((p) => {
382
- let isMatch = false;
383
- return typeof matcher == "string" && (isMatch = resolve(matcher) === p), typeof matcher == "function" && (isMatch = matcher(p)), matcher instanceof RegExp && (isMatch = matcher.test(p)), isMatch;
384
- });
385
- }
386
- function onDependencyAdd(data) {
387
- let { url, parentURL } = data, path = fileURLToPath(url), parentPath = fileURLToPath(parentURL), reverseEntry = reverseDependencyGraph.get(path);
388
- reverseEntry || (reverseEntry = /* @__PURE__ */ new Set(), reverseDependencyGraph.set(path, reverseEntry)), reverseEntry.add(parentPath);
389
- let forwardEntry = forwardDependencyGraph.get(parentPath);
390
- forwardEntry || (forwardEntry = /* @__PURE__ */ new Set(), forwardDependencyGraph.set(parentPath, forwardEntry)), forwardEntry.add(path);
391
- }
392
- function isInHotDirectory(path) {
393
- let sourceRoot = getEntryDirectory();
394
- if (!path.startsWith(sourceRoot))
395
- return false;
396
- let topLevelDir = getTopLevelDirectory(path, sourceRoot);
397
- if (!topLevelDir)
398
- return;
399
- let entryDirectory = join(sourceRoot, topLevelDir);
400
- return hotReloaders.some((m) => m.entryDirectory === entryDirectory);
401
- }
402
- function isEntryFile(path) {
403
- return path === getEntryFile();
404
- }
405
- function containsEntryFile(chain) {
406
- return chain.some((x) => isEntryFile(x));
407
- }
408
- function containsHotModule(chain) {
409
- return chain.some((x) => isInHotDirectory(x));
410
- }
411
- function restartProcess() {
412
- processRPC?.send("restart");
413
- }
414
- async function unloadModule(path, reload = false) {
415
- if (path.includes("/node_modules/"))
416
- return;
417
- let topLevel = getTopLevelDirectory(path, getEntryDirectory());
418
- if (!topLevel)
419
- return;
420
- let directory = resolve(getEntryDirectory(), topLevel), reloader = hotReloaders.get(directory);
421
- if (!reloader) {
422
- await unload(path);
423
- return;
424
- }
425
- reloader[reload ? "reload" : "unload"](path);
426
- }
427
- async function onFileRemove(path) {
428
- if (isEntryFile(path)) {
429
- restartProcess();
430
- return;
431
- }
432
- if (!isImported(path))
433
- return;
434
- let chain = getDependencyChain(path);
435
- if (containsEntryFile(chain)) {
436
- restartProcess();
437
- return;
438
- }
439
- if (containsHotModule(chain))
440
- for (let path2 of chain.reverse())
441
- await unloadModule(path2);
442
- }
443
- async function onFileChange(path) {
444
- if (isEntryFile(path)) {
445
- restartProcess();
446
- return;
447
- }
448
- if (!isImported(path))
449
- return;
450
- let chain = getDependencyChain(path);
451
- if (containsEntryFile(chain)) {
452
- restartProcess();
453
- return;
454
- }
455
- if (containsHotModule(chain))
456
- for (let path2 of chain.toReversed())
457
- await unloadModule(path2, true);
458
- }
459
-
460
- // src/core/structures/param/Param.ts
461
- var BaseParam = class {
462
- constructor(options, schema) {
463
- this.schema = schema;
464
- let parsed = schema.parse({
465
- ...options,
466
- description: options.description ?? options.name
467
- });
468
- this.options = parsed;
469
- }
470
- options;
471
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
472
- setOption(key, value) {
473
- if (value === null)
474
- return delete this.options[key], this;
475
- let fieldValidator = this.schema.shape[key];
476
- if (!fieldValidator)
477
- return this.options[key] = value, this;
478
- let parsedValue = fieldValidator.parse(value);
479
- return this.options[key] = parsedValue, this;
480
- }
481
- name(value) {
482
- return this.setOption("name", value);
483
- }
484
- description(value) {
485
- return this.setOption("description", value);
486
- }
487
- required(value) {
488
- return this.setOption("required", value);
489
- }
490
- async resolve(context, value) {
491
- if (context.isChatInput())
492
- return await this.resolveChatInput(context);
493
- if (context.isMessage()) {
494
- let { required, name } = this.options;
495
- if (value === void 0) {
496
- if (required)
497
- throw new ArgumentError(name, "is required");
498
- return null;
499
- }
500
- return await this.resolveMessage(context, value);
501
- }
502
- throw new Error("Invalid context type provided");
503
- }
504
- /**
505
- * Helper to normalize string inputs into an options object.
506
- */
507
- static getOptions(options) {
508
- return typeof options == "string" ? { name: options } : options;
509
- }
510
- }, StringParam = class extends BaseParam {
511
- constructor(options) {
512
- super(BaseParam.getOptions(options), StringParamSchema);
513
- }
514
- required(value) {
515
- return super.required(value);
516
- }
517
- resolveMessage(_context, value) {
518
- let { minLength, maxLength, name } = this.options;
519
- if (minLength && value.length < minLength)
520
- throw new ArgumentError(name, `must be at least ${minLength} chars long`);
521
- if (maxLength && value.length > maxLength)
522
- throw new ArgumentError(name, `must be at most ${maxLength} chars long`);
523
- return value;
524
- }
525
- resolveChatInput(context) {
526
- let { name, required } = this.options;
527
- return context.source.options.getString(name, required);
528
- }
529
- /**
530
- * Sets the minimum allowed length for this string.
531
- * Pass `null` to remove this constraint.
532
- */
533
- min(length) {
534
- return this.setOption("minLength", length);
535
- }
536
- /**
537
- * Sets the maximum allowed length for this string.
538
- * Pass `null` to remove this constraint.
539
- */
540
- max(length) {
541
- return this.setOption("maxLength", length);
542
- }
543
- }, NumberParam = class extends BaseParam {
544
- constructor(options) {
545
- super(BaseParam.getOptions(options), NumberParamSchema);
546
- }
547
- required(value) {
548
- return super.required(value);
549
- }
550
- resolveMessage(_context, value) {
551
- let { minValue, maxValue, name } = this.options, num = Number(value);
552
- if (isNaN(num))
553
- throw new ArgumentError(name, "must be a number");
554
- if (minValue !== void 0 && num < minValue)
555
- throw new ArgumentError(name, `must be greater than ${minValue}`);
556
- if (maxValue !== void 0 && num > maxValue)
557
- throw new ArgumentError(name, `must be less than ${minValue}`);
558
- return num;
559
- }
560
- resolveChatInput(context) {
561
- let { name, required } = this.options;
562
- return context.source.options.getString(name, required);
563
- }
564
- /**
565
- * Sets the minimum allowed value for this number.
566
- * Pass `null` to remove this constraint.
567
- */
568
- min(value) {
569
- return this.setOption("minValue", value);
570
- }
571
- /**
572
- * Sets the maximum allowed value for this number.
573
- * Pass `null` to remove this constraint.
574
- */
575
- max(value) {
576
- return this.setOption("maxValue", value);
577
- }
578
- }, UserParam = class extends BaseParam {
579
- constructor(options) {
580
- super(BaseParam.getOptions(options), UserParamSchema);
581
- }
582
- required(value) {
583
- return super.required(value);
584
- }
585
- async resolveMessage(context, value) {
586
- let id = extractSnowflakeId(value);
587
- if (!id)
588
- return null;
589
- let { users } = context.client;
590
- return await users.fetch(id).catch(() => null);
591
- }
592
- resolveChatInput(context) {
593
- let { name, required } = this.options;
594
- return context.source.options.getUser(name, required);
595
- }
596
- };
597
- var HookState = /* @__PURE__ */ ((HookState2) => (HookState2.Pre = "PRE", HookState2.Main = "MAIN", HookState2.Post = "POST", HookState2.Error = "ERROR", HookState2))(HookState || {}), HookOrder = /* @__PURE__ */ ((HookOrder2) => (HookOrder2[HookOrder2.First = 0] = "First", HookOrder2[HookOrder2.Last = 1] = "Last", HookOrder2))(HookOrder || {}), LifecycleManager = class {
598
- constructor(id) {
599
- this.id = id;
600
- }
601
- // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
602
- hooks = {
603
- MAIN: new Collection(),
604
- PRE: new Collection(),
605
- POST: new Collection(),
606
- ERROR: new Collection()
607
- };
608
- getName(name) {
609
- return `${this.id}:${name}`;
610
- }
611
- // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
612
- setHook(name, state, callback, order = 1 /* Last */) {
613
- let currentHooks = this.hooks[state], key = this.getName(name);
614
- if (currentHooks.has(key) && console.warn(`Overriding duplicate hook '${key}' for state '${state}'`), order === 1 /* Last */)
615
- currentHooks.set(key, callback);
616
- else {
617
- let existingEntries = [...currentHooks.entries()].filter(([k]) => k !== key);
618
- currentHooks.clear(), currentHooks.set(key, callback);
619
- for (let [k, v] of existingEntries)
620
- currentHooks.set(k, v);
621
- }
622
- return this;
623
- }
624
- main(callback) {
625
- return this.setHook("main", "MAIN" /* Main */, callback);
626
- }
627
- pre(callback) {
628
- return this.setHook("pre", "PRE" /* Pre */, callback);
629
- }
630
- post(callback) {
631
- return this.setHook("post", "POST" /* Post */, callback);
632
- }
633
- error(callback) {
634
- return this.setHook("error", "ERROR" /* Error */, callback);
635
- }
636
- async execute(context, ...args) {
637
- let pipeline = [
638
- ...this.hooks.PRE.values(),
639
- ...this.hooks.MAIN.values(),
640
- ...this.hooks.POST.values()
641
- ], error;
642
- for (let hook of pipeline) {
643
- if (context.canceled)
644
- break;
645
- try {
646
- await hook(context, ...args);
647
- } catch (e) {
648
- error = e;
649
- break;
650
- }
651
- }
652
- if (!error)
653
- return;
654
- if (!this.hooks.ERROR.size)
655
- throw error;
656
- for (let [key, callback] of this.hooks.ERROR.entries()) {
657
- if (context.canceled)
658
- break;
659
- try {
660
- await callback(context, error, ...args);
661
- } catch (innerError) {
662
- console.error(`[Lifecycle] Error handler for '${key}' failed:`, innerError);
663
- }
664
- }
665
- }
666
- };
667
-
668
- // src/core/structures/Command.ts
669
- function validateParamsOrder(params) {
670
- let seenOptional = false;
671
- for (let param of params)
672
- if (param.options.required) {
673
- if (seenOptional)
674
- return false;
675
- } else
676
- seenOptional = true;
677
- return true;
678
- }
679
- var CommandOptionsSchema = z2.object({
680
- name: z2.string().readonly(),
681
- description: z2.string().min(1).max(100).optional().readonly(),
682
- nsfw: z2.boolean().default(false).readonly(),
683
- params: z2.array(z2.instanceof(BaseParam)).default([]).readonly(),
684
- quotes: z2.boolean().default(true).readonly()
685
- }).transform((data) => ({
686
- ...data,
687
- description: data.description ?? `Command ${data.name}`
688
- })).refine(({ params }) => validateParamsOrder(params), {
689
- path: ["params"],
690
- error: "Required params must be placed before optional params"
691
- }), Command = class extends LifecycleManager {
692
- constructor(options) {
693
- let _options = CommandOptionsSchema.parse(typeof options == "string" ? { name: options } : options);
694
- super(`command:${_options.name}`), this.options = _options, this.setHook("syntaxError", "ERROR" /* Error */, async (ctx, error, ...args) => {
695
- await this.handleSyntaxError(ctx, error, args);
696
- });
697
- }
698
- async handleSyntaxError(context, error, _args) {
699
- error instanceof BakitError && await context.send(error.message);
700
- }
701
- toSlashCommandJSON() {
702
- let { name, description, nsfw, params } = this.options, builder = new SlashCommandBuilder().setName(name).setDescription(description).setNSFW(nsfw);
703
- return this.initSlashCommandOptions(builder, params), builder.toJSON();
704
- }
705
- initSlashCommandOptions(builder, params) {
706
- for (let param of params)
707
- this.initSlashCommandOption(builder, param);
708
- }
709
- initSlashCommandOption(builder, param) {
710
- let initOption = (builder2) => {
711
- let { name, description, required } = param.options;
712
- return builder2.setName(name).setDescription(description).setRequired(required);
713
- };
714
- if (param instanceof StringParam) {
715
- let { maxLength, minLength } = param.options, option = initOption(new SlashCommandStringOption());
716
- maxLength && option.setMaxLength(maxLength), minLength && option.setMinLength(minLength), builder.addStringOption(option);
717
- return;
718
- }
719
- if (param instanceof NumberParam) {
720
- let { maxValue, minValue } = param.options, option = initOption(new SlashCommandNumberOption());
721
- maxValue && option.setMaxValue(maxValue), minValue && option.setMinValue(minValue), builder.addNumberOption(option);
722
- return;
723
- }
724
- if (param instanceof UserParam) {
725
- let option = initOption(new SlashCommandUserOption());
726
- builder.addUserOption(option);
727
- return;
728
- }
729
- }
730
- };
731
- function defineCommand(options) {
732
- return new Command(options);
733
- }
734
- var HotReloadable2 = class {
735
- constructor(entryDirectory) {
736
- this.entryDirectory = entryDirectory;
737
- entryDirectory = resolve(entryDirectory);
738
- }
739
- unloadFile(path) {
740
- return loader_exports.unload(path);
741
- }
742
- };
743
-
744
- // src/core/managers/CommandManager.ts
745
- var CommandManager = class extends HotReloadable2 {
746
- constructor(client) {
747
- super(join(getEntryDirectory(), "commands"));
748
- this.client = client;
749
- }
750
- commands = new Collection();
751
- entries = new Collection();
752
- async loadModules() {
753
- let pattern = join(this.entryDirectory, "**/*.{ts,js}"), files = await glob(pattern, { cwd: process.cwd(), absolute: true }), filtered = (await Promise.all(files.map((file) => this.load(file)))).filter((c) => !!c);
754
- return console.log(`[Loader] Loaded ${filtered.length}/${files.length} command(s)`), filtered;
755
- }
756
- /**
757
- * Load the file and add the command to the registry.
758
- * @param path The path to the command file.
759
- * @returns The command object if added successfully.
760
- */
761
- async load(path) {
762
- path = resolve(path);
763
- let command = (await import(pathToFileURL(path).href)).default;
764
- if (!command) {
765
- console.warn(`[Loader] File has no default export: ${path}`);
766
- return;
767
- }
768
- if (!(command instanceof Command)) {
769
- console.warn(`[Loader] Default export is not a Command: ${path}`);
770
- return;
771
- }
772
- return this.add(command), this.entries.set(path, command), command;
773
- }
774
- /**
775
- * Unload the file and remove the command from the registry.
776
- * @param path The path to the command file.
777
- * @returns The command object if unloaded successfully.
778
- */
779
- async unload(path) {
780
- path = resolve(path);
781
- let command = this.entries.get(path);
782
- if (this.entries.delete(path), await this.unloadFile(path), !!command)
783
- return this.remove(command);
784
- }
785
- async reload(path) {
786
- path = resolve(path), await this.unload(path);
787
- let command = await this.load(path);
788
- if (command)
789
- return console.log(`[Loader] Reloaded command '${command.options.name}' at '${path}'`), command;
790
- }
791
- /**
792
- * Add a command to the registry.
793
- * @param command Command to add.
794
- */
795
- add(command) {
796
- if (!(command instanceof Command))
797
- throw new Error("Invalid command provided");
798
- let { name } = command.options;
799
- if (this.commands.has(name)) {
800
- console.warn(`[Loader] Duplicate command registered: '${name}'`);
801
- return;
802
- }
803
- this.commands.set(name, command);
804
- }
805
- /**
806
- * Remove a command from the registry.
807
- * @param target Command name or object to remove.
808
- * @returns The command object if removed successfully.
809
- */
810
- remove(target) {
811
- if (typeof target != "string" && !(target instanceof Command))
812
- return;
813
- let name = typeof target == "string" ? target : target.options.name, existing = this.commands.get(name);
814
- if (existing)
815
- return this.commands.delete(name), existing;
816
- }
817
- /**
818
- * Get a command using its name.
819
- * @param name The command to get.
820
- * @returns The command object.
821
- */
822
- get(name) {
823
- return this.commands.get(name);
824
- }
825
- };
826
- var ListenerOptionsSchema = z2.object({
827
- name: z2.enum(Events),
828
- once: z2.boolean().default(false)
829
- }), Listener = class extends LifecycleManager {
830
- options;
831
- constructor(options) {
832
- let _options = ListenerOptionsSchema.parse(typeof options == "string" ? { name: options } : options);
833
- super(`listener:${_options.name}`), this.options = _options;
834
- }
835
- };
836
- function defineListener(options) {
837
- return new Listener(options);
838
- }
839
-
840
- // src/core/context/Context.ts
841
- var Context = class {
842
- canceled = false;
843
- cancel() {
844
- this.canceled = true;
845
- }
846
- };
847
-
848
- // src/core/managers/ListenerManager.ts
849
- var ListenerManager = class extends HotReloadable2 {
850
- constructor(client) {
851
- super(join(getEntryDirectory(), "listeners"));
852
- this.client = client;
853
- }
854
- listeners = [];
855
- entries = new Collection();
856
- executors = /* @__PURE__ */ new WeakMap();
857
- async loadModules() {
858
- let pattern = join(this.entryDirectory, "**/*.{ts,js}"), files = await glob(pattern, { cwd: process.cwd(), absolute: true }), filtered = (await Promise.all(files.map((file) => this.load(file)))).filter((l) => !!l);
859
- return console.log(`[Loader] Loaded ${filtered.length}/${files.length} listener(s)`), filtered;
860
- }
861
- /**
862
- * Load the file and add the listener to the registry.
863
- * @param path The path to the listener file.
864
- * @returns The listener object if added successfully.
865
- */
866
- async load(path) {
867
- path = resolve(path);
868
- let listener = (await import(pathToFileURL(path).href)).default;
869
- if (!listener) {
870
- console.warn(`[Loader] File has no default export: ${path}`);
871
- return;
872
- }
873
- if (!(listener instanceof Listener)) {
874
- console.warn(`[Loader] Default export is not a Listener: ${path}`);
875
- return;
876
- }
877
- return this.add(listener), this.entries.set(path, listener), listener;
878
- }
879
- /**
880
- * Unload the file and remove the listener from the registry.
881
- * @param path The path to the listener file.
882
- * @returns The listener object if unloaded successfully.
883
- */
884
- async unload(path) {
885
- path = resolve(path);
886
- let listener = this.entries.get(path);
887
- if (this.entries.delete(path), await this.unloadFile(path), !!listener)
888
- return this.remove(listener)?.[0];
889
- }
890
- async reload(path) {
891
- path = resolve(path), await this.unload(path);
892
- let listener = await this.load(path);
893
- if (listener)
894
- return console.log(`[Loader] Reloaded listener '${listener.options.name}' at '${path}'`), listener;
895
- }
896
- /**
897
- * Add a listener to the registry and create a listener for client.
898
- * @param listener Listener to add.
899
- */
900
- add(listener) {
901
- if (!(listener instanceof Listener))
902
- throw new Error("Invalid listener provided");
903
- let { once, name } = listener.options, execute = (...args) => {
904
- listener.execute(new Context(), ...args);
905
- };
906
- this.listeners.push(listener), this.executors.set(listener, execute), this.client[once ? "once" : "on"](name, execute);
907
- }
908
- /**
909
- * Remove a listener from the registry and client.
910
- * @param target Listener name or object to remove.
911
- * @returns The list of listener objects if removed successfully.
912
- */
913
- remove(target) {
914
- let isMatched = (listener) => typeof target == "string" ? listener.options.name === target : listener === target, removed = [];
915
- return this.listeners = this.listeners.filter((listener) => {
916
- if (!isMatched(listener))
917
- return true;
918
- removed.push(listener);
919
- let execute = this.executors.get(listener);
920
- return execute && (this.client.removeListener(listener.options.name, execute), this.executors.delete(listener)), false;
921
- }), removed;
922
- }
923
- /**
924
- * Get a list of required intents for Bakit to run correctly.
925
- * @returns Used intents.
926
- */
927
- getBaseIntents() {
928
- return new IntentsBitField([GatewayIntentBits.Guilds]);
929
- }
930
- /**
931
- * Get a list of needed intents based on registered listeners to receive needed events.
932
- * @returns Used intents.
933
- */
934
- getNeededIntents() {
935
- let result = this.getBaseIntents();
936
- for (let listener of this.listeners) {
937
- let eventName = listener.options.name, requiredIntents = EVENT_INTENT_MAPPING[eventName];
938
- requiredIntents && result.add(requiredIntents);
939
- }
940
- return result;
941
- }
942
- };
943
-
944
- // src/core/client/BakitClient.ts
945
- var BakitClient = class extends Client {
946
- constructor(options, instance) {
947
- super(options);
948
- this.instance = instance;
949
- this.managers = {
950
- commands: new CommandManager(this),
951
- listeners: new ListenerManager(this)
952
- };
953
- }
954
- managers;
955
- /**
956
- * Check if the client is connected to gateway successfully and finished initialization.
957
- */
958
- isReady() {
959
- return super.isReady();
960
- }
961
- on(event, listener) {
962
- return super.on(event, listener);
963
- }
964
- once(event, listener) {
965
- return super.once(event, listener);
966
- }
967
- off(event, listener) {
968
- return super.off(event, listener);
969
- }
970
- removeAllListeners(event) {
971
- return super.removeAllListeners(event);
972
- }
973
- removeListener(event, listener) {
974
- return super.removeListener(event, listener);
975
- }
976
- emit(event, ...args) {
977
- return super.emit(event, ...args);
978
- }
979
- /**
980
- * Override BakitClient output when using logger for security concern.
981
- * @returns `BakitClient {}`
982
- */
983
- [inspect.custom]() {
984
- return `${this.constructor.name} {}`;
985
- }
986
- };
987
-
988
- // src/core/managers/BaseClientManager.ts
989
- var BaseClientManager = class {
990
- constructor(client) {
991
- this.client = client;
992
- }
993
- };
994
- var BaseCommandContext = class extends Context {
995
- constructor(source) {
996
- super();
997
- this.source = source;
998
- }
999
- get client() {
1000
- return this.source.client;
1001
- }
1002
- get channel() {
1003
- return this.source.channel;
1004
- }
1005
- get channelId() {
1006
- return this.source.channelId;
1007
- }
1008
- get guild() {
1009
- return this.source.guild;
1010
- }
1011
- get guildId() {
1012
- return this.source.guildId;
1013
- }
1014
- get member() {
1015
- return this.source.member;
1016
- }
1017
- inGuild() {
1018
- return !!this.guildId;
1019
- }
1020
- inCachedGuild() {
1021
- if (this.isChatInput())
1022
- return this.source.inCachedGuild();
1023
- if (this.isMessage())
1024
- return this.source.inGuild();
1025
- throw new Error("Invalid source");
1026
- }
1027
- get user() {
1028
- if (this.isChatInput())
1029
- return this.source.user;
1030
- if (this.isMessage())
1031
- return this.source.author;
1032
- throw new Error("Invalid source");
1033
- }
1034
- isChatInput() {
1035
- return this.source instanceof ChatInputCommandInteraction;
1036
- }
1037
- isMessage() {
1038
- return this.source instanceof Message;
1039
- }
1040
- }, ChatInputContext = class extends BaseCommandContext {
1041
- async send(options) {
1042
- typeof options == "string" && (options = { content: options });
1043
- let sendOptions = {
1044
- ...options,
1045
- withResponse: true
1046
- };
1047
- return this.source.deferred || this.source.replied ? await this.source.followUp(sendOptions) : (await this.source.reply(sendOptions)).resource?.message;
1048
- }
1049
- }, MessageContext = class extends BaseCommandContext {
1050
- async send(options) {
1051
- let { channel } = this;
1052
- if (!channel?.isSendable())
1053
- throw new Error("Invalid channel or channel is not sendable");
1054
- return await channel.send(options);
1055
- }
1056
- };
1057
- var ProjectCacheManager = class {
1058
- rootDir;
1059
- constructor(root = process.cwd()) {
1060
- this.rootDir = join(root, ".bakit"), this.ensureRoot();
1061
- }
1062
- ensureRoot() {
1063
- existsSync(this.rootDir) || mkdirSync(this.rootDir, { recursive: true });
1064
- }
1065
- getHash(data) {
1066
- return createHash("sha256").update(JSON.stringify(data)).digest("hex");
1067
- }
1068
- async write(path, data) {
1069
- let fullPath = join(this.rootDir, path), dir = dirname(fullPath);
1070
- await mkdir(dir, { recursive: true });
1071
- let content = typeof data == "string" ? data : JSON.stringify(data);
1072
- await writeFile(fullPath, content, "utf-8");
1073
- }
1074
- async read(path) {
1075
- let fullPath = join(this.rootDir, path);
1076
- try {
1077
- let content = await readFile(fullPath, "utf-8");
1078
- return JSON.parse(content);
1079
- } catch {
1080
- return null;
1081
- }
1082
- }
1083
- async clear() {
1084
- await rm(this.rootDir, { recursive: true, force: true });
1085
- }
1086
- clearSync() {
1087
- existsSync(this.rootDir) && rmSync(this.rootDir, { recursive: true, force: true });
1088
- }
1089
- };
1090
- var messageCommandHandler = defineListener(Events.MessageCreate), chatInputCommandHandler = defineListener(Events.InteractionCreate), registerCommandsHandler = defineListener({
1091
- name: Events.ClientReady,
1092
- once: true
1093
- });
1094
- registerCommandsHandler.main(async (_, client) => {
1095
- let { managers, instance } = client, { commands } = managers, { cache } = instance, payload = commands.commands.map((cmd) => cmd.toSlashCommandJSON()).sort((a, b) => a.name.localeCompare(b.name)), currentHash = cache.getHash(payload), CACHE_KEY = "commands/meta.json", cachedMeta = await cache.read(CACHE_KEY);
1096
- if (cachedMeta && cachedMeta.hash === currentHash) {
1097
- let { timestamp, count } = cachedMeta, time = new Date(timestamp).toLocaleString();
1098
- console.log(`${count} command(s) are up to date (Last sync: ${time}). Skipping registration.`);
1099
- return;
1100
- }
1101
- try {
1102
- let result = await client.application.commands.set(payload);
1103
- cache.write(CACHE_KEY, {
1104
- hash: currentHash,
1105
- timestamp: Date.now(),
1106
- count: result.size
1107
- }), cache.write("commands/debug_dump.json", payload), console.log(`Registered ${result.size} application command(s).`);
1108
- } catch (error) {
1109
- console.error("Failed to register commands:", error);
1110
- }
1111
- });
1112
- messageCommandHandler.main(async (_, message) => {
1113
- let config = getConfig();
1114
- if (message.author.bot)
1115
- return;
1116
- let { content } = message, client = message.client, lowerContent = content.toLowerCase(), prefix = config.prefixes.find((p) => lowerContent.startsWith(p));
1117
- if (!prefix)
1118
- return;
1119
- let [name, ...args] = content.slice(prefix.length).trim().split(/\s+/g);
1120
- if (!name)
1121
- return;
1122
- let command = client.managers.commands.get(name);
1123
- if (!command)
1124
- return;
1125
- let context = new MessageContext(message), { params, quotes } = command.options, rawArgs = quotes ? tokenize(args.join(" ")) : args, resolvedArgs = [];
1126
- for (let i = 0; i < params.length; i++) {
1127
- let param = params[i], arg = rawArgs[i];
1128
- if (!param)
1129
- break;
1130
- let resolved = await param.resolve(context, arg);
1131
- resolvedArgs.push(resolved);
1132
- }
1133
- await command.execute(context, ...resolvedArgs);
1134
- });
1135
- chatInputCommandHandler.main(async (_, interaction) => {
1136
- if (!interaction.isChatInputCommand())
1137
- return;
1138
- let { commandName } = interaction, command = interaction.client.managers.commands.get(commandName);
1139
- if (!command)
1140
- return;
1141
- let context = new ChatInputContext(interaction), { params } = command.options, resolvedArgs = [];
1142
- for (let param of params) {
1143
- let resolved = await param.resolve(context);
1144
- resolvedArgs.push(resolved);
1145
- }
1146
- await command.execute(context, ...resolvedArgs);
1147
- });
1148
-
1149
- // src/core/internal/Instance.ts
1150
- var Instance = class {
1151
- client;
1152
- cache;
1153
- constructor() {
1154
- this.cache = new ProjectCacheManager();
1155
- }
1156
- async start() {
1157
- await loadConfig();
1158
- let config = getConfig();
1159
- this.client = new BakitClient(
1160
- {
1161
- intents: [],
1162
- ...config.clientOptions
1163
- },
1164
- this
1165
- ), await this.loadModules(), this.initIntents(), await this.client.login(config.token), this.initProcess();
1166
- }
1167
- initProcess() {
1168
- process.on("SIGINT", () => this.shutdown()), process.on("SIGTERM", () => this.shutdown());
1169
- }
1170
- loadModules() {
1171
- let { managers } = this.client, { commands, listeners } = managers;
1172
- return listeners.add(chatInputCommandHandler), listeners.add(messageCommandHandler), listeners.add(registerCommandsHandler), addHotReloader(commands), addHotReloader(listeners), Promise.all([commands.loadModules(), listeners.loadModules()]);
1173
- }
1174
- initIntents() {
1175
- let config = getConfig(), { options, managers } = this.client, { listeners } = managers, intents;
1176
- config.intents === "auto" ? intents = listeners.getNeededIntents() : (intents = listeners.getBaseIntents(), typeof config.intents == "bigint" ? intents.bitfield = Number(config.intents) : intents.add(...config.intents)), options.intents = intents;
1177
- }
1178
- async shutdown() {
1179
- this.client && await this.client.destroy().catch(() => null), process.exit(0);
1180
- }
1181
- };
1182
- function useApp() {
1183
- return new Instance();
1184
- }
1185
-
1186
- // src/core/structures/param/Params.ts
1187
- function createFactory(ctor) {
1188
- return (...args) => new ctor(...args);
1189
- }
1190
- var Params = {
1191
- string: createFactory(StringParam),
1192
- number: createFactory(NumberParam),
1193
- user: createFactory(UserParam)
1194
- };
1195
-
1196
- export { ArgumentError, BakitClient, BakitError, BaseClientManager, BaseCommandContext, BaseParam, BaseParamSchema, ChatInputContext, Command, CommandManager, CommandOptionsSchema, Context, EVENT_INTENT_MAPPING, HookOrder, HookState, HotReloadable2 as HotReloadable, Instance, LifecycleManager, Listener, ListenerManager, ListenerOptionsSchema, loader_exports as Loader, MessageContext, NumberParam, NumberParamSchema, ParamUserType, Params, ProjectCacheManager, ProjectConfigSchema, RPC, RPC_RESPONSE_MARK, RPC_RESPONSE_TIMEOUT, StringParam, StringParamSchema, UserParam, UserParamSchema, chatInputCommandHandler, defineCommand, defineConfig, defineListener, extractSnowflakeId, getConfig, getEntryDirectory, getEntryFile, getTopLevelDirectory, loadConfig, messageCommandHandler, registerCommandsHandler, tokenize, useApp, validateParamsOrder };