djs-next 1.0.0-dev.2 → 1.0.0-dev.3

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.

Potentially problematic release.


This version of djs-next might be problematic. Click here for more details.

package/dist/index.js CHANGED
@@ -32,6 +32,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
32
32
  var index_exports = {};
33
33
  __export(index_exports, {
34
34
  DJSNextClient: () => DJSNextClient,
35
+ PaginationBuilder: () => PaginationBuilder,
36
+ confirmPrompt: () => confirmPrompt,
35
37
  defineConfig: () => defineConfig,
36
38
  getLocalesCache: () => getLocalesCache,
37
39
  loadConfig: () => loadConfig,
@@ -40,9 +42,10 @@ __export(index_exports, {
40
42
  translate: () => translate
41
43
  });
42
44
  module.exports = __toCommonJS(index_exports);
45
+ var import_config = require("dotenv/config");
43
46
 
44
47
  // src/client.ts
45
- var import_discord3 = require("discord.js");
48
+ var import_discord4 = require("discord.js");
46
49
 
47
50
  // src/handlers/commandHandler.ts
48
51
  var import_discord = require("discord.js");
@@ -288,9 +291,9 @@ function loadLocales(localesDir, defaultLocale) {
288
291
  for (const file of files) {
289
292
  if (file.endsWith(".json")) {
290
293
  const lang = file.replace(".json", "");
291
- const content = import_fs7.default.readFileSync(import_path5.default.join(localesDir, file), "utf8");
294
+ const content2 = import_fs7.default.readFileSync(import_path5.default.join(localesDir, file), "utf8");
292
295
  try {
293
- localesCache[lang] = JSON.parse(content);
296
+ localesCache[lang] = JSON.parse(content2);
294
297
  } catch (e) {
295
298
  console.error(`[djs-next] Failed to parse locale file: ${file}`, e);
296
299
  }
@@ -324,26 +327,92 @@ function getLocalesCache() {
324
327
  }
325
328
 
326
329
  // src/plugins/dnxt.ts
330
+ var import_discord3 = require("discord.js");
327
331
  var import_child_process = require("child_process");
328
332
  var import_util = __toESM(require("util"));
329
333
  var import_os = __toESM(require("os"));
330
334
  var execAsync = import_util.default.promisify(import_child_process.exec);
331
- async function handleDNXT(message, client, prefix = "dnxt") {
335
+ function buildDisplayMessage(content2, color = 5397233) {
336
+ const djs = require("discord.js");
337
+ const container = new djs.ContainerBuilder().setAccentColor(color).addTextDisplayComponents(
338
+ new djs.TextDisplayBuilder().setContent(content2)
339
+ );
340
+ return { components: [container], flags: 32768 };
341
+ }
342
+ async function handleDNXT(message, client, devPrefix) {
332
343
  if (message.author.bot) return;
333
- if (!client["_developers"].includes(message.author.id)) return;
334
- if (!message.content.startsWith(prefix)) return;
335
- const args = message.content.slice(prefix.length).trim().split(/ +/);
344
+ if (!client._developers.includes(message.author.id)) return;
345
+ if (client.config?.devGuildId && message.guildId !== client.config.devGuildId) return;
346
+ const originalReply = message.reply.bind(message);
347
+ message.reply = (async (content2) => {
348
+ if (typeof content2 === "string") {
349
+ return await originalReply({ ...buildDisplayMessage(content2), allowedMentions: { repliedUser: false } });
350
+ }
351
+ if (!content2.allowedMentions) content2.allowedMentions = { repliedUser: false };
352
+ return await originalReply(content2);
353
+ });
354
+ const content = message.content.trim();
355
+ const validTriggers = [devPrefix];
356
+ const clientPrefixes = client._prefixes || [];
357
+ for (const p of clientPrefixes) {
358
+ if (p !== "") validTriggers.push(`${p}${devPrefix}`);
359
+ }
360
+ let matchedTrigger = validTriggers.find((t) => content === t || content.startsWith(`${t} `));
361
+ if (!matchedTrigger) return;
362
+ const args = content.slice(matchedTrigger.length).trim().split(/ +/g);
336
363
  const command = args.shift()?.toLowerCase();
364
+ if (command === "help") {
365
+ const djs = require("discord.js");
366
+ const container = new djs.ContainerBuilder().setAccentColor(5397233).addTextDisplayComponents(
367
+ new djs.TextDisplayBuilder().setContent(`# \u{1F4D6} DNXT Toolkit Reference
368
+ > Current prefix trigger: \`${matchedTrigger}\``)
369
+ ).addSeparatorComponents(new djs.SeparatorBuilder()).addTextDisplayComponents(
370
+ new djs.TextDisplayBuilder().setContent(`### \u{1F4CA} Core Framework
371
+ - \`${matchedTrigger}\` \u2014 Developer system dashboard
372
+ - \`${matchedTrigger} help\` \u2014 Shows this reference menu`),
373
+ new djs.TextDisplayBuilder().setContent(`### \u{1F4BB} Execution & Diagnostics
374
+ - \`${matchedTrigger} js <code>\` \u2014 Evaluates raw JS code
375
+ - \`${matchedTrigger} sh <cmd>\` \u2014 Runs terminal shell script
376
+ - \`${matchedTrigger} debug <code>\` \u2014 Evaluates JS with precise memory deltas`),
377
+ new djs.TextDisplayBuilder().setContent(`### \u{1F4C2} File System & Network
378
+ - \`${matchedTrigger} cat <file>\` \u2014 Reads file contents
379
+ - \`${matchedTrigger} curl <url>\` \u2014 Fetches remote URL data
380
+ - \`${matchedTrigger} git <cmd>\` \u2014 Executes git repository commands`),
381
+ new djs.TextDisplayBuilder().setContent(`### \u{1F9F0} Utilities
382
+ - \`${matchedTrigger} <load|unload|reload> <target>\` \u2014 Manages system modules
383
+ - \`${matchedTrigger} sync\` \u2014 Forces global slash command sync
384
+ - \`${matchedTrigger} in <channel> <cmd>\` \u2014 Executes command in target channel
385
+ - \`${matchedTrigger} restart\` \u2014 Restarts the bot completely
386
+ - \`${matchedTrigger} shutdown\` \u2014 Stops the bot completely`)
387
+ );
388
+ await message.reply({ components: [container], flags: 32768, allowedMentions: { repliedUser: false } });
389
+ return;
390
+ }
337
391
  if (!command) {
338
392
  const mem = process.memoryUsage();
339
- const discordJsVersion = require("discord.js/package.json").version;
340
393
  const botPing = client.ws.ping;
341
- const text = `Module was loaded <t:${Math.floor((Date.now() - client.uptime) / 1e3)}:R>.
342
- DNXT framework plugin, discord.js \`${discordJsVersion}\`, \`Node.js ${process.version}\` on \`${import_os.default.type()}\`.
343
- Latencies: \`${botPing}ms\` websocket ping.
344
- Memory: \`${(mem.rss / 1024 / 1024).toFixed(2)} MB\` physical, \`${(mem.heapUsed / 1024 / 1024).toFixed(2)} MB\` heap.
345
- System: \`${import_os.default.cpus().length}\` thread(s), \`${(import_os.default.uptime() / 60 / 60).toFixed(2)}\` hrs system uptime.`;
346
- await message.reply(text);
394
+ const djs = require("discord.js");
395
+ const container = new djs.ContainerBuilder().setAccentColor(5397233).addTextDisplayComponents(
396
+ new djs.TextDisplayBuilder().setContent(`# \u{1F6E0}\uFE0F Developer System Dashboard
397
+ > **DNXT Framework Engine**`)
398
+ ).addSeparatorComponents(new djs.SeparatorBuilder()).addTextDisplayComponents(
399
+ new djs.TextDisplayBuilder().setContent(`### \u{1F4E1} Network Status
400
+ - **System Uptime:** <t:${Math.floor((Date.now() - client.uptime) / 1e3)}:R>
401
+ - **WebSocket Latency:** \`${botPing}ms\``),
402
+ new djs.TextDisplayBuilder().setContent(`### \u{1F4E6} Host Environment
403
+ - **Discord.js Library:** \`v${import_discord3.version}\`
404
+ - **Node.js Runtime:** \`${process.version}\`
405
+ - **Operating System:** \`${import_os.default.type()}\` (\`${import_os.default.cpus().length}\` thread cores, \`${(import_os.default.uptime() / 60 / 60).toFixed(2)}\` hrs uptime)`),
406
+ new djs.TextDisplayBuilder().setContent(`### \u{1F9E0} Resource Utilization
407
+ - **Physical Memory:** \`${(mem.rss / 1024 / 1024).toFixed(2)} MB\`
408
+ - **Heap Allocated:** \`${(mem.heapUsed / 1024 / 1024).toFixed(2)} MB\``)
409
+ );
410
+ await message.reply({
411
+ components: [container],
412
+ flags: 32768,
413
+ // MessageFlags.IsComponentsV2
414
+ allowedMentions: { repliedUser: false }
415
+ });
347
416
  return;
348
417
  }
349
418
  if (command === "js" || command === "eval" || command === "py") {
@@ -357,12 +426,12 @@ System: \`${import_os.default.cpus().length}\` thread(s), \`${(import_os.default
357
426
  let evaled = await eval(`(async () => { ${code} })()`);
358
427
  const end = process.hrtime.bigint();
359
428
  const timeMs = Number(end - start) / 1e6;
360
- if (typeof evaled !== "string") evaled = import_util.default.inspect(evaled, { depth: 1 });
429
+ if (typeof evaled !== "string") evaled = import_util.default.inspect(evaled, { depth: 1, colors: true });
361
430
  await sendPaginatedText(message, `\u2705 **Evaluated in ${timeMs.toFixed(3)}ms**
362
- `, evaled, "js");
431
+ `, evaled, "ansi");
363
432
  } catch (err) {
364
433
  await sendPaginatedText(message, `\u274C **Evaluation Error**
365
- `, err.message, "js");
434
+ `, err.message, "ansi");
366
435
  }
367
436
  return;
368
437
  }
@@ -371,52 +440,74 @@ System: \`${import_os.default.cpus().length}\` thread(s), \`${(import_os.default
371
440
  if (!cmd) return void await message.reply("\u274C Please provide a command to execute.");
372
441
  try {
373
442
  const start2 = process.hrtime.bigint();
374
- const { stdout, stderr } = await execAsync(cmd);
443
+ let stdout = "", stderr = "", code2 = 0;
444
+ try {
445
+ const result = await execAsync(cmd);
446
+ stdout = result.stdout;
447
+ stderr = result.stderr;
448
+ } catch (err) {
449
+ stdout = err.stdout || "";
450
+ stderr = err.stderr || err.message || "";
451
+ code2 = err.code || 1;
452
+ }
375
453
  const end2 = process.hrtime.bigint();
376
454
  const timeMs2 = Number(end2 - start2) / 1e6;
377
- const result = stdout || stderr || "No output.";
455
+ let resultText = `$ ${cmd}
456
+ `;
457
+ if (stdout) resultText += `${stdout}
458
+ `;
459
+ if (stderr) resultText += `${stderr}
460
+ `;
461
+ resultText += `
462
+ [status] Return code ${code2}`;
378
463
  await sendPaginatedText(message, `\u2705 **Executed in ${timeMs2.toFixed(3)}ms**
379
- `, result, "sh");
464
+ `, resultText, "ansi");
380
465
  } catch (err) {
381
466
  await sendPaginatedText(message, `\u274C **Shell Error**
382
- `, err.message, "sh");
467
+ `, err.message, "ansi");
383
468
  }
384
469
  return;
385
470
  }
386
- if (command === "reload" || command === "load") {
471
+ if (command === "load" || command === "unload" || command === "reload") {
387
472
  const target = args[0]?.toLowerCase();
388
473
  try {
389
474
  if (target === "commands" && client["_commandsDir"]) {
390
- client.commands.clear();
391
- client.commands = await loadAndDeployCommands(client["_commandsDir"], client.token, client["_clientId"], client["_guildId"]);
392
- await message.reply("\u2705 Reloaded commands.");
475
+ if (command === "unload" || command === "reload") client.commands.clear();
476
+ if (command === "load" || command === "reload") client.commands = await loadAndDeployCommands(client["_commandsDir"], client.token, client["_clientId"], client["_guildId"]);
477
+ await message.reply(`\u2705 Successfully ${command}ed commands.`);
393
478
  } else if (target === "events" && client["_eventsDir"]) {
394
- client.removeAllListeners();
395
- client["attachCoreListeners"]();
396
- await loadEvents(client, client["_eventsDir"]);
397
- await message.reply("\u2705 Reloaded events.");
479
+ if (command === "unload" || command === "reload") {
480
+ client.removeAllListeners();
481
+ client["attachCoreListeners"]();
482
+ }
483
+ if (command === "load" || command === "reload") await loadEvents(client, client["_eventsDir"]);
484
+ await message.reply(`\u2705 Successfully ${command}ed events.`);
398
485
  } else if (target === "components" && client["_componentsDir"]) {
399
- client.components.clear();
400
- client.components = await loadComponents(client["_componentsDir"]);
401
- await message.reply("\u2705 Reloaded components.");
486
+ if (command === "unload" || command === "reload") client.components.clear();
487
+ if (command === "load" || command === "reload") client.components = await loadComponents(client["_componentsDir"]);
488
+ await message.reply(`\u2705 Successfully ${command}ed components.`);
402
489
  } else if (target === "locales" && client["_localesDir"]) {
403
- loadLocales(client["_localesDir"], client.config.defaultLocale);
404
- await message.reply("\u2705 Reloaded locales.");
490
+ if (command === "load" || command === "reload") loadLocales(client["_localesDir"], client.config.defaultLocale);
491
+ await message.reply(`\u2705 Successfully ${command}ed locales.`);
405
492
  } else if (target === "all" || !target) {
406
- client.removeAllListeners();
407
- client["attachCoreListeners"]();
408
- client.commands.clear();
409
- client.components.clear();
410
- if (client["_eventsDir"]) await loadEvents(client, client["_eventsDir"]);
411
- if (client["_componentsDir"]) client.components = await loadComponents(client["_componentsDir"]);
412
- if (client["_localesDir"]) loadLocales(client["_localesDir"], client.config.defaultLocale);
413
- if (client["_commandsDir"]) client.commands = await loadAndDeployCommands(client["_commandsDir"], client.token, client["_clientId"], client["_guildId"]);
414
- await message.reply("\u2705 Reloaded all framework modules.");
493
+ if (command === "unload" || command === "reload") {
494
+ client.removeAllListeners();
495
+ client["attachCoreListeners"]();
496
+ client.commands.clear();
497
+ client.components.clear();
498
+ }
499
+ if (command === "load" || command === "reload") {
500
+ if (client["_eventsDir"]) await loadEvents(client, client["_eventsDir"]);
501
+ if (client["_componentsDir"]) client.components = await loadComponents(client["_componentsDir"]);
502
+ if (client["_localesDir"]) loadLocales(client["_localesDir"], client.config.defaultLocale);
503
+ if (client["_commandsDir"]) client.commands = await loadAndDeployCommands(client["_commandsDir"], client.token, client["_clientId"], client["_guildId"]);
504
+ }
505
+ await message.reply(`\u2705 Successfully ${command}ed all framework modules.`);
415
506
  } else {
416
507
  await message.reply("\u274C Unknown target. Valid targets: `commands, events, components, locales, all`");
417
508
  }
418
509
  } catch (err) {
419
- await message.reply(`\u274C **Reload Error:** ${err.message}`);
510
+ await message.reply(`\u274C **${command.toUpperCase()} Error:** ${err.message}`);
420
511
  }
421
512
  return;
422
513
  }
@@ -426,49 +517,14 @@ System: \`${import_os.default.cpus().length}\` thread(s), \`${(import_os.default
426
517
  const file = args.join(" ");
427
518
  if (!file) return void await message.reply("\u274C Please provide a file to read.");
428
519
  try {
429
- const content = fs9.readFileSync(path7.resolve(process.cwd(), file), "utf8");
520
+ const content2 = fs9.readFileSync(path7.resolve(process.cwd(), file), "utf8");
430
521
  await sendPaginatedText(message, `\u{1F4C4} **${file}**
431
- `, content, file.split(".").pop() || "");
522
+ `, content2, file.split(".").pop() || "");
432
523
  } catch (e) {
433
524
  await message.reply(`\u274C **Error reading file:** ${e.message}`);
434
525
  }
435
526
  return;
436
527
  }
437
- if (command === "su" || command === "sudo") {
438
- const targetUserId = args.shift();
439
- const cmd = args.join(" ");
440
- if (!targetUserId || !cmd) return void await message.reply(`\u274C Usage: ${prefix} su <user_id> <command>`);
441
- try {
442
- const targetUser = await client.users.fetch(targetUserId.replace(/<@!?|>/g, ""));
443
- if (!targetUser) throw new Error("User not found.");
444
- const mockMessage = Object.assign(Object.create(Object.getPrototypeOf(message)), message);
445
- mockMessage.author = targetUser;
446
- if (message.guild) {
447
- mockMessage.member = await message.guild.members.fetch(targetUser.id).catch(() => null);
448
- }
449
- mockMessage.content = cmd;
450
- client.emit("messageCreate", mockMessage);
451
- await message.reply(`\u2705 Invoked \`${cmd}\` as **${targetUser.tag}**.`);
452
- } catch (e) {
453
- await message.reply(`\u274C **Sudo Error:** ${e.message}`);
454
- }
455
- return;
456
- }
457
- if (command === "source" || command === "src") {
458
- const target = args.join(" ");
459
- if (!target) return void await message.reply("\u274C Please provide a command name.");
460
- const cmdData = client.commands.get(target);
461
- if (!cmdData || !cmdData.filepath) return void await message.reply("\u274C Command not found or has no associated filepath.");
462
- try {
463
- const fs9 = await import("fs");
464
- const content = fs9.readFileSync(cmdData.filepath, "utf8");
465
- await sendPaginatedText(message, `\u{1F4C4} **Source of \`${target}\`**
466
- `, content, cmdData.filepath.split(".").pop() || "");
467
- } catch (e) {
468
- await message.reply(`\u274C **Error reading source:** ${e.message}`);
469
- }
470
- return;
471
- }
472
528
  if (command === "curl") {
473
529
  const url = args[0];
474
530
  if (!url) return void await message.reply("\u274C Please provide a URL.");
@@ -496,64 +552,38 @@ System: \`${import_os.default.cpus().length}\` thread(s), \`${(import_os.default
496
552
  const endMem = process.memoryUsage().heapUsed;
497
553
  const timeMs = Number(end - start) / 1e6;
498
554
  const memDiff = (endMem - startMem) / 1024 / 1024;
499
- if (typeof evaled !== "string") evaled = import_util.default.inspect(evaled, { depth: 1 });
555
+ if (typeof evaled !== "string") evaled = import_util.default.inspect(evaled, { depth: 1, colors: true });
500
556
  await sendPaginatedText(message, `\u23F1\uFE0F **Debug Execution**
501
557
  Time: \`${timeMs.toFixed(3)}ms\` | Heap Delta: \`${memDiff.toFixed(3)}MB\`
502
- `, evaled, "js");
558
+ `, evaled, "ansi");
503
559
  } catch (err) {
504
560
  await sendPaginatedText(message, `\u274C **Debug Error**
505
- `, err.message, "js");
561
+ `, err.message, "ansi");
506
562
  }
507
563
  return;
508
564
  }
509
565
  if (command === "in") {
510
566
  const channelId = args.shift()?.replace(/<#|>/g, "");
511
567
  const cmd = args.join(" ");
512
- if (!channelId || !cmd) return void await message.reply(`\u274C Usage: ${prefix} in <channel> <command>`);
568
+ if (!channelId || !cmd) return void await message.reply(`\u274C Usage: ${matchedTrigger} in <#channel|id> <command>`);
513
569
  try {
514
570
  const targetChannel = await client.channels.fetch(channelId);
515
571
  if (!targetChannel || !targetChannel.isTextBased()) throw new Error("Invalid Text Channel.");
516
572
  const mockMessage = Object.assign(Object.create(Object.getPrototypeOf(message)), message);
517
- mockMessage.channel = targetChannel;
518
- mockMessage.channelId = targetChannel.id;
519
- mockMessage.content = cmd;
573
+ Object.defineProperty(mockMessage, "client", { value: client, configurable: true });
574
+ if (message.guild) {
575
+ Object.defineProperty(mockMessage, "guild", { value: message.guild, configurable: true });
576
+ Object.defineProperty(mockMessage, "guildId", { value: message.guildId, configurable: true });
577
+ }
578
+ Object.defineProperty(mockMessage, "channel", { value: targetChannel, configurable: true });
579
+ Object.defineProperty(mockMessage, "channelId", { value: targetChannel.id, configurable: true });
580
+ Object.defineProperty(mockMessage, "content", { value: cmd, configurable: true });
520
581
  client.emit("messageCreate", mockMessage);
521
- await message.reply(`\u2705 Redirected execution to <#${targetChannel.id}>.`);
522
582
  } catch (e) {
523
583
  await message.reply(`\u274C **In Error:** ${e.message}`);
524
584
  }
525
585
  return;
526
586
  }
527
- if (command === "tasks") {
528
- const tasks = client._activeTasks;
529
- if (!tasks || tasks.size === 0) return void await message.reply("No active background tasks running.");
530
- let text = `\u2699\uFE0F **Active Background Tasks (${tasks.size})**
531
- `;
532
- for (const [name] of tasks.entries()) {
533
- text += `- \`${name.split("/").pop()}\`
534
- `;
535
- }
536
- await sendPaginatedText(message, "", text, "");
537
- return;
538
- }
539
- if (command === "cancel") {
540
- const target = args.join(" ");
541
- if (!target) return void await message.reply(`\u274C Usage: ${prefix} cancel <task_name>`);
542
- const tasks = client._activeTasks;
543
- if (!tasks) return void await message.reply("No active tasks to cancel.");
544
- let found = false;
545
- for (const [name, intervalId] of tasks.entries()) {
546
- if (name.includes(target)) {
547
- clearInterval(intervalId);
548
- tasks.delete(name);
549
- found = true;
550
- await message.reply(`\u2705 Cancelled background task: \`${name.split("/").pop()}\``);
551
- break;
552
- }
553
- }
554
- if (!found) await message.reply("\u274C Task not found.");
555
- return;
556
- }
557
587
  if (command === "sync") {
558
588
  try {
559
589
  await message.reply("\u{1F504} Force syncing slash commands...");
@@ -564,85 +594,107 @@ Time: \`${timeMs.toFixed(3)}ms\` | Heap Delta: \`${memDiff.toFixed(3)}MB\`
564
594
  }
565
595
  return;
566
596
  }
567
- if (command === "sql") {
568
- const query = args.join(" ");
569
- if (!query) return void await message.reply(`\u274C Usage: ${prefix} sql <query>`);
570
- try {
571
- let res;
572
- if (client.db && typeof client.db.$queryRawUnsafe === "function") {
573
- res = await client.db.$queryRawUnsafe(query);
574
- } else if (client.db && typeof client.db.query === "function") {
575
- res = await client.db.query(query);
576
- } else {
577
- return void await message.reply("\u274C Your configured `client.db` does not expose a recognized raw SQL execution method (`$queryRawUnsafe` or `query`).");
578
- }
579
- const inspect = import_util.default.inspect(res, { depth: 2 });
580
- await sendPaginatedText(message, `\u{1F5C4}\uFE0F **SQL Query**
581
- `, inspect, "js");
582
- } catch (e) {
583
- await message.reply(`\u274C **SQL Error:** ${e.message}`);
584
- }
597
+ if (command === "restart") {
598
+ await message.reply("\u{1F504} Restarting framework...");
599
+ client.destroy();
600
+ const { spawn } = require("child_process");
601
+ const child = spawn(process.argv[0], process.argv.slice(1), {
602
+ detached: true,
603
+ stdio: "ignore",
604
+ cwd: process.cwd()
605
+ });
606
+ child.unref();
607
+ process.exit(0);
585
608
  return;
586
609
  }
587
- if (command === "vc" || command === "voice") {
588
- if (!message.guild) return void await message.reply("\u274C This command must be used in a server.");
589
- const me = message.guild.members.me;
590
- if (!me?.voice?.channel) return void await message.reply("\u274C Bot is not currently in a voice channel.");
591
- const text = `\u{1F399}\uFE0F **Voice Debugger**
592
- Channel: <#${me.voice.channel.id}> (\`${me.voice.channel.id}\`)
593
- Muted: ${me.voice.mute} | Deafened: ${me.voice.deaf}
594
- Session ID: \`${me.voice.sessionId || "None"}\``;
595
- await message.reply(text);
610
+ if (command === "shutdown" || command === "stop") {
611
+ await message.reply("\u{1F6D1} Shutting down framework...");
612
+ client.destroy();
613
+ process.exit(0);
596
614
  return;
597
615
  }
598
- await message.reply(`\u2753 Unknown ${prefix} command. Available: \`js, sh, git, cat, curl, su, in, source, debug, reload, tasks, cancel, sync, sql, vc\``);
616
+ await message.reply(`\u2753 Unknown ${matchedTrigger} command. Available: \`js, sh, git, cat, curl, in, debug, reload, sync, restart, shutdown\``);
599
617
  }
600
- async function sendPaginatedText(message2, header, content, language = "") {
601
- const maxLength = 1900;
602
- if (content.length <= maxLength) {
603
- await message2.reply(`${header}\`\`\`${language}
604
- ${content}
605
- \`\`\``);
618
+ async function sendPaginatedText(message2, header, content2, language = "") {
619
+ const maxLength = 800;
620
+ if (content2.length <= maxLength) {
621
+ await message2.reply(buildDisplayMessage(`### ${header.replace(/\\*\\*/g, "")}
622
+ \`\`\`${language}
623
+ ${content2}
624
+ \`\`\``));
606
625
  return;
607
626
  }
608
627
  const chunks = [];
609
- for (let i = 0; i < content.length; i += maxLength) {
610
- chunks.push(content.substring(i, i + maxLength));
628
+ for (let i = 0; i < content2.length; i += maxLength) {
629
+ chunks.push(content2.substring(i, i + maxLength));
611
630
  }
612
631
  let index = 0;
613
- const reply = await message2.reply(`${header}\`\`\`${language}
614
- ${chunks[index]}
632
+ const djs = require("discord.js");
633
+ function buildPage(idx) {
634
+ const text = `### ${header.replace(/\\*\\*/g, "")}
635
+ \`\`\`${language}
636
+ ${chunks[idx]}
615
637
  \`\`\`
616
- *Page 1 of ${chunks.length}*`);
617
- await reply.react("\u25C0\uFE0F");
618
- await reply.react("\u25B6\uFE0F");
619
- await reply.react("\u23F9\uFE0F");
620
- const collector = reply.createReactionCollector({
621
- filter: (reaction, user) => ["\u25C0\uFE0F", "\u25B6\uFE0F", "\u23F9\uFE0F"].includes(reaction.emoji.name) && user.id === message2.author.id,
638
+ > *Page ${idx + 1} of ${chunks.length}*`;
639
+ const container = new djs.ContainerBuilder().setAccentColor(5397233).addTextDisplayComponents(
640
+ new djs.TextDisplayBuilder().setContent(text)
641
+ );
642
+ const row1 = new djs.ActionRowBuilder().addComponents(
643
+ new djs.ButtonBuilder().setCustomId("first").setLabel("\u226A").setStyle(2),
644
+ new djs.ButtonBuilder().setCustomId("prev").setLabel("\uFF1C").setStyle(2),
645
+ new djs.ButtonBuilder().setCustomId("goto").setLabel("\u2398").setStyle(1),
646
+ new djs.ButtonBuilder().setCustomId("next").setLabel("\uFF1E").setStyle(2),
647
+ new djs.ButtonBuilder().setCustomId("last").setLabel("\u226B").setStyle(2)
648
+ );
649
+ const row2 = new djs.ActionRowBuilder().addComponents(
650
+ new djs.ButtonBuilder().setCustomId("stop").setLabel("\u2716 Close").setStyle(4)
651
+ );
652
+ container.addActionRowComponents(row1, row2);
653
+ return { components: [container], flags: 32768 };
654
+ }
655
+ const reply = await message2.reply(buildPage(index));
656
+ const collector = reply.createMessageComponentCollector({
657
+ filter: (i) => ["first", "prev", "goto", "stop", "next", "last"].includes(i.customId) && i.user.id === message2.author.id,
622
658
  time: 12e4
623
659
  });
624
- collector.on("collect", async (reaction, user) => {
625
- await reaction.users.remove(user.id).catch(() => null);
626
- if (reaction.emoji.name === "\u25C0\uFE0F") {
660
+ collector.on("collect", async (i) => {
661
+ if (i.customId === "first") {
662
+ index = 0;
663
+ } else if (i.customId === "prev") {
627
664
  index = index > 0 ? index - 1 : index;
628
- } else if (reaction.emoji.name === "\u25B6\uFE0F") {
665
+ } else if (i.customId === "next") {
629
666
  index = index < chunks.length - 1 ? index + 1 : index;
630
- } else if (reaction.emoji.name === "\u23F9\uFE0F") {
667
+ } else if (i.customId === "last") {
668
+ index = chunks.length - 1;
669
+ } else if (i.customId === "goto") {
670
+ const modal = new djs.ModalBuilder().setCustomId("goto_modal").setTitle("Go to Page");
671
+ const input = new djs.TextInputBuilder().setCustomId("page_num").setLabel(`Page Number (1-${chunks.length})`).setStyle(1).setRequired(true);
672
+ modal.addComponents(new djs.ActionRowBuilder().addComponents(input));
673
+ await i.showModal(modal);
674
+ try {
675
+ const modalSubmit = await i.awaitModalSubmit({ filter: (mi) => mi.user.id === message2.author.id && mi.customId === "goto_modal", time: 6e4 });
676
+ const targetPage = parseInt(modalSubmit.fields.getTextInputValue("page_num"), 10);
677
+ if (!isNaN(targetPage) && targetPage >= 1 && targetPage <= chunks.length) {
678
+ index = targetPage - 1;
679
+ }
680
+ await modalSubmit.update(buildPage(index));
681
+ } catch {
682
+ }
683
+ return;
684
+ } else if (i.customId === "stop") {
685
+ await reply.delete().catch(() => null);
631
686
  collector.stop();
632
687
  return;
633
688
  }
634
- await reply.edit(`${header}\`\`\`${language}
635
- ${chunks[index]}
636
- \`\`\`
637
- *Page ${index + 1} of ${chunks.length}*`);
689
+ await i.update(buildPage(index));
638
690
  });
639
691
  collector.on("end", () => {
640
- reply.reactions.removeAll().catch(() => null);
692
+ reply.edit({ components: [] }).catch(() => null);
641
693
  });
642
694
  }
643
695
 
644
696
  // src/client.ts
645
- var DJSNextClient = class extends import_discord3.Client {
697
+ var DJSNextClient = class extends import_discord4.Client {
646
698
  commands;
647
699
  components;
648
700
  cooldowns;
@@ -659,12 +711,15 @@ var DJSNextClient = class extends import_discord3.Client {
659
711
  _developers;
660
712
  _middleware;
661
713
  _prefixes;
714
+ _enableSlashCommands;
715
+ _enableTextCommands;
662
716
  _enableMentionPrefix;
717
+ _enableNoPrefix;
663
718
  constructor(options) {
664
719
  super(options);
665
- this.commands = new import_discord3.Collection();
666
- this.components = new import_discord3.Collection();
667
- this.cooldowns = new import_discord3.Collection();
720
+ this.commands = new import_discord4.Collection();
721
+ this.components = new import_discord4.Collection();
722
+ this.cooldowns = new import_discord4.Collection();
668
723
  this._commandsDir = options.commandsDir ? import_path6.default.resolve(process.cwd(), options.commandsDir) : void 0;
669
724
  this._eventsDir = options.eventsDir ? import_path6.default.resolve(process.cwd(), options.eventsDir) : void 0;
670
725
  this._componentsDir = options.componentsDir ? import_path6.default.resolve(process.cwd(), options.componentsDir) : void 0;
@@ -675,7 +730,12 @@ var DJSNextClient = class extends import_discord3.Client {
675
730
  this._middleware = options.middleware;
676
731
  const prefs = options.prefixes || [];
677
732
  this._prefixes = Array.isArray(prefs) ? prefs : [prefs];
733
+ this._prefixes = this._prefixes.filter((p) => p !== "");
734
+ this._enableSlashCommands = options.enableSlashCommands ?? true;
735
+ this._enableTextCommands = options.enableTextCommands ?? true;
678
736
  this._enableMentionPrefix = options.enableMentionPrefix ?? true;
737
+ this._enableNoPrefix = options.enableNoPrefix ?? false;
738
+ if (options.db) this.db = options.db;
679
739
  this.attachCoreListeners();
680
740
  }
681
741
  attachCoreListeners() {
@@ -690,6 +750,7 @@ var DJSNextClient = class extends import_discord3.Client {
690
750
  }
691
751
  }
692
752
  if (interaction.isChatInputCommand()) {
753
+ if (!this._enableSlashCommands) return;
693
754
  let commandKey = interaction.commandName;
694
755
  const group = interaction.options.getSubcommandGroup(false);
695
756
  const sub = interaction.options.getSubcommand(false);
@@ -697,48 +758,11 @@ var DJSNextClient = class extends import_discord3.Client {
697
758
  if (sub) commandKey += ` ${sub}`;
698
759
  const command2 = this.commands.get(commandKey);
699
760
  if (!command2 || !command2.execute) return;
700
- if (command2.developerOnly && !this._developers.includes(interaction.user.id)) {
701
- return interaction.reply({ content: "Only developers can use this command.", ephemeral: true });
702
- }
703
- if (command2.guildOnly && !interaction.inGuild()) {
704
- return interaction.reply({ content: "This command can only be used in a server.", ephemeral: true });
705
- }
706
- if (command2.userPermissions && interaction.memberPermissions) {
707
- const missing = interaction.memberPermissions.missing(command2.userPermissions);
708
- if (missing.length > 0) {
709
- return interaction.reply({ content: `You are missing permissions: \`${missing.join(", ")}\``, ephemeral: true });
710
- }
711
- }
712
- if (command2.botPermissions && interaction.guild?.members.me?.permissions) {
713
- const missing = interaction.guild.members.me.permissions.missing(command2.botPermissions);
714
- if (missing.length > 0) {
715
- return interaction.reply({ content: `I am missing permissions to run this: \`${missing.join(", ")}\``, ephemeral: true });
716
- }
717
- }
718
- if (command2.cooldown) {
719
- if (!this.cooldowns.has(commandKey)) this.cooldowns.set(commandKey, new import_discord3.Collection());
720
- const now = Date.now();
721
- const timestamps = this.cooldowns.get(commandKey);
722
- const cooldownAmount = command2.cooldown * 1e3;
723
- if (timestamps.has(interaction.user.id)) {
724
- const expirationTime = timestamps.get(interaction.user.id) + cooldownAmount;
725
- if (now < expirationTime) {
726
- return interaction.reply({
727
- content: `Please wait, you are on a cooldown. You can use it again <t:${Math.round(expirationTime / 1e3)}:R>.`,
728
- ephemeral: true
729
- });
730
- }
731
- }
732
- timestamps.set(interaction.user.id, now);
733
- setTimeout(() => timestamps.delete(interaction.user.id), cooldownAmount);
734
- }
761
+ if (!await this.handlePreconditions(command2, interaction, commandKey)) return;
735
762
  try {
736
763
  await command2.execute(interaction, this);
737
764
  } catch (error) {
738
- console.error(`[djs-next] Error executing command: "${commandKey}"`, error);
739
- const msg = { content: "We ran into an internal error executing this command. The developers have been notified.", ephemeral: true };
740
- if (interaction.replied || interaction.deferred) await interaction.followUp(msg).catch(() => null);
741
- else await interaction.reply(msg).catch(() => null);
765
+ await this.handleCommandError(error, commandKey, interaction);
742
766
  }
743
767
  }
744
768
  if (interaction.isAutocomplete()) {
@@ -774,19 +798,18 @@ var DJSNextClient = class extends import_discord3.Client {
774
798
  }
775
799
  }
776
800
  if (component) {
801
+ if (!await this.handlePreconditions(component, interaction, interaction.customId)) return;
777
802
  try {
778
803
  await component.execute(interaction, this, params);
779
804
  } catch (error) {
780
- console.error(`[djs-next] Error executing component: "${interaction.customId}"`, error);
781
- const msg = { content: "We ran into an internal error executing this component.", ephemeral: true };
782
- if (interaction.replied || interaction.deferred) await interaction.followUp(msg).catch(() => null);
783
- else await interaction.reply(msg).catch(() => null);
805
+ await this.handleCommandError(error, interaction.customId, interaction);
784
806
  }
785
807
  }
786
808
  }
787
809
  });
788
810
  this.on("messageCreate", async (message2) => {
789
811
  if (message2.author.bot) return;
812
+ if (!this._enableTextCommands) return;
790
813
  if (this._middleware) {
791
814
  try {
792
815
  const shouldContinue = await this._middleware(message2, this);
@@ -796,28 +819,31 @@ var DJSNextClient = class extends import_discord3.Client {
796
819
  return;
797
820
  }
798
821
  }
799
- let content = message2.content.trim();
822
+ let content2 = message2.content.trim();
800
823
  let matchedPrefix = "";
801
824
  let isCommand = false;
802
825
  const mentionRegex = new RegExp(`^<@!?${this.user?.id}>\\s*`);
803
- if (this._enableMentionPrefix && mentionRegex.test(content)) {
804
- matchedPrefix = content.match(mentionRegex)[0];
826
+ const canUseMention = this._enableMentionPrefix === true || Array.isArray(this._enableMentionPrefix) && this._enableMentionPrefix.includes(message2.author.id);
827
+ if (canUseMention && mentionRegex.test(content2)) {
828
+ matchedPrefix = content2.match(mentionRegex)[0];
805
829
  isCommand = true;
806
830
  } else {
807
831
  const sortedPrefs = [...this._prefixes].sort((a, b) => b.length - a.length);
808
- if (sortedPrefs.includes("")) {
809
- isCommand = true;
810
- }
811
832
  for (const p of sortedPrefs) {
812
- if (p !== "" && content.startsWith(p)) {
833
+ if (content2.startsWith(p)) {
813
834
  matchedPrefix = p;
814
835
  isCommand = true;
815
836
  break;
816
837
  }
817
838
  }
839
+ const canUseNoPrefix = this._enableNoPrefix === true || Array.isArray(this._enableNoPrefix) && this._enableNoPrefix.includes(message2.author.id);
840
+ if (!isCommand && canUseNoPrefix) {
841
+ isCommand = true;
842
+ matchedPrefix = "";
843
+ }
818
844
  }
819
845
  if (!isCommand) return;
820
- const args2 = content.slice(matchedPrefix.length).trim().split(/ +/g);
846
+ const args2 = content2.slice(matchedPrefix.length).trim().split(/ +/g);
821
847
  const commandName = args2.shift()?.toLowerCase();
822
848
  if (!commandName) return;
823
849
  let command2 = this.commands.get(commandName);
@@ -825,35 +851,11 @@ var DJSNextClient = class extends import_discord3.Client {
825
851
  command2 = this.commands.find((c) => c.aliases?.includes(commandName) || false);
826
852
  }
827
853
  if (!command2 || !command2.executeText) return;
828
- if (command2.developerOnly && !this._developers.includes(message2.author.id)) return;
829
- if (command2.guildOnly && !message2.guild) return;
830
- if (command2.userPermissions && message2.member?.permissions) {
831
- const missing = message2.member.permissions.missing(command2.userPermissions);
832
- if (missing.length > 0) return;
833
- }
834
- if (command2.botPermissions && message2.guild?.members.me?.permissions) {
835
- const missing = message2.guild.members.me.permissions.missing(command2.botPermissions);
836
- if (missing.length > 0) return;
837
- }
838
- if (command2.cooldown) {
839
- if (!this.cooldowns.has(commandName)) this.cooldowns.set(commandName, new import_discord3.Collection());
840
- const now = Date.now();
841
- const timestamps = this.cooldowns.get(commandName);
842
- const cooldownAmount = command2.cooldown * 1e3;
843
- if (timestamps.has(message2.author.id)) {
844
- const expirationTime = timestamps.get(message2.author.id) + cooldownAmount;
845
- if (now < expirationTime) {
846
- return void await message2.reply(`Please wait, you are on a cooldown. You can use it again <t:${Math.round(expirationTime / 1e3)}:R>.`);
847
- }
848
- }
849
- timestamps.set(message2.author.id, now);
850
- setTimeout(() => timestamps.delete(message2.author.id), cooldownAmount);
851
- }
854
+ if (!await this.handlePreconditions(command2, message2, commandName)) return;
852
855
  try {
853
856
  await command2.executeText(message2, args2, this);
854
857
  } catch (error) {
855
- console.error(`[djs-next] Error executing text command: "${commandName}"`, error);
856
- await message2.reply("We ran into an internal error executing this command.").catch(() => null);
858
+ await this.handleCommandError(error, commandName, message2);
857
859
  }
858
860
  });
859
861
  }
@@ -861,12 +863,12 @@ var DJSNextClient = class extends import_discord3.Client {
861
863
  if (!token) throw new Error("[djs-next] A token must be provided to start the bot.");
862
864
  this.config = await loadConfig();
863
865
  this._guildId = this._guildId || this.config.devGuildId;
864
- if (!this._commandsDir && this.config.directories?.commands) this._commandsDir = import_path6.default.resolve(process.cwd(), this.config.directories.commands);
865
- if (!this._eventsDir && this.config.directories?.events) this._eventsDir = import_path6.default.resolve(process.cwd(), this.config.directories.events);
866
- if (!this._componentsDir && this.config.directories?.components) this._componentsDir = import_path6.default.resolve(process.cwd(), this.config.directories.components);
867
- if (!this._tasksDir && this.config.directories?.tasks) this._tasksDir = import_path6.default.resolve(process.cwd(), this.config.directories.tasks);
868
- if (!this._localesDir && this.config.directories?.locales) this._localesDir = import_path6.default.resolve(process.cwd(), this.config.directories.locales);
869
- if (this._localesDir) loadLocales(this._localesDir, this.config.defaultLocale);
866
+ if (!this._commandsDir) this._commandsDir = import_path6.default.resolve(process.cwd(), this.config.directories?.commands || "src/commands");
867
+ if (!this._eventsDir) this._eventsDir = import_path6.default.resolve(process.cwd(), this.config.directories?.events || "src/events");
868
+ if (!this._componentsDir) this._componentsDir = import_path6.default.resolve(process.cwd(), this.config.directories?.components || "src/components");
869
+ if (!this._tasksDir) this._tasksDir = import_path6.default.resolve(process.cwd(), this.config.directories?.tasks || "src/tasks");
870
+ if (!this._localesDir) this._localesDir = import_path6.default.resolve(process.cwd(), this.config.directories?.locales || "src/locales");
871
+ if (import_fs8.default.existsSync(this._localesDir)) loadLocales(this._localesDir, this.config.defaultLocale);
870
872
  if (!this._middleware) {
871
873
  const exts = [".js", ".mjs", ".cjs", ".ts", ".mts", ".cts"];
872
874
  const cwd = process.cwd();
@@ -886,24 +888,24 @@ var DJSNextClient = class extends import_discord3.Client {
886
888
  }
887
889
  }
888
890
  }
889
- if (this._eventsDir) await loadEvents(this, this._eventsDir);
890
- if (this._componentsDir) this.components = await loadComponents(this._componentsDir);
891
- if (this._tasksDir) await loadTasks(this, this._tasksDir);
892
- if (this._commandsDir) {
893
- if (!this._clientId) throw new Error("[djs-next] You must provide a clientId to deploy commands.");
894
- this.commands = await loadAndDeployCommands(this._commandsDir, token, this._clientId, this._guildId);
895
- }
891
+ if (import_fs8.default.existsSync(this._eventsDir)) await loadEvents(this, this._eventsDir);
892
+ if (import_fs8.default.existsSync(this._componentsDir)) this.components = await loadComponents(this._componentsDir);
893
+ if (import_fs8.default.existsSync(this._tasksDir)) await loadTasks(this, this._tasksDir);
896
894
  await this.login(token);
897
895
  console.log(`[djs-next] Bot is ready and logged in as ${this.user?.tag}!`);
896
+ if (import_fs8.default.existsSync(this._commandsDir)) {
897
+ this._clientId = this._clientId || this.user.id;
898
+ this.commands = await loadAndDeployCommands(this._commandsDir, token, this._clientId, this._guildId);
899
+ }
898
900
  }
899
- enableDevTools(prefix2 = "dnxt") {
900
- if (prefix2 !== "dnxt" && prefix2 !== "nxt") {
901
- throw new Error(`[djs-next] Developer Tools prefix must be either 'dnxt' or 'nxt'. Received: ${prefix2}`);
901
+ enableDevTools(prefix = "dnxt") {
902
+ if (prefix !== "dnxt" && prefix !== "nxt") {
903
+ throw new Error(`[djs-next] Developer Tools prefix must be either 'dnxt' or 'nxt'. Received: ${prefix}`);
902
904
  }
903
905
  this.on("messageCreate", async (message2) => {
904
- await handleDNXT(message2, this, prefix2);
906
+ await handleDNXT(message2, this, prefix);
905
907
  });
906
- console.log(`[djs-next] \u{1F6E0}\uFE0F Developer Tools enabled. Use "${prefix2}" in chat. Ensure MessageContent intent is enabled!`);
908
+ console.log(`[djs-next] \u{1F6E0}\uFE0F Developer Tools enabled. Use "${prefix}" in chat. Ensure MessageContent intent is enabled!`);
907
909
  }
908
910
  async enableHMR() {
909
911
  try {
@@ -933,27 +935,193 @@ var DJSNextClient = class extends import_discord3.Client {
933
935
  console.warn(`[djs-next] chokidar not installed. HMR is disabled. Please run "npm install chokidar" to use this feature.`);
934
936
  }
935
937
  }
938
+ async handleCommandError(error, commandKey, context) {
939
+ console.error(`[djs-next] Error executing command/component: "${commandKey}"`, error);
940
+ const djs = require("discord.js");
941
+ if (this.config.responses?.errorBoundary === null) {
942
+ } else {
943
+ try {
944
+ const errorContent = this.config.responses?.errorBoundary || `### \u26A0\uFE0F Execution Error
945
+ > The framework encountered a fatal error while executing \`${commandKey}\`.`;
946
+ const codeBlock = `\`\`\`js
947
+ ${String(error.stack || error.message).substring(0, 1500)}
948
+ \`\`\``;
949
+ if ("commandName" in context || "customId" in context) {
950
+ const container = new djs.ContainerBuilder().setAccentColor(15548997).addTextDisplayComponents(new djs.TextDisplayBuilder().setContent(errorContent)).addSeparatorComponents(new djs.SeparatorBuilder()).addTextDisplayComponents(new djs.TextDisplayBuilder().setContent(codeBlock));
951
+ const payload = { components: [container], flags: 32768, ephemeral: true };
952
+ const i = context;
953
+ if (i.isRepliable()) {
954
+ if (i.replied || i.deferred) await i.followUp(payload);
955
+ else await i.reply(payload);
956
+ }
957
+ } else {
958
+ const embed = new djs.EmbedBuilder().setColor(15548997).setDescription(`${errorContent}
959
+
960
+ ${codeBlock}`);
961
+ const m = context;
962
+ await m.reply({ embeds: [embed] });
963
+ }
964
+ } catch (e) {
965
+ }
966
+ }
967
+ if (this.config.errorLogChannelId) {
968
+ try {
969
+ const channel = await this.channels.fetch(this.config.errorLogChannelId);
970
+ if (channel && channel.isTextBased()) {
971
+ const userId = "user" in context ? context.user.id : context.author.id;
972
+ await channel.send(`\u{1F6A8} **Global Error Boundary Caught Exception**
973
+ **Context:** \`${commandKey}\`
974
+ **User:** <@${userId}>
975
+ \`\`\`js
976
+ ${String(error.stack || error.message).substring(0, 1900)}
977
+ \`\`\``);
978
+ }
979
+ } catch (e) {
980
+ console.error(`[djs-next] Failed to send error to log channel:`, e);
981
+ }
982
+ }
983
+ }
984
+ async handlePreconditions(command2, context, commandKey) {
985
+ const userId = "user" in context ? context.user.id : context.author.id;
986
+ const isGuild = "guild" in context && context.guild !== null;
987
+ const member = context.member;
988
+ const sendErr = async (msg) => {
989
+ if (msg === null) return;
990
+ if ("reply" in context && typeof context.reply === "function") {
991
+ try {
992
+ if ("replied" in context && context.isRepliable() && (context.replied || context.deferred)) {
993
+ await context.followUp({ content: msg, ephemeral: true });
994
+ } else {
995
+ await context.reply({ content: msg, ephemeral: true, allowedMentions: { repliedUser: false } });
996
+ }
997
+ } catch (e) {
998
+ }
999
+ }
1000
+ };
1001
+ if ("developerOnly" in command2 && command2.developerOnly && !this._developers.includes(userId)) {
1002
+ await sendErr(this.config.responses?.developerOnly !== void 0 ? this.config.responses.developerOnly : "Only developers can use this command.");
1003
+ return false;
1004
+ }
1005
+ if ("guildOnly" in command2 && command2.guildOnly && !isGuild) {
1006
+ await sendErr(this.config.responses?.guildOnly !== void 0 ? this.config.responses.guildOnly : "This command can only be used in a server.");
1007
+ return false;
1008
+ }
1009
+ if ("userPermissions" in command2 && command2.userPermissions && member?.permissions) {
1010
+ const missing = member.permissions.missing(command2.userPermissions);
1011
+ if (missing.length > 0) {
1012
+ await sendErr(this.config.responses?.missingPerms !== void 0 ? this.config.responses.missingPerms === null ? null : this.config.responses.missingPerms.replace("{perms}", missing.join(", ")) : `You are missing permissions: \`${missing.join(", ")}\``);
1013
+ return false;
1014
+ }
1015
+ }
1016
+ if ("botPermissions" in command2 && command2.botPermissions && context.guild?.members.me?.permissions) {
1017
+ const missing = context.guild.members.me.permissions.missing(command2.botPermissions);
1018
+ if (missing.length > 0) {
1019
+ await sendErr(this.config.responses?.missingPerms !== void 0 ? this.config.responses.missingPerms === null ? null : this.config.responses.missingPerms.replace("{perms}", missing.join(", ")) : `I am missing permissions to run this: \`${missing.join(", ")}\``);
1020
+ return false;
1021
+ }
1022
+ }
1023
+ let totalCooldown = "cooldown" in command2 && command2.cooldown ? command2.cooldown * 1e3 : 0;
1024
+ if (command2.preconditions) {
1025
+ for (const pre of command2.preconditions) {
1026
+ const parts = pre.split(":");
1027
+ const type = parts[0].toLowerCase();
1028
+ const val = parts.slice(1).join(":");
1029
+ if (type === "owneronly" || type === "developeronly") {
1030
+ if (!this._developers.includes(userId)) {
1031
+ await sendErr(this.config.responses?.developerOnly !== void 0 ? this.config.responses.developerOnly : "Only developers can use this command.");
1032
+ return false;
1033
+ }
1034
+ } else if (type === "guildonly") {
1035
+ if (!isGuild) {
1036
+ await sendErr(this.config.responses?.guildOnly !== void 0 ? this.config.responses.guildOnly : "This command can only be used in a server.");
1037
+ return false;
1038
+ }
1039
+ } else if (type === "requireperms") {
1040
+ const perms = val.split(",").map((p) => p.trim());
1041
+ if (member?.permissions) {
1042
+ const missing = member.permissions.missing(perms);
1043
+ if (missing.length > 0) {
1044
+ await sendErr(this.config.responses?.missingPerms !== void 0 ? this.config.responses.missingPerms === null ? null : this.config.responses.missingPerms.replace("{perms}", missing.join(", ")) : `You are missing permissions: \`${missing.join(", ")}\``);
1045
+ return false;
1046
+ }
1047
+ }
1048
+ } else if (type === "cooldown") {
1049
+ const match = val.match(/(\d+)(s|m|h)/);
1050
+ if (match) {
1051
+ let amount = parseInt(match[1]) * 1e3;
1052
+ if (match[2] === "m") amount *= 60;
1053
+ if (match[2] === "h") amount *= 3600;
1054
+ totalCooldown = Math.max(totalCooldown, amount);
1055
+ }
1056
+ }
1057
+ }
1058
+ }
1059
+ if (totalCooldown > 0) {
1060
+ const now = Date.now();
1061
+ if (this.config.cooldownAdapter) {
1062
+ const lastUsed = await this.config.cooldownAdapter.get(commandKey, userId);
1063
+ if (lastUsed) {
1064
+ const expirationTime = lastUsed + totalCooldown;
1065
+ if (now < expirationTime) {
1066
+ const timeStr = `<t:${Math.round(expirationTime / 1e3)}:R>`;
1067
+ await sendErr(this.config.responses?.cooldown !== void 0 ? this.config.responses.cooldown === null ? null : this.config.responses.cooldown.replace("{time}", timeStr) : `Please wait, you are on a cooldown. You can use it again ${timeStr}.`);
1068
+ return false;
1069
+ }
1070
+ }
1071
+ await this.config.cooldownAdapter.set(commandKey, userId, now);
1072
+ } else {
1073
+ if (!this.cooldowns.has(commandKey)) this.cooldowns.set(commandKey, new import_discord4.Collection());
1074
+ const timestamps = this.cooldowns.get(commandKey);
1075
+ if (timestamps.has(userId)) {
1076
+ const expirationTime = timestamps.get(userId) + totalCooldown;
1077
+ if (now < expirationTime) {
1078
+ const timeStr = `<t:${Math.round(expirationTime / 1e3)}:R>`;
1079
+ await sendErr(this.config.responses?.cooldown !== void 0 ? this.config.responses.cooldown === null ? null : this.config.responses.cooldown.replace("{time}", timeStr) : `Please wait, you are on a cooldown. You can use it again ${timeStr}.`);
1080
+ return false;
1081
+ }
1082
+ }
1083
+ timestamps.set(userId, now);
1084
+ setTimeout(() => timestamps.delete(userId), totalCooldown);
1085
+ }
1086
+ }
1087
+ return true;
1088
+ }
936
1089
  };
937
1090
 
938
1091
  // src/utils/paginate.ts
939
- var import_discord4 = require("discord.js");
940
- async function paginate(interaction, pages, time = 6e4) {
941
- if (!interaction.deferred && !interaction.replied) {
942
- await interaction.deferReply();
1092
+ var import_discord5 = require("discord.js");
1093
+ async function paginate(context, pages, time = 6e4) {
1094
+ const isMessage = "author" in context;
1095
+ if (!isMessage) {
1096
+ if (!context.deferred && !context.replied) {
1097
+ await context.deferReply();
1098
+ }
943
1099
  }
944
1100
  if (pages.length === 1) {
945
- return interaction.editReply({ embeds: [pages[0]], components: [] });
1101
+ if (isMessage) {
1102
+ return context.reply({ embeds: [pages[0]], components: [] });
1103
+ } else {
1104
+ return context.editReply({ embeds: [pages[0]], components: [] });
1105
+ }
946
1106
  }
947
1107
  let index = 0;
948
- const prevButton = new import_discord4.ButtonBuilder().setCustomId("djs_prev").setLabel("Previous").setStyle(import_discord4.ButtonStyle.Primary).setDisabled(true);
949
- const nextButton = new import_discord4.ButtonBuilder().setCustomId("djs_next").setLabel("Next").setStyle(import_discord4.ButtonStyle.Primary);
950
- const row = new import_discord4.ActionRowBuilder().addComponents(prevButton, nextButton);
951
- const message2 = await interaction.editReply({
952
- embeds: [pages[index]],
953
- components: [row]
954
- });
1108
+ const prevButton = new import_discord5.ButtonBuilder().setCustomId("djs_prev").setLabel("Previous").setStyle(import_discord5.ButtonStyle.Primary).setDisabled(true);
1109
+ const nextButton = new import_discord5.ButtonBuilder().setCustomId("djs_next").setLabel("Next").setStyle(import_discord5.ButtonStyle.Primary);
1110
+ const row = new import_discord5.ActionRowBuilder().addComponents(prevButton, nextButton);
1111
+ let message2;
1112
+ if (isMessage) {
1113
+ message2 = await context.reply({
1114
+ embeds: [pages[index]],
1115
+ components: [row]
1116
+ });
1117
+ } else {
1118
+ message2 = await context.editReply({
1119
+ embeds: [pages[index]],
1120
+ components: [row]
1121
+ });
1122
+ }
955
1123
  const collector = message2.createMessageComponentCollector({
956
- filter: (i) => i.user.id === interaction.user.id,
1124
+ filter: (i) => i.user.id === (isMessage ? context.author.id : context.user.id),
957
1125
  time
958
1126
  });
959
1127
  collector.on("collect", async (i) => {
@@ -966,24 +1134,144 @@ async function paginate(interaction, pages, time = 6e4) {
966
1134
  nextButton.setDisabled(index === pages.length - 1);
967
1135
  await i.update({
968
1136
  embeds: [pages[index]],
969
- components: [new import_discord4.ActionRowBuilder().addComponents(prevButton, nextButton)]
1137
+ components: [new import_discord5.ActionRowBuilder().addComponents(prevButton, nextButton)]
970
1138
  });
971
1139
  });
972
1140
  collector.on("end", async () => {
973
1141
  prevButton.setDisabled(true);
974
1142
  nextButton.setDisabled(true);
975
- await interaction.editReply({
976
- components: [new import_discord4.ActionRowBuilder().addComponents(prevButton, nextButton)]
977
- }).catch(() => {
978
- });
1143
+ if (message2.editable) {
1144
+ await message2.edit({
1145
+ components: [new import_discord5.ActionRowBuilder().addComponents(prevButton, nextButton)]
1146
+ }).catch(() => {
1147
+ });
1148
+ }
979
1149
  });
980
1150
  }
981
1151
 
1152
+ // src/utils/prompts.ts
1153
+ var import_discord6 = require("discord.js");
1154
+ async function confirmPrompt(context, content2, time = 3e4) {
1155
+ const isMessage = "author" in context;
1156
+ if (!isMessage) {
1157
+ if (!context.deferred && !context.replied) {
1158
+ await context.deferReply();
1159
+ }
1160
+ }
1161
+ const yesButton = new import_discord6.ButtonBuilder().setCustomId("djs_prompt_yes").setLabel("Yes").setStyle(import_discord6.ButtonStyle.Success);
1162
+ const noButton = new import_discord6.ButtonBuilder().setCustomId("djs_prompt_no").setLabel("No").setStyle(import_discord6.ButtonStyle.Danger);
1163
+ const row = new import_discord6.ActionRowBuilder().addComponents(yesButton, noButton);
1164
+ const payload = { components: [row] };
1165
+ if (typeof content2 === "string") {
1166
+ payload.content = content2;
1167
+ } else {
1168
+ payload.embeds = [content2];
1169
+ }
1170
+ let message2;
1171
+ if (isMessage) {
1172
+ message2 = await context.reply(payload);
1173
+ } else {
1174
+ message2 = await context.editReply(payload);
1175
+ }
1176
+ try {
1177
+ const interaction = await message2.awaitMessageComponent({
1178
+ filter: (i) => i.user.id === (isMessage ? context.author.id : context.user.id),
1179
+ time
1180
+ });
1181
+ if (interaction.customId === "djs_prompt_yes") {
1182
+ await interaction.update({ components: [] });
1183
+ return true;
1184
+ } else {
1185
+ await interaction.update({ components: [] });
1186
+ return false;
1187
+ }
1188
+ } catch (error) {
1189
+ if (message2.editable) {
1190
+ await message2.edit({ components: [] }).catch(() => {
1191
+ });
1192
+ }
1193
+ return null;
1194
+ }
1195
+ }
1196
+
1197
+ // src/utils/PaginationBuilder.ts
1198
+ var import_discord7 = require("discord.js");
1199
+ var PaginationBuilder = class {
1200
+ pages = [];
1201
+ timeout = 6e4;
1202
+ constructor(pages) {
1203
+ if (pages) this.pages = pages;
1204
+ }
1205
+ addPage(embed) {
1206
+ this.pages.push(embed);
1207
+ return this;
1208
+ }
1209
+ setPages(pages) {
1210
+ this.pages = pages;
1211
+ return this;
1212
+ }
1213
+ setTimeout(ms) {
1214
+ this.timeout = ms;
1215
+ return this;
1216
+ }
1217
+ async build(target) {
1218
+ if (this.pages.length === 0) throw new Error("[djs-next] PaginationBuilder requires at least one page.");
1219
+ if (this.pages.length === 1) {
1220
+ if (target instanceof import_discord7.CommandInteraction) {
1221
+ if (target.deferred || target.replied) {
1222
+ return await target.editReply({ embeds: [this.pages[0]] });
1223
+ }
1224
+ return await target.reply({ embeds: [this.pages[0]], fetchReply: true });
1225
+ } else {
1226
+ return await target.reply({ embeds: [this.pages[0]] });
1227
+ }
1228
+ }
1229
+ let currentPage = 0;
1230
+ const row = new import_discord7.ActionRowBuilder().addComponents(
1231
+ new import_discord7.ButtonBuilder().setCustomId("prev").setLabel("\u25C0").setStyle(import_discord7.ButtonStyle.Primary).setDisabled(true),
1232
+ new import_discord7.ButtonBuilder().setCustomId("page").setLabel(`1 / ${this.pages.length}`).setStyle(import_discord7.ButtonStyle.Secondary).setDisabled(true),
1233
+ new import_discord7.ButtonBuilder().setCustomId("next").setLabel("\u25B6").setStyle(import_discord7.ButtonStyle.Primary)
1234
+ );
1235
+ let replyMsg;
1236
+ if (target instanceof import_discord7.CommandInteraction) {
1237
+ if (target.deferred || target.replied) {
1238
+ replyMsg = await target.editReply({ embeds: [this.pages[0]], components: [row] });
1239
+ } else {
1240
+ replyMsg = await target.reply({ embeds: [this.pages[0]], components: [row], fetchReply: true });
1241
+ }
1242
+ } else {
1243
+ replyMsg = await target.reply({ embeds: [this.pages[0]], components: [row] });
1244
+ }
1245
+ const userId = target instanceof import_discord7.CommandInteraction ? target.user.id : target.author.id;
1246
+ const collector = replyMsg.createMessageComponentCollector({
1247
+ componentType: import_discord7.ComponentType.Button,
1248
+ time: this.timeout,
1249
+ filter: (i) => i.user.id === userId
1250
+ });
1251
+ collector.on("collect", async (i) => {
1252
+ if (i.customId === "prev") currentPage--;
1253
+ else if (i.customId === "next") currentPage++;
1254
+ const newRow = new import_discord7.ActionRowBuilder().addComponents(
1255
+ new import_discord7.ButtonBuilder().setCustomId("prev").setLabel("\u25C0").setStyle(import_discord7.ButtonStyle.Primary).setDisabled(currentPage === 0),
1256
+ new import_discord7.ButtonBuilder().setCustomId("page").setLabel(`${currentPage + 1} / ${this.pages.length}`).setStyle(import_discord7.ButtonStyle.Secondary).setDisabled(true),
1257
+ new import_discord7.ButtonBuilder().setCustomId("next").setLabel("\u25B6").setStyle(import_discord7.ButtonStyle.Primary).setDisabled(currentPage === this.pages.length - 1)
1258
+ );
1259
+ await i.update({ embeds: [this.pages[currentPage]], components: [newRow] });
1260
+ });
1261
+ collector.on("end", () => {
1262
+ replyMsg.edit({ components: [] }).catch(() => null);
1263
+ });
1264
+ return replyMsg;
1265
+ }
1266
+ };
1267
+
982
1268
  // src/index.ts
983
1269
  __reExport(index_exports, require("discord.js"), module.exports);
984
1270
  // Annotate the CommonJS export names for ESM import in node:
985
1271
  0 && (module.exports = {
986
1272
  DJSNextClient,
1273
+ PaginationBuilder,
1274
+ confirmPrompt,
987
1275
  defineConfig,
988
1276
  getLocalesCache,
989
1277
  loadConfig,