vimcord 1.0.36 → 1.0.37

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
@@ -5,6 +5,9 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
5
5
  throw Error('Dynamic require of "' + x + '" is not supported');
6
6
  });
7
7
 
8
+ // src/modules/validators/permissions.validator.ts
9
+ import { BaseInteraction } from "discord.js";
10
+
8
11
  // src/types/command.base.ts
9
12
  var CommandType = /* @__PURE__ */ ((CommandType2) => {
10
13
  CommandType2[CommandType2["Slash"] = 0] = "Slash";
@@ -32,8 +35,7 @@ var RateLimitScope = /* @__PURE__ */ ((RateLimitScope2) => {
32
35
  return RateLimitScope2;
33
36
  })(RateLimitScope || {});
34
37
 
35
- // src/validators/permissions.validator.ts
36
- import { BaseInteraction } from "discord.js";
38
+ // src/modules/validators/permissions.validator.ts
37
39
  function __existsAndTrue(value) {
38
40
  return value !== void 0 && value;
39
41
  }
@@ -132,8 +134,8 @@ function validateCommandPermissions(permissions, client, user, command) {
132
134
  }
133
135
 
134
136
  // src/builders/baseCommand.builder.ts
135
- import { randomUUID } from "crypto";
136
137
  import _ from "lodash";
138
+ import { randomUUID } from "crypto";
137
139
  var BaseCommandBuilder = class {
138
140
  uuid = randomUUID();
139
141
  commandType;
@@ -423,55 +425,199 @@ var ContextCommandBuilder = class extends BaseCommandBuilder {
423
425
  }
424
426
  };
425
427
 
426
- // src/utils/dir.ts
427
- import path from "path";
428
- import { $ } from "qznt";
429
- function getCallerFileName() {
430
- const stack = new Error().stack?.split("\n");
431
- return stack?.at(4)?.split("at file")?.at(1)?.split("/").at(-1)?.split(":").at(0)?.split(".").at(0);
432
- }
433
- function getProcessDir() {
434
- const mainPath = process.argv[1];
435
- if (!mainPath) return "";
436
- return path.dirname(mainPath);
437
- }
438
- async function importModulesFromDir(dir, suffix) {
439
- const cwd = getProcessDir();
440
- const MODULE_RELATIVE_PATH = path.join(cwd, dir);
441
- const MODULE_LOG_PATH = dir;
442
- const files = $.fs.readDir(MODULE_RELATIVE_PATH).filter((fn) => fn.endsWith(`${suffix ? `.${suffix}` : ""}.js`) || fn.endsWith(`${suffix ? `.${suffix}` : ""}.ts`));
443
- if (!files.length) {
444
- return [];
428
+ // src/builders/event.builder.ts
429
+ import { randomUUID as randomUUID2 } from "crypto";
430
+ import _3 from "lodash";
431
+
432
+ // src/tools/Logger.ts
433
+ import chalk from "chalk";
434
+ var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
435
+ LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
436
+ LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
437
+ LogLevel2[LogLevel2["SUCCESS"] = 2] = "SUCCESS";
438
+ LogLevel2[LogLevel2["WARN"] = 3] = "WARN";
439
+ LogLevel2[LogLevel2["ERROR"] = 4] = "ERROR";
440
+ return LogLevel2;
441
+ })(LogLevel || {});
442
+ var LOGGER_COLORS = {
443
+ primary: "#5865F2",
444
+ success: "#57F287",
445
+ warn: "#FEE75C",
446
+ danger: "#ED4245",
447
+ muted: "#747F8D",
448
+ text: "#FFFFFF"
449
+ };
450
+ var Logger = class {
451
+ logPrefixEmoji;
452
+ logPrefix;
453
+ minLevel;
454
+ showTimestamp;
455
+ colorScheme;
456
+ constructor(options) {
457
+ const { prefixEmoji = null, prefix = null, minLevel = 0 /* DEBUG */, showTimestamp = true } = options || {};
458
+ this.logPrefixEmoji = prefixEmoji;
459
+ this.logPrefix = prefix;
460
+ this.minLevel = minLevel;
461
+ this.showTimestamp = showTimestamp;
462
+ this.colorScheme = {
463
+ ...LOGGER_COLORS,
464
+ ...options?.colors
465
+ };
445
466
  }
446
- const modules = await Promise.all(
447
- files.map(async (fn) => {
448
- let _path = path.join(MODULE_RELATIVE_PATH, fn);
449
- let _logPath = `./${path.join(MODULE_LOG_PATH, fn)}`;
450
- let _module;
451
- try {
452
- delete __require.cache[__require.resolve(_path)];
453
- _module = __require(_path);
454
- } catch (err) {
455
- console.warn(`Failed to import module at '${_logPath}'`, err);
456
- _module = null;
467
+ formatTimestamp() {
468
+ if (!this.showTimestamp) return "";
469
+ const now = /* @__PURE__ */ new Date();
470
+ const time = now.toLocaleTimeString("en-US", {
471
+ hour12: false,
472
+ hour: "2-digit",
473
+ minute: "2-digit",
474
+ second: "2-digit"
475
+ });
476
+ return chalk.hex(this.colorScheme.muted)(`[${time}]`);
477
+ }
478
+ formatPrefix() {
479
+ if (!this.logPrefix) return "";
480
+ return chalk.bold.hex(this.colorScheme.primary)(
481
+ `${this.logPrefixEmoji ? `${this.logPrefixEmoji} ` : ""}${this.logPrefix}`
482
+ );
483
+ }
484
+ shouldLog(level) {
485
+ return level >= this.minLevel;
486
+ }
487
+ get prefixEmoji() {
488
+ return this.logPrefixEmoji;
489
+ }
490
+ get prefix() {
491
+ return this.logPrefix;
492
+ }
493
+ get colors() {
494
+ return this.colorScheme;
495
+ }
496
+ extend(extras) {
497
+ for (const [key, fn] of Object.entries(extras)) {
498
+ if (typeof fn === "function") {
499
+ this[key] = function(...args) {
500
+ return fn.call(this, ...args);
501
+ };
457
502
  }
458
- return { module: _module, path: _logPath };
459
- })
460
- );
461
- const filteredModules = modules.filter((m) => m.module);
462
- if (!filteredModules.length) {
463
- console.warn(`No valid modules were found in directory '${dir}'`);
503
+ }
504
+ return this;
464
505
  }
465
- return filteredModules;
466
- }
506
+ setPrefix(prefix) {
507
+ this.logPrefix = prefix;
508
+ return this;
509
+ }
510
+ setPrefixEmoji(prefixEmoji) {
511
+ this.logPrefixEmoji = prefixEmoji;
512
+ return this;
513
+ }
514
+ setMinLevel(minLevel) {
515
+ this.minLevel = minLevel;
516
+ return this;
517
+ }
518
+ setShowTimestamp(show) {
519
+ this.showTimestamp = show;
520
+ return this;
521
+ }
522
+ setColors(colors) {
523
+ this.colorScheme = {
524
+ ...LOGGER_COLORS,
525
+ ...colors
526
+ };
527
+ return this;
528
+ }
529
+ log(message, ...args) {
530
+ console.log(this.formatTimestamp(), this.formatPrefix(), message, ...args);
531
+ }
532
+ debug(message, ...args) {
533
+ if (!this.shouldLog(0 /* DEBUG */)) return;
534
+ console.log(
535
+ this.formatTimestamp(),
536
+ this.formatPrefix(),
537
+ chalk.hex(this.colorScheme.muted)("DEBUG"),
538
+ chalk.dim(message),
539
+ ...args
540
+ );
541
+ }
542
+ info(message, ...args) {
543
+ if (!this.shouldLog(1 /* INFO */)) return;
544
+ console.log(this.formatTimestamp(), this.formatPrefix(), chalk.hex("#87CEEB")("INFO"), message, ...args);
545
+ }
546
+ success(message, ...args) {
547
+ if (!this.shouldLog(2 /* SUCCESS */)) return;
548
+ console.log(
549
+ this.formatTimestamp(),
550
+ this.formatPrefix(),
551
+ chalk.bold.hex(this.colorScheme.success)("\u2713 SUCCESS"),
552
+ chalk.hex(this.colorScheme.success)(message),
553
+ ...args
554
+ );
555
+ }
556
+ warn(message, ...args) {
557
+ if (!this.shouldLog(3 /* WARN */)) return;
558
+ console.warn(
559
+ this.formatTimestamp(),
560
+ this.formatPrefix(),
561
+ chalk.bold.hex(this.colorScheme.warn)("\u26A0 WARN"),
562
+ chalk.hex(this.colorScheme.warn)(message),
563
+ ...args
564
+ );
565
+ }
566
+ error(message, error, ...args) {
567
+ if (!this.shouldLog(4 /* ERROR */)) return;
568
+ console.error(
569
+ this.formatTimestamp(),
570
+ this.formatPrefix(),
571
+ chalk.bold.hex(this.colorScheme.danger)("\u2715 ERROR"),
572
+ chalk.hex(this.colorScheme.danger)(message),
573
+ ...args
574
+ );
575
+ if (error && error.stack) {
576
+ console.error(chalk.dim(error.stack));
577
+ }
578
+ }
579
+ loader(message) {
580
+ const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
581
+ let i = 0;
582
+ const interval = setInterval(() => {
583
+ process.stdout.write(
584
+ `\r${this.formatTimestamp()} ${this.formatPrefix()} ${chalk.hex(this.colorScheme.warn)(frames[i])} ${message}`
585
+ );
586
+ i = (i + 1) % frames.length;
587
+ }, 100);
588
+ return (newMessage) => {
589
+ clearInterval(interval);
590
+ process.stdout.write(
591
+ `\r${this.formatTimestamp()} ${this.formatPrefix()} ${chalk.hex(this.colorScheme.success)("\u2713")} ${newMessage || message}
592
+ `
593
+ );
594
+ };
595
+ }
596
+ table(title, data) {
597
+ console.log(this.formatTimestamp(), this.formatPrefix(), chalk.bold(title));
598
+ Object.entries(data).forEach(([key, value]) => {
599
+ const formattedKey = chalk.hex(this.colorScheme.warn)(` ${key}`);
600
+ const formattedValue = chalk.hex(this.colorScheme.muted)(value);
601
+ console.log(`${formattedKey.padEnd(25)} ${formattedValue}`);
602
+ });
603
+ }
604
+ section(title) {
605
+ const line = "\u2500".repeat(Math.max(30, title.length + 4));
606
+ console.log(chalk.hex(this.colorScheme.muted)(`
607
+ \u250C\u2500${line}\u2500\u2510`));
608
+ console.log(
609
+ chalk.hex(this.colorScheme.muted)("\u2502 ") + chalk.bold.hex(this.colorScheme.text)(title.padEnd(line.length)) + chalk.hex(this.colorScheme.muted)(" \u2502")
610
+ );
611
+ console.log(chalk.hex(this.colorScheme.muted)(`\u2514\u2500${line}\u2500\u2518`));
612
+ }
613
+ };
614
+ var logger = new Logger();
467
615
 
468
616
  // src/builders/event.builder.ts
469
- import { randomUUID as randomUUID2 } from "crypto";
470
- import _3 from "lodash";
471
617
  var EventBuilder = class _EventBuilder {
472
618
  uuid = randomUUID2();
473
619
  event;
474
- name = getCallerFileName() || this.uuid;
620
+ name = this.uuid;
475
621
  enabled;
476
622
  once;
477
623
  priority;
@@ -636,6 +782,7 @@ var EventBuilder = class _EventBuilder {
636
782
  return;
637
783
  }
638
784
  if (this.isRateLimited()) {
785
+ logger.warn(`Event '${this.name}' (${this.event}) is rate limited`);
639
786
  if (this.rateLimit?.onRateLimit) {
640
787
  return await this.rateLimit.onRateLimit(...args);
641
788
  }
@@ -656,7 +803,7 @@ var EventBuilder = class _EventBuilder {
656
803
  if (this.onError) {
657
804
  return await this.onError(err, ...args);
658
805
  }
659
- console.error(`Error in event '${this.name}':`, err);
806
+ logger.error(`Event execution error '${this.name}' (${this.event}):`, err);
660
807
  throw err;
661
808
  }
662
809
  }
@@ -822,22 +969,120 @@ var SlashCommandBuilder = class extends BaseCommandBuilder {
822
969
  }
823
970
  };
824
971
 
825
- // src/client.ts
826
- import { Client as Client2 } from "discord.js";
827
- import dotEnv from "dotenv";
972
+ // src/modules/builtins/context-command.builtins.ts
973
+ var contextCommandHandler = new EventBuilder({
974
+ event: "interactionCreate",
975
+ name: "ContextCommandHandler",
976
+ async execute(client, interaction) {
977
+ if (!interaction.isContextMenuCommand()) return;
978
+ const command = client.commands.context.get(interaction.commandName);
979
+ if (!command) {
980
+ const content = `**${interaction.commandName}** is not a registered context command.`;
981
+ if (interaction.replied || interaction.deferred) {
982
+ return interaction.followUp({ content, flags: "Ephemeral" });
983
+ }
984
+ return interaction.reply({ content, flags: "Ephemeral" });
985
+ }
986
+ try {
987
+ return await command.run(client, client, interaction);
988
+ } catch (err) {
989
+ await client.error.handleCommandError(err, interaction.guild, interaction);
990
+ }
991
+ }
992
+ });
828
993
 
829
- // src/configs/tools.config.ts
830
- import _5 from "lodash";
831
- var globalToolsConfig = {
832
- devMode: false,
833
- embedColor: [],
834
- embedColorDev: [],
835
- timeouts: {
836
- collectorTimeout: 6e4,
837
- collectorIdle: 6e4,
838
- pagination: 6e4,
839
- prompt: 3e4,
840
- modalSubmit: 6e4
994
+ // src/modules/builtins/prefix-command.builtins.ts
995
+ import { userMention } from "discord.js";
996
+ var prefixCommandHandler = new EventBuilder({
997
+ event: "messageCreate",
998
+ name: "PrefixCommandHandler",
999
+ async execute(client, message) {
1000
+ if (message.author.bot || !message.guild) return;
1001
+ const config = client.config.prefixCommands;
1002
+ let activePrefix = config.defaultPrefix;
1003
+ if (config.guildPrefixResolver) {
1004
+ try {
1005
+ const customPrefix = await config.guildPrefixResolver(client, message.guild.id);
1006
+ if (customPrefix) activePrefix = customPrefix;
1007
+ } catch (err) {
1008
+ client.logger.error(`Error in guildPrefixResolver for guild ${message.guild.id}:`, err);
1009
+ }
1010
+ }
1011
+ let prefixUsed;
1012
+ if (message.content.startsWith(activePrefix)) {
1013
+ prefixUsed = activePrefix;
1014
+ } else if (config.allowMentionAsPrefix) {
1015
+ const mention = userMention(client.user.id);
1016
+ if (message.content.startsWith(mention)) {
1017
+ prefixUsed = message.content.startsWith(`${mention} `) ? `${mention} ` : mention;
1018
+ }
1019
+ }
1020
+ if (!prefixUsed) return;
1021
+ const contentWithoutPrefix = message.content.slice(prefixUsed.length).trim();
1022
+ const args = contentWithoutPrefix.split(/\s+/);
1023
+ const trigger = args.shift();
1024
+ if (!trigger) return;
1025
+ const command = client.commands.prefix.get(trigger);
1026
+ if (!command) return;
1027
+ message.content = args.join(" ");
1028
+ try {
1029
+ return await command.run(client, client, message);
1030
+ } catch (err) {
1031
+ await client.error.handleCommandError(err, message.guild, message);
1032
+ }
1033
+ }
1034
+ });
1035
+
1036
+ // src/modules/builtins/slash-command.builtins.ts
1037
+ var slashCommandHandler = new EventBuilder({
1038
+ event: "interactionCreate",
1039
+ name: "SlashCommandHandler",
1040
+ async execute(client, interaction) {
1041
+ if (!interaction.isChatInputCommand()) return;
1042
+ const command = client.commands.slash.get(interaction.commandName);
1043
+ if (!command) {
1044
+ const content = `**/\`${interaction.commandName}\`** is not a registered command.`;
1045
+ if (interaction.replied || interaction.deferred) {
1046
+ return interaction.followUp({ content, flags: "Ephemeral" });
1047
+ }
1048
+ return interaction.reply({ content, flags: "Ephemeral" });
1049
+ }
1050
+ try {
1051
+ return await command.run(client, client, interaction);
1052
+ } catch (err) {
1053
+ await client.error.handleCommandError(err, interaction.guild, interaction);
1054
+ }
1055
+ }
1056
+ });
1057
+
1058
+ // src/tools/BetterEmbed.ts
1059
+ import {
1060
+ EmbedBuilder,
1061
+ GuildMember as GuildMember3,
1062
+ User as User3
1063
+ } from "discord.js";
1064
+
1065
+ // src/utils/config.factory.ts
1066
+ import _5 from "lodash";
1067
+ function createConfigFactory(defaultConfig6, validate) {
1068
+ return (options = {}, existing) => {
1069
+ const result = _5.merge({}, defaultConfig6, existing, options);
1070
+ validate?.(result);
1071
+ return result;
1072
+ };
1073
+ }
1074
+
1075
+ // src/configs/tools.config.ts
1076
+ var globalToolsConfig = {
1077
+ devMode: false,
1078
+ embedColor: [],
1079
+ embedColorDev: [],
1080
+ timeouts: {
1081
+ collectorTimeout: 6e4,
1082
+ collectorIdle: 6e4,
1083
+ pagination: 6e4,
1084
+ prompt: 3e4,
1085
+ modalSubmit: 6e4
841
1086
  },
842
1087
  collector: {
843
1088
  notAParticipantMessage: "You are not allowed to use this.",
@@ -863,103 +1108,10 @@ var globalToolsConfig = {
863
1108
  rejectLabel: "Cancel"
864
1109
  }
865
1110
  };
866
- function defineGlobalToolsConfig(options) {
867
- Object.assign(globalToolsConfig, _5.merge(globalToolsConfig, options));
868
- }
869
- function createToolsConfig(options) {
870
- return _5.merge(globalToolsConfig, options);
871
- }
872
-
873
- // src/configs/app.config.ts
874
- import _6 from "lodash";
875
- var defaultConfig = {
876
- devMode: process.argv.includes("--dev"),
877
- name: "Discord Bot",
878
- appVersion: "1.0.0",
879
- verbose: false,
880
- disableBanner: false,
881
- moduleSuffixes: {
882
- slashCommand: "slash",
883
- contextCommand: "ctx",
884
- prefixCommand: "prefix",
885
- event: "event"
886
- }
887
- };
888
- function createAppConfig(options = {}) {
889
- return _6.merge(defaultConfig, options);
890
- }
891
-
892
- // src/configs/staff.config.ts
893
- import _7 from "lodash";
894
- var defaultConfig2 = {
895
- ownerId: null,
896
- superUsers: [],
897
- superUserRoles: [],
898
- bypassers: [],
899
- bypassesGuildAdmin: {
900
- allBotStaff: false,
901
- botOwner: false,
902
- superUsers: false,
903
- bypassers: false
904
- },
905
- guild: {
906
- id: null,
907
- inviteUrl: null,
908
- channels: {}
909
- }
910
- };
911
- function createStaffConfig(options = {}) {
912
- return _7.merge(defaultConfig2, options);
913
- }
914
-
915
- // src/configs/slashCommand.config.ts
916
- import _8 from "lodash";
917
- var defaultConfig3 = {
918
- logExecution: true
919
- };
920
- function createSlashCommandConfig(options = {}) {
921
- return _8.merge(defaultConfig3, options);
922
- }
923
-
924
- // src/configs/prefixCommand.config.ts
925
- import _9 from "lodash";
926
- var defaultConfig4 = {
927
- enabled: true,
928
- defaultPrefix: "!",
929
- allowMentionAsPrefix: true,
930
- allowCaseInsensitiveCommandNames: true,
931
- logExecution: true
932
- };
933
- function createPrefixCommandConfig(options = {}) {
934
- return _9.merge(defaultConfig4, options);
935
- }
936
-
937
- // src/configs/contextCommand.config.ts
938
- import _10 from "lodash";
939
- var defaultConfig5 = {
940
- enabled: true,
941
- logExecution: true
1111
+ var createToolsConfig = createConfigFactory(globalToolsConfig);
1112
+ var defineGlobalToolsConfig = (options) => {
1113
+ Object.assign(globalToolsConfig, createToolsConfig(options, globalToolsConfig));
942
1114
  };
943
- function createContextCommandConfig(options = {}) {
944
- return _10.merge(defaultConfig5, options);
945
- }
946
-
947
- // src/utils/sendCommandErrorEmbed.ts
948
- import {
949
- ActionRowBuilder as ActionRowBuilder2,
950
- AttachmentBuilder,
951
- ButtonBuilder,
952
- ButtonStyle,
953
- ComponentType,
954
- Message as Message3
955
- } from "discord.js";
956
-
957
- // src/tools/BetterEmbed.ts
958
- import {
959
- EmbedBuilder,
960
- GuildMember as GuildMember3,
961
- User as User3
962
- } from "discord.js";
963
1115
 
964
1116
  // src/tools/dynaSend.ts
965
1117
  import {
@@ -1415,7 +1567,15 @@ var BetterEmbed = class _BetterEmbed {
1415
1567
  }
1416
1568
  };
1417
1569
 
1418
- // src/utils/sendCommandErrorEmbed.ts
1570
+ // src/utils/command-error.utils.ts
1571
+ import {
1572
+ ActionRowBuilder as ActionRowBuilder2,
1573
+ AttachmentBuilder,
1574
+ ButtonBuilder,
1575
+ ButtonStyle,
1576
+ ComponentType,
1577
+ Message as Message3
1578
+ } from "discord.js";
1419
1579
  async function sendCommandErrorEmbed(client, error, guild, messageOrInteraction) {
1420
1580
  if (!client.features.enableCommandErrorMessage) return null;
1421
1581
  const config = typeof client.features.enableCommandErrorMessage !== "boolean" ? client.features.enableCommandErrorMessage : void 0;
@@ -1440,7 +1600,8 @@ async function sendCommandErrorEmbed(client, error, guild, messageOrInteraction)
1440
1600
  title: "Something went wrong",
1441
1601
  description: "If you keep encountering this error, please report it."
1442
1602
  });
1443
- const msg = await embed_error.send(messageOrInteraction, {
1603
+ const msg = await dynaSend(messageOrInteraction, {
1604
+ embeds: [embed_error],
1444
1605
  components: [actionRow],
1445
1606
  flags: config?.ephemeral ? "Ephemeral" : void 0,
1446
1607
  deleteAfter: config?.deleteAfter
@@ -1461,7 +1622,8 @@ ${error.stack}`), {
1461
1622
  });
1462
1623
  collector.on("end", () => {
1463
1624
  buttons.details.setDisabled(true);
1464
- embed_error.send(messageOrInteraction, {
1625
+ dynaSend(messageOrInteraction, {
1626
+ embeds: [embed_error],
1465
1627
  sendMethod: messageOrInteraction instanceof Message3 ? 5 /* MessageEdit */ : void 0,
1466
1628
  components: [actionRow]
1467
1629
  });
@@ -1469,529 +1631,326 @@ ${error.stack}`), {
1469
1631
  return msg;
1470
1632
  }
1471
1633
 
1472
- // src/modules/builtins/builtin.slashCommandHandler.ts
1473
- var BUILTIN_SlashCommandHandler = new EventBuilder({
1474
- event: "interactionCreate",
1475
- name: "SlashCommandHandler",
1476
- async execute(client, interaction) {
1477
- if (!interaction.isChatInputCommand()) return;
1478
- const command = client.commands.slash.get(interaction.commandName);
1479
- if (!command) {
1480
- const content = `**/\`${interaction.commandName}\`** is not a registered command.`;
1481
- if (interaction.replied || interaction.deferred) {
1482
- return interaction.followUp({ content, flags: "Ephemeral" });
1483
- }
1484
- return interaction.reply({ content, flags: "Ephemeral" });
1485
- }
1486
- try {
1487
- return await command.run(client, client, interaction);
1488
- } catch (err) {
1489
- await sendCommandErrorEmbed(client, err, interaction.guild, interaction);
1490
- throw err;
1491
- }
1634
+ // src/client/error-handler.ts
1635
+ var ErrorHandler = class {
1636
+ client;
1637
+ constructor(client) {
1638
+ this.client = client;
1492
1639
  }
1493
- });
1640
+ /** Handles command errors - sends error embed to user, then rethrows. */
1641
+ async handleCommandError(error, guild, messageOrInteraction) {
1642
+ await sendCommandErrorEmbed(this.client, error, guild, messageOrInteraction);
1643
+ throw error;
1644
+ }
1645
+ /** Handles internal Vimcord errors - logs with [Vimcord] prefix. */
1646
+ handleVimcordError(error, context) {
1647
+ this.client.logger.error(`[Vimcord] [${context}]`, error);
1648
+ }
1649
+ /** Sets up global process error handlers. */
1650
+ setupGlobalHandlers() {
1651
+ process.on("uncaughtException", (err) => this.handleVimcordError(err, "Uncaught Exception"));
1652
+ process.on("unhandledRejection", (err) => this.handleVimcordError(err, "Unhandled Rejection"));
1653
+ process.on("exit", (code) => this.client.logger.debug(`Process exited with code ${code}`));
1654
+ this.client.on("error", (err) => this.handleVimcordError(err, "Client Error"));
1655
+ this.client.on("shardError", (err) => this.handleVimcordError(err, "Client Shard Error"));
1656
+ }
1657
+ };
1494
1658
 
1495
- // src/modules/builtins/builtin.prefixCommandHandler.ts
1496
- import { userMention } from "discord.js";
1497
- var BUILTIN_PrefixCommandHandler = new EventBuilder({
1498
- event: "messageCreate",
1499
- name: "PrefixCommandHandler",
1500
- async execute(client, message) {
1501
- if (message.author.bot || !message.guild) return;
1502
- const config = client.config.prefixCommands;
1503
- let activePrefix = config.defaultPrefix;
1504
- if (config.guildPrefixResolver) {
1505
- try {
1506
- const customPrefix = await config.guildPrefixResolver(client, message.guild.id);
1507
- if (customPrefix) activePrefix = customPrefix;
1508
- } catch (err) {
1509
- client.logger.error(`Error in guildPrefixResolver for guild ${message.guild.id}:`, err);
1510
- }
1511
- }
1512
- let prefixUsed;
1513
- if (message.content.startsWith(activePrefix)) {
1514
- prefixUsed = activePrefix;
1515
- } else if (config.allowMentionAsPrefix) {
1516
- const mention = userMention(client.user.id);
1517
- if (message.content.startsWith(mention)) {
1518
- prefixUsed = message.content.startsWith(`${mention} `) ? `${mention} ` : mention;
1519
- }
1520
- }
1521
- if (!prefixUsed) return;
1522
- const contentWithoutPrefix = message.content.slice(prefixUsed.length).trim();
1523
- const args = contentWithoutPrefix.split(/\s+/);
1524
- const trigger = args.shift();
1525
- if (!trigger) return;
1526
- const command = client.commands.prefix.get(trigger);
1527
- if (!command) return;
1528
- message.content = args.join(" ");
1529
- try {
1530
- return await command.run(client, client, message);
1531
- } catch (err) {
1532
- await sendCommandErrorEmbed(client, err, message.guild, message);
1533
- throw err;
1534
- }
1659
+ // src/client/vimcord.logger.ts
1660
+ import chalk2 from "chalk";
1661
+
1662
+ // package.json
1663
+ var version = "1.0.37";
1664
+
1665
+ // src/client/vimcord.logger.ts
1666
+ var clientLoggerFactory = (client) => new Logger({ prefixEmoji: "\u26A1", prefix: `vimcord (i${client.clientId})` }).extend({
1667
+ clientBanner(client2) {
1668
+ if (client2.config.app.disableBanner) return;
1669
+ const border = "\u2550".repeat(50);
1670
+ console.log(chalk2.hex(this.colors.primary)(`
1671
+ \u2554${border}\u2557`));
1672
+ console.log(
1673
+ chalk2.hex(this.colors.primary)("\u2551") + chalk2.bold.hex(this.colors.text)(
1674
+ ` \u{1F680} ${client2.config.app.name} v${client2.$version}`.padEnd(50 - (client2.$devMode ? 12 : 0))
1675
+ ) + chalk2.hex(this.colors.primary)(`${client2.$devMode ? chalk2.hex(this.colors.warn)("devMode \u26A0\uFE0F ") : ""}\u2551`)
1676
+ );
1677
+ console.log(chalk2.hex(this.colors.primary)(`\u2551${"".padEnd(50)}\u2551`));
1678
+ console.log(
1679
+ chalk2.hex(this.colors.primary)("\u2551") + chalk2.hex(this.colors.muted)(
1680
+ ` # Powered by Vimcord v${version}`.padEnd(50 - 3 - `${client2.clientId}`.length)
1681
+ ) + chalk2.hex(this.colors.primary)(`${chalk2.hex(this.colors.muted)(`i${client2.clientId}`)} \u2551`)
1682
+ );
1683
+ console.log(chalk2.hex(this.colors.primary)(`\u255A${border}\u255D
1684
+ `));
1685
+ },
1686
+ clientReady(clientTag, guildCount) {
1687
+ console.log(
1688
+ this.formatTimestamp(),
1689
+ this.formatPrefix(),
1690
+ chalk2.hex(this.colors.success)("\u{1F916} READY"),
1691
+ chalk2.white(`Connected as ${chalk2.bold.hex(this.colors.primary)(clientTag)}`),
1692
+ chalk2.hex(this.colors.muted)(`\u2022 ${guildCount} guilds`)
1693
+ );
1694
+ },
1695
+ moduleLoaded(moduleName, count, ignoredCount) {
1696
+ const countText = count ? chalk2.hex(this.colors.muted)(`(${count} items)`) : "";
1697
+ console.log(
1698
+ this.formatTimestamp(),
1699
+ this.formatPrefix(),
1700
+ chalk2.hex("#9B59B6")("\u{1F4E6} MODULE"),
1701
+ chalk2.hex(this.colors.warn)(`${moduleName} loaded`),
1702
+ ignoredCount ? chalk2.hex(this.colors.muted)(`(${ignoredCount} ignored)`) : "",
1703
+ countText
1704
+ );
1705
+ },
1706
+ commandExecuted(commandName, username, guildName) {
1707
+ const location = guildName ? `in ${chalk2.hex(this.colors.muted)(guildName)}` : "in DMs";
1708
+ console.log(
1709
+ this.formatTimestamp(),
1710
+ this.formatPrefix(),
1711
+ chalk2.hex("#87CEEB")("\u{1F4DD} COMMAND"),
1712
+ chalk2.hex(this.colors.warn)(`/${commandName}`),
1713
+ chalk2.white(`used by ${chalk2.bold(username)}`),
1714
+ chalk2.hex(this.colors.muted)(location)
1715
+ );
1716
+ },
1717
+ database(action, details) {
1718
+ console.log(
1719
+ this.formatTimestamp(),
1720
+ this.formatPrefix(),
1721
+ chalk2.hex("#FF6B9D")("\u{1F5C4}\uFE0F DATABASE"),
1722
+ chalk2.white(action),
1723
+ details ? chalk2.hex(this.colors.muted)(details) : ""
1724
+ );
1535
1725
  }
1536
1726
  });
1537
1727
 
1538
- // src/modules/builtins/builtin.contextCommandHandler.ts
1539
- var BUILTIN_ContextCommandHandler = new EventBuilder({
1540
- event: "interactionCreate",
1541
- name: "ContextCommandHandler",
1542
- async execute(client, interaction) {
1543
- if (!interaction.isContextMenuCommand()) return;
1544
- const command = client.commands.context.get(interaction.commandName);
1545
- if (!command) {
1546
- const content = `**${interaction.commandName}** is not a registered context command.`;
1547
- if (interaction.replied || interaction.deferred) {
1548
- return interaction.followUp({ content, flags: "Ephemeral" });
1549
- }
1550
- return interaction.reply({ content, flags: "Ephemeral" });
1551
- }
1552
- try {
1553
- return await command.run(client, client, interaction);
1554
- } catch (err) {
1555
- await sendCommandErrorEmbed(client, err, interaction.guild, interaction);
1556
- throw err;
1557
- }
1558
- }
1728
+ // src/utils/process.utils.ts
1729
+ import { readFileSync } from "fs";
1730
+ import { join } from "path";
1731
+ function getPackageJson() {
1732
+ return JSON.parse(readFileSync(join(process.cwd(), "package.json"), "utf-8"));
1733
+ }
1734
+ function getDevMode() {
1735
+ return process.argv.includes("--dev");
1736
+ }
1737
+
1738
+ // src/configs/app.config.ts
1739
+ var defaultConfig = {
1740
+ name: "Discord Bot",
1741
+ version: getPackageJson()?.version ?? "1.0.0",
1742
+ devMode: getDevMode(),
1743
+ verbose: false,
1744
+ enableCLI: false,
1745
+ disableBanner: false
1746
+ };
1747
+ var createAppConfig = createConfigFactory(defaultConfig, (config) => {
1748
+ if (!config.name) throw new Error("App name is required");
1749
+ defineGlobalToolsConfig({ devMode: config.devMode });
1559
1750
  });
1560
1751
 
1561
- // src/tools/Logger.ts
1562
- import chalk from "chalk";
1563
- var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
1564
- LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
1565
- LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
1566
- LogLevel2[LogLevel2["SUCCESS"] = 2] = "SUCCESS";
1567
- LogLevel2[LogLevel2["WARN"] = 3] = "WARN";
1568
- LogLevel2[LogLevel2["ERROR"] = 4] = "ERROR";
1569
- return LogLevel2;
1570
- })(LogLevel || {});
1571
- var LOGGER_COLORS = {
1572
- primary: "#5865F2",
1573
- success: "#57F287",
1574
- warn: "#FEE75C",
1575
- danger: "#ED4245",
1576
- muted: "#747F8D",
1577
- text: "#FFFFFF"
1752
+ // src/configs/contextCommand.config.ts
1753
+ var defaultConfig2 = {
1754
+ enabled: true,
1755
+ logExecution: true
1578
1756
  };
1579
- var Logger = class {
1580
- logPrefixEmoji;
1581
- logPrefix;
1582
- minLevel;
1583
- showTimestamp;
1584
- colorScheme;
1585
- constructor(options) {
1586
- const { prefixEmoji = null, prefix = null, minLevel = 0 /* DEBUG */, showTimestamp = true } = options || {};
1587
- this.logPrefixEmoji = prefixEmoji;
1588
- this.logPrefix = prefix;
1589
- this.minLevel = minLevel;
1590
- this.showTimestamp = showTimestamp;
1591
- this.colorScheme = {
1592
- ...LOGGER_COLORS,
1593
- ...options?.colors
1594
- };
1595
- }
1596
- formatTimestamp() {
1597
- if (!this.showTimestamp) return "";
1598
- const now = /* @__PURE__ */ new Date();
1599
- const time = now.toLocaleTimeString("en-US", {
1600
- hour12: false,
1601
- hour: "2-digit",
1602
- minute: "2-digit",
1603
- second: "2-digit"
1604
- });
1605
- return chalk.hex(this.colorScheme.muted)(`[${time}]`);
1606
- }
1607
- formatPrefix() {
1608
- if (!this.logPrefix) return "";
1609
- return chalk.bold.hex(this.colorScheme.primary)(
1610
- `${this.logPrefixEmoji ? `${this.logPrefixEmoji} ` : ""}${this.logPrefix}`
1611
- );
1612
- }
1613
- shouldLog(level) {
1614
- return level >= this.minLevel;
1615
- }
1616
- get prefixEmoji() {
1617
- return this.logPrefixEmoji;
1618
- }
1619
- get prefix() {
1620
- return this.logPrefix;
1621
- }
1622
- get colors() {
1623
- return this.colorScheme;
1624
- }
1625
- extend(extras) {
1626
- for (const [key, fn] of Object.entries(extras)) {
1627
- if (typeof fn === "function") {
1628
- this[key] = function(...args) {
1629
- return fn.call(this, ...args);
1630
- };
1631
- }
1632
- }
1633
- return this;
1634
- }
1635
- setPrefix(prefix) {
1636
- this.logPrefix = prefix;
1637
- return this;
1638
- }
1639
- setPrefixEmoji(prefixEmoji) {
1640
- this.logPrefixEmoji = prefixEmoji;
1641
- return this;
1642
- }
1643
- setMinLevel(minLevel) {
1644
- this.minLevel = minLevel;
1645
- return this;
1646
- }
1647
- setShowTimestamp(show) {
1648
- this.showTimestamp = show;
1649
- return this;
1650
- }
1651
- setColors(colors) {
1652
- this.colorScheme = {
1653
- ...LOGGER_COLORS,
1654
- ...colors
1655
- };
1656
- return this;
1657
- }
1658
- log(message, ...args) {
1659
- console.log(this.formatTimestamp(), this.formatPrefix(), message, ...args);
1660
- }
1661
- debug(message, ...args) {
1662
- if (!this.shouldLog(0 /* DEBUG */)) return;
1663
- console.log(
1664
- this.formatTimestamp(),
1665
- this.formatPrefix(),
1666
- chalk.hex(this.colorScheme.muted)("DEBUG"),
1667
- chalk.dim(message),
1668
- ...args
1669
- );
1670
- }
1671
- info(message, ...args) {
1672
- if (!this.shouldLog(1 /* INFO */)) return;
1673
- console.log(this.formatTimestamp(), this.formatPrefix(), chalk.hex("#87CEEB")("INFO"), message, ...args);
1674
- }
1675
- success(message, ...args) {
1676
- if (!this.shouldLog(2 /* SUCCESS */)) return;
1677
- console.log(
1678
- this.formatTimestamp(),
1679
- this.formatPrefix(),
1680
- chalk.bold.hex(this.colorScheme.success)("\u2713 SUCCESS"),
1681
- chalk.hex(this.colorScheme.success)(message),
1682
- ...args
1683
- );
1684
- }
1685
- warn(message, ...args) {
1686
- if (!this.shouldLog(3 /* WARN */)) return;
1687
- console.warn(
1688
- this.formatTimestamp(),
1689
- this.formatPrefix(),
1690
- chalk.bold.hex(this.colorScheme.warn)("\u26A0 WARN"),
1691
- chalk.hex(this.colorScheme.warn)(message),
1692
- ...args
1693
- );
1694
- }
1695
- error(message, error, ...args) {
1696
- if (!this.shouldLog(4 /* ERROR */)) return;
1697
- console.error(
1698
- this.formatTimestamp(),
1699
- this.formatPrefix(),
1700
- chalk.bold.hex(this.colorScheme.danger)("\u2715 ERROR"),
1701
- chalk.hex(this.colorScheme.danger)(message),
1702
- ...args
1703
- );
1704
- if (error && error.stack) {
1705
- console.error(chalk.dim(error.stack));
1706
- }
1707
- }
1708
- loader(message) {
1709
- const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
1710
- let i = 0;
1711
- const interval = setInterval(() => {
1712
- process.stdout.write(
1713
- `\r${this.formatTimestamp()} ${this.formatPrefix()} ${chalk.hex(this.colorScheme.warn)(frames[i])} ${message}`
1714
- );
1715
- i = (i + 1) % frames.length;
1716
- }, 100);
1717
- return (newMessage) => {
1718
- clearInterval(interval);
1719
- process.stdout.write(
1720
- `\r${this.formatTimestamp()} ${this.formatPrefix()} ${chalk.hex(this.colorScheme.success)("\u2713")} ${newMessage || message}
1721
- `
1722
- );
1723
- };
1724
- }
1725
- table(title, data) {
1726
- console.log(this.formatTimestamp(), this.formatPrefix(), chalk.bold(title));
1727
- Object.entries(data).forEach(([key, value]) => {
1728
- const formattedKey = chalk.hex(this.colorScheme.warn)(` ${key}`);
1729
- const formattedValue = chalk.hex(this.colorScheme.muted)(value);
1730
- console.log(`${formattedKey.padEnd(25)} ${formattedValue}`);
1731
- });
1732
- }
1733
- section(title) {
1734
- const line = "\u2500".repeat(Math.max(30, title.length + 4));
1735
- console.log(chalk.hex(this.colorScheme.muted)(`
1736
- \u250C\u2500${line}\u2500\u2510`));
1737
- console.log(
1738
- chalk.hex(this.colorScheme.muted)("\u2502 ") + chalk.bold.hex(this.colorScheme.text)(title.padEnd(line.length)) + chalk.hex(this.colorScheme.muted)(" \u2502")
1739
- );
1740
- console.log(chalk.hex(this.colorScheme.muted)(`\u2514\u2500${line}\u2500\u2518`));
1741
- }
1757
+ var createContextCommandConfig = createConfigFactory(defaultConfig2);
1758
+
1759
+ // src/configs/prefixCommand.config.ts
1760
+ var defaultConfig3 = {
1761
+ enabled: true,
1762
+ defaultPrefix: "!",
1763
+ allowMentionAsPrefix: true,
1764
+ allowCaseInsensitiveCommandNames: true,
1765
+ logExecution: true
1742
1766
  };
1743
- var logger = new Logger();
1767
+ var createPrefixCommandConfig = createConfigFactory(defaultConfig3);
1744
1768
 
1745
- // src/tools/utils.ts
1746
- function __zero(str) {
1747
- return str?.length ? str : "0";
1748
- }
1749
- function isMentionOrSnowflake(str) {
1750
- return str ? str.match(/<@[#&]?[\d]{6,}>/) || str.match(/\d{6,}/) ? true : false : false;
1751
- }
1752
- function cleanMention(str) {
1753
- return str ? str.replaceAll(/[<@#&>]/g, "").trim() : void 0;
1754
- }
1755
- async function getMessageMention(message, content, type, index = 0, idOnly) {
1756
- const args = content?.split(" ");
1757
- const arg = isMentionOrSnowflake(args?.[index]) ? cleanMention(args?.[index]) : void 0;
1758
- switch (type) {
1759
- case "user":
1760
- const userMention2 = message.mentions.users.at(index) || null;
1761
- if (!userMention2 && arg) {
1762
- return idOnly ? arg : await fetchUser(message.client, arg);
1763
- } else {
1764
- return idOnly ? userMention2?.id || null : userMention2;
1765
- }
1766
- case "member":
1767
- if (!message.guild) return null;
1768
- const member = await fetchMember(message.guild, message.mentions.users.at(index)?.id ?? arg);
1769
- return idOnly ? member?.id || null : member;
1770
- case "channel":
1771
- const channelMention = message.mentions.channels.at(index) || null;
1772
- if (!channelMention && arg) {
1773
- return idOnly ? arg : message.guild ? await fetchChannel(message.guild, arg) : message.client.channels.cache.get(__zero(arg)) ?? message.client.channels.fetch(__zero(arg));
1774
- } else {
1775
- return idOnly ? channelMention?.id || null : channelMention;
1776
- }
1777
- case "role":
1778
- const roleMention = message.mentions.roles.at(index) || null;
1779
- if (!roleMention && arg) {
1780
- return idOnly ? arg : message.guild ? await fetchRole(message.guild, arg) : null;
1781
- } else {
1782
- return idOnly ? roleMention?.id || null : roleMention;
1783
- }
1784
- default:
1785
- return null;
1786
- }
1787
- }
1788
- function getFirstMentionId(options) {
1789
- let mentionId = "";
1790
- if (options.message) {
1791
- switch (options.type) {
1792
- case "user":
1793
- mentionId = options.message.mentions.users.first()?.id || "";
1794
- break;
1795
- case "channel":
1796
- mentionId = options.message.mentions.channels.first()?.id || "";
1797
- break;
1798
- case "role":
1799
- mentionId = options.message.mentions.roles.first()?.id || "";
1800
- break;
1801
- }
1769
+ // src/configs/slashCommand.config.ts
1770
+ var defaultConfig4 = {
1771
+ logExecution: true
1772
+ };
1773
+ var createSlashCommandConfig = createConfigFactory(defaultConfig4);
1774
+
1775
+ // src/configs/staff.config.ts
1776
+ var defaultConfig5 = {
1777
+ ownerId: null,
1778
+ superUsers: [],
1779
+ superUserRoles: [],
1780
+ bypassers: [],
1781
+ bypassesGuildAdmin: {
1782
+ allBotStaff: false,
1783
+ botOwner: false,
1784
+ superUsers: false,
1785
+ bypassers: false
1786
+ },
1787
+ guild: {
1788
+ id: null,
1789
+ inviteUrl: null,
1790
+ channels: {}
1802
1791
  }
1803
- const firstArg = options.content?.split(" ")[0] || "";
1804
- return mentionId || isMentionOrSnowflake(firstArg) ? cleanMention(firstArg) : "";
1805
- }
1806
- async function fetchUser(client, userId) {
1807
- if (!userId) return null;
1808
- return client.users.cache.get(__zero(userId)) || await client.users.fetch(__zero(userId)).catch(() => null);
1809
- }
1810
- async function fetchGuild(client, guildId) {
1811
- if (!guildId) return null;
1812
- return client.guilds.cache.get(__zero(guildId)) || await client.guilds.fetch(__zero(guildId)).catch(() => null);
1813
- }
1814
- async function fetchMember(guild, memberId) {
1815
- if (!memberId) return null;
1816
- return guild.members.cache.get(__zero(memberId)) || await guild.members.fetch(__zero(memberId)).catch(() => null);
1817
- }
1818
- async function fetchChannel(guild, channelId, type) {
1819
- if (!channelId) return null;
1820
- const channel = guild.channels.cache.get(__zero(channelId)) || await guild.channels.fetch(__zero(channelId)).catch(() => null);
1821
- if (type && channel?.type !== type) return null;
1822
- return channel;
1823
- }
1824
- async function fetchRole(guild, roleId) {
1825
- if (!roleId) return null;
1826
- return guild.roles.cache.get(__zero(roleId)) || await guild.roles.fetch(__zero(roleId)).catch(() => null) || null;
1827
- }
1828
- async function fetchMessage(channel, messageId) {
1829
- if (!messageId) return null;
1830
- return channel.messages.cache.get(__zero(messageId)) || await channel.messages.fetch(__zero(messageId)).catch(() => null) || null;
1831
- }
1792
+ };
1793
+ var createStaffConfig = createConfigFactory(defaultConfig5);
1832
1794
 
1833
- // src/types/status.ts
1834
- import { ActivityType } from "discord.js";
1835
- import _11 from "lodash";
1836
- var StatusType = /* @__PURE__ */ ((StatusType2) => {
1837
- StatusType2["DND"] = "dnd";
1838
- StatusType2["Idle"] = "idle";
1839
- StatusType2["Online"] = "online";
1840
- StatusType2["Invisible"] = "invisible";
1841
- return StatusType2;
1842
- })(StatusType || {});
1843
- var defaultPresence = {
1844
- production: {
1845
- interval: 6e4,
1846
- randomize: false,
1847
- activity: [
1848
- { status: "online" /* Online */, type: ActivityType.Custom, name: "Need help? Use /help or !help" },
1849
- { status: "online" /* Online */, type: ActivityType.Custom, name: "Join our community!" },
1850
- { status: "online" /* Online */, type: ActivityType.Watching, name: "\u2728 $GUILD_COUNT servers" }
1851
- ]
1795
+ // src/client/vimcord.utils.ts
1796
+ var DEFAULT_MODULE_SUFFIXES = {
1797
+ slashCommands: ".slash",
1798
+ contextCommands: ".ctx",
1799
+ prefixCommands: ".prefix",
1800
+ events: ".event"
1801
+ };
1802
+ var configSetters = {
1803
+ app: createAppConfig,
1804
+ staff: createStaffConfig,
1805
+ slashCommands: createSlashCommandConfig,
1806
+ prefixCommands: createPrefixCommandConfig,
1807
+ contextCommands: createContextCommandConfig
1808
+ };
1809
+ var moduleImporters = {
1810
+ slashCommands: (client, options, set) => {
1811
+ const opt = options;
1812
+ const dir = Array.isArray(options) ? options : opt?.dir ?? [];
1813
+ const suffix = Array.isArray(options) ? DEFAULT_MODULE_SUFFIXES.slashCommands : opt?.suffix ?? DEFAULT_MODULE_SUFFIXES.slashCommands;
1814
+ return client.commands.slash.importFrom(dir, set, suffix);
1852
1815
  },
1853
- development: {
1854
- activity: { status: "dnd" /* DND */, type: ActivityType.Custom, name: "In development!" }
1816
+ contextCommands: (client, options, set) => {
1817
+ const opt = options;
1818
+ const dir = Array.isArray(options) ? options : opt?.dir ?? [];
1819
+ const suffix = Array.isArray(options) ? DEFAULT_MODULE_SUFFIXES.contextCommands : opt?.suffix ?? DEFAULT_MODULE_SUFFIXES.contextCommands;
1820
+ return client.commands.context.importFrom(dir, set, suffix);
1821
+ },
1822
+ prefixCommands: (client, options, set) => {
1823
+ const opt = options;
1824
+ const dir = Array.isArray(options) ? options : opt?.dir ?? [];
1825
+ const suffix = Array.isArray(options) ? DEFAULT_MODULE_SUFFIXES.prefixCommands : opt?.suffix ?? DEFAULT_MODULE_SUFFIXES.prefixCommands;
1826
+ return client.commands.prefix.importFrom(dir, set, suffix);
1827
+ },
1828
+ events: (client, options, set) => {
1829
+ const opt = options;
1830
+ const dir = Array.isArray(options) ? options : opt?.dir ?? [];
1831
+ const suffix = Array.isArray(options) ? DEFAULT_MODULE_SUFFIXES.events : opt?.suffix ?? DEFAULT_MODULE_SUFFIXES.events;
1832
+ return client.events.importFrom(dir, set, suffix);
1855
1833
  }
1856
1834
  };
1857
- function createVimcordStatusConfig(options = {}) {
1858
- return _11.merge(defaultPresence, options);
1835
+ function defineClientOptions(options) {
1836
+ return options;
1859
1837
  }
1838
+ function defineVimcordFeatures(features) {
1839
+ return features;
1840
+ }
1841
+ function defineVimcordConfig(config) {
1842
+ return {
1843
+ app: createAppConfig(config.app),
1844
+ staff: createStaffConfig(config.staff),
1845
+ slashCommands: createSlashCommandConfig(config.slashCommands),
1846
+ prefixCommands: createPrefixCommandConfig(config.prefixCommands),
1847
+ contextCommands: createContextCommandConfig(config.contextCommands)
1848
+ };
1849
+ }
1850
+ var useClient = Vimcord.getInstance;
1851
+ var useReadyClient = Vimcord.getReadyInstance;
1852
+ var createClient = Vimcord.create;
1860
1853
 
1861
- // src/modules/status.manager.ts
1862
- import EventEmitter from "events";
1863
- import { $ as $2 } from "qznt";
1864
- var StatusManager = class {
1865
- client;
1866
- logger;
1867
- emitter = new EventEmitter();
1868
- lastActivity = null;
1869
- lastActivityIndex = 0;
1870
- task = null;
1871
- constructor(client) {
1872
- this.client = client;
1873
- this.logger = new Logger({ prefixEmoji: "\u{1F4AC}", prefix: `StatusManager (i${this.client.clientId})` });
1874
- this.emitter.on("changed", (activity) => {
1875
- if (this.client.config.app.verbose) {
1876
- this.logger.debug(`Status changed to '${activity.name}'`);
1877
- }
1878
- });
1879
- this.emitter.on("cleared", () => {
1880
- if (this.client.config.app.verbose) {
1881
- this.logger.debug("Status cleared");
1882
- }
1883
- });
1884
- }
1885
- clearData() {
1886
- this.task?.stop();
1887
- this.task = null;
1888
- this.lastActivity = null;
1889
- this.lastActivityIndex = 0;
1890
- return this;
1891
- }
1892
- async getReadyClient() {
1893
- const client = await this.client.waitForReady();
1894
- if (!client.user) throw new Error("Cannot manage the client's activity when its user is not hydrated");
1895
- return client;
1896
- }
1897
- async formatActivityName(name) {
1898
- name = name.replace("$USER_COUNT", $2.format.number(this.client.users.cache.size)).replace("$GUILD_COUNT", $2.format.number(this.client.guilds.cache.size)).replace(
1899
- "$INVITE",
1900
- this.client.config.staff.guild.inviteUrl ? this.client.config.staff.guild.inviteUrl : "<STAFF_INVITE_URL_NOT_SET>"
1901
- );
1902
- if (name.includes("$STAFF_GUILD_MEMBER_COUNT")) {
1903
- await fetchGuild(this.client, this.client.config.staff.guild.id).then((guild) => {
1904
- if (!guild) return name = name.replace("$STAFF_GUILD_MEMBER_COUNT", "<STAFF_GUILD_NOT_FOUND>");
1905
- name = name.replace("$STAFF_GUILD_MEMBER_COUNT", $2.format.number(guild.members.cache.size));
1906
- }).catch((err) => this.logger.error("Failed to fetch the staff guild", err));
1907
- }
1908
- return name;
1909
- }
1910
- async setActivity(activity) {
1911
- const client = await this.getReadyClient();
1912
- activity.name = await this.formatActivityName(activity.name);
1913
- client.user.setStatus(activity.status);
1914
- client.user.setActivity({ name: activity.name, type: activity.type, url: activity.streamUrl });
1915
- this.emitter.emit("changed", activity);
1916
- }
1917
- async statusRotationTask(clientStatus) {
1918
- let activity;
1919
- if (clientStatus.randomize && Array.isArray(clientStatus.activity)) {
1920
- activity = $2.rnd.choice(clientStatus.activity, { not: this.lastActivity });
1921
- this.lastActivity = activity;
1922
- } else {
1923
- const activityIndex = (this.lastActivityIndex + 1) % clientStatus.activity.length;
1924
- this.lastActivityIndex = activityIndex;
1925
- activity = clientStatus.activity[activityIndex];
1926
- }
1927
- await this.setActivity(activity);
1928
- this.emitter.emit("rotation", activity);
1929
- }
1930
- async scheduleStatusRotation(clientStatus) {
1931
- if (!clientStatus.interval) throw new Error("Cannot create client activity interval without interval time");
1932
- this.task?.stop();
1933
- this.task = null;
1934
- this.task = new $2.Loop(() => this.statusRotationTask(clientStatus), $2.math.ms(clientStatus.interval), true);
1935
- this.start();
1936
- }
1937
- start() {
1938
- if (this.task) {
1939
- this.task.start();
1940
- this.emitter.emit("started", this.task);
1941
- }
1942
- return this;
1854
+ // src/modules/command.manager.ts
1855
+ import { Routes } from "discord.js";
1856
+
1857
+ // src/utils/import.utils.ts
1858
+ import path from "path";
1859
+ import { $ } from "qznt";
1860
+ function getProcessDir() {
1861
+ const mainPath = process.argv[1];
1862
+ if (!mainPath) return "";
1863
+ return path.dirname(mainPath);
1864
+ }
1865
+ async function importModulesFromDir(dir, suffix) {
1866
+ const cwd = getProcessDir();
1867
+ const MODULE_RELATIVE_PATH = path.join(cwd, dir);
1868
+ const MODULE_LOG_PATH = dir;
1869
+ const files = $.fs.readDir(MODULE_RELATIVE_PATH).filter((fn) => fn.endsWith(`${suffix ? `.${suffix}` : ""}.js`) || fn.endsWith(`${suffix ? `.${suffix}` : ""}.ts`));
1870
+ if (!files.length) {
1871
+ return [];
1943
1872
  }
1944
- pause() {
1945
- if (this.task) {
1946
- this.task.stop();
1947
- this.emitter.emit("paused", this.task);
1948
- }
1949
- return this;
1873
+ const modules = await Promise.all(
1874
+ files.map(async (fn) => {
1875
+ let _path = path.join(MODULE_RELATIVE_PATH, fn);
1876
+ let _logPath = `./${path.join(MODULE_LOG_PATH, fn)}`;
1877
+ let _module;
1878
+ try {
1879
+ delete __require.cache[__require.resolve(_path)];
1880
+ _module = __require(_path);
1881
+ } catch (err) {
1882
+ console.warn(`Failed to import module at '${_logPath}'`, err);
1883
+ _module = null;
1884
+ }
1885
+ return { module: _module, path: _logPath };
1886
+ })
1887
+ );
1888
+ const filteredModules = modules.filter((m) => m.module);
1889
+ if (!filteredModules.length) {
1890
+ console.warn(`No valid modules were found in directory '${dir}'`);
1950
1891
  }
1951
- async set(status) {
1952
- const statusConfig = createVimcordStatusConfig(status);
1953
- let clientStatus;
1954
- if (this.client.config.app.devMode) {
1955
- clientStatus = statusConfig.development;
1956
- } else {
1957
- clientStatus = statusConfig.production;
1958
- }
1959
- if (!clientStatus.interval) {
1960
- await this.setActivity(Array.isArray(clientStatus.activity) ? clientStatus.activity[0] : clientStatus.activity);
1961
- } else {
1962
- await this.scheduleStatusRotation(clientStatus);
1963
- }
1964
- return this;
1892
+ return filteredModules;
1893
+ }
1894
+
1895
+ // src/modules/base-module.importer.ts
1896
+ var ModuleImporter = class {
1897
+ client;
1898
+ constructor(client) {
1899
+ this.client = client;
1965
1900
  }
1966
- async destroy() {
1967
- if (this.task) {
1968
- this.task.stop();
1969
- this.task = null;
1970
- this.emitter.emit("destroyed");
1971
- await this.clear();
1901
+ async importFrom(dir, set = false, suffix) {
1902
+ if (set) this.items.clear();
1903
+ const dirs = Array.isArray(dir) ? dir : [dir];
1904
+ const modules = [];
1905
+ const effectiveSuffix = Array.isArray(suffix) ? suffix[0] : suffix ?? this.itemSuffix;
1906
+ for (const _dir of dirs) {
1907
+ const results = await importModulesFromDir(_dir, effectiveSuffix ?? void 0);
1908
+ modules.push(...results.map(({ module }) => module.default));
1972
1909
  }
1973
- return this;
1974
- }
1975
- async clear() {
1976
- const client = await this.getReadyClient();
1977
- this.clearData();
1978
- client.user.setActivity({ name: "" });
1979
- this.emitter.emit("cleared");
1980
- return this;
1910
+ for (const module of modules) {
1911
+ const name = this.getName(module);
1912
+ this.items.set(name, module);
1913
+ }
1914
+ this.client.logger.moduleLoaded(this.itemName, modules.length);
1915
+ return this.items;
1981
1916
  }
1982
1917
  };
1983
1918
 
1984
1919
  // src/modules/command.manager.ts
1985
- import { Routes } from "discord.js";
1986
- var BaseCommandManager = class {
1920
+ var BaseCommandManager = class extends ModuleImporter {
1987
1921
  type;
1988
- client;
1989
- commands = /* @__PURE__ */ new Map();
1990
- moduleSuffix;
1991
- constructor(client, type, moduleSuffix) {
1922
+ items = /* @__PURE__ */ new Map();
1923
+ itemSuffix;
1924
+ constructor(client, type, itemSuffix) {
1925
+ super(client);
1992
1926
  this.type = type;
1993
- this.client = client;
1994
- this.moduleSuffix = moduleSuffix;
1927
+ switch (type) {
1928
+ case 0 /* Slash */:
1929
+ this.itemSuffix = itemSuffix ?? DEFAULT_MODULE_SUFFIXES.slashCommands;
1930
+ break;
1931
+ case 2 /* Context */:
1932
+ this.itemSuffix = itemSuffix ?? DEFAULT_MODULE_SUFFIXES.contextCommands;
1933
+ break;
1934
+ case 1 /* Prefix */:
1935
+ this.itemSuffix = itemSuffix ?? DEFAULT_MODULE_SUFFIXES.prefixCommands;
1936
+ break;
1937
+ }
1938
+ }
1939
+ get commands() {
1940
+ return this.items;
1941
+ }
1942
+ get itemName() {
1943
+ switch (this.type) {
1944
+ case 0 /* Slash */:
1945
+ return "Slash Commands";
1946
+ case 2 /* Context */:
1947
+ return "Context Commands";
1948
+ case 1 /* Prefix */:
1949
+ return "Prefix Commands";
1950
+ }
1951
+ }
1952
+ getName(module) {
1953
+ return "builder" in module ? module.builder.name : module.options.name;
1995
1954
  }
1996
1955
  /**
1997
1956
  * Gets a command by name.
@@ -2000,7 +1959,7 @@ var BaseCommandManager = class {
2000
1959
  if (this.type === 1 /* Prefix */) {
2001
1960
  const config = this.client.config.prefixCommands;
2002
1961
  const search = config.allowCaseInsensitiveCommandNames ? name.toLowerCase() : name;
2003
- return Array.from(this.commands.values()).find((cmd) => {
1962
+ return Array.from(this.items.values()).find((cmd) => {
2004
1963
  const commandName = "builder" in cmd ? cmd.builder.name : cmd.options.name;
2005
1964
  const trigger = config.allowCaseInsensitiveCommandNames ? commandName.toLowerCase() : commandName;
2006
1965
  if (trigger === search) return true;
@@ -2011,7 +1970,7 @@ var BaseCommandManager = class {
2011
1970
  }
2012
1971
  });
2013
1972
  } else {
2014
- return this.commands.get(name);
1973
+ return this.items.get(name);
2015
1974
  }
2016
1975
  }
2017
1976
  /**
@@ -2020,7 +1979,7 @@ var BaseCommandManager = class {
2020
1979
  getAll(options = {}) {
2021
1980
  const matchedCommands = /* @__PURE__ */ new Map();
2022
1981
  const isDev = this.client.config.app.devMode;
2023
- for (const cmd of this.commands.values()) {
1982
+ for (const cmd of this.items.values()) {
2024
1983
  const commandName = "builder" in cmd ? cmd.builder.name : cmd.options.name;
2025
1984
  if (options.names || options.fuzzyNames) {
2026
1985
  const nameMatched = options.names?.includes(commandName) || options.fuzzyNames?.some((fuzzy) => commandName.includes(fuzzy));
@@ -2047,7 +2006,7 @@ var BaseCommandManager = class {
2047
2006
  */
2048
2007
  sortByCategory() {
2049
2008
  const categories = /* @__PURE__ */ new Map();
2050
- for (const cmd of this.commands.values()) {
2009
+ for (const cmd of this.items.values()) {
2051
2010
  const metadata = cmd.options.metadata;
2052
2011
  if (!metadata?.category) continue;
2053
2012
  let entry = categories.get(metadata.category);
@@ -2070,52 +2029,20 @@ var BaseCommandManager = class {
2070
2029
  return cat;
2071
2030
  });
2072
2031
  }
2073
- /**
2074
- * Imports command modules from a directory.
2075
- * @param dir Path of one or more folders.
2076
- * @param set Replaces imported command modules with the ones found.
2077
- */
2078
- async importFrom(dir, set = false) {
2079
- if (set) this.commands.clear();
2080
- const dirs = Array.isArray(dir) ? dir : [dir];
2081
- const modules = [];
2082
- for (const _dir of dirs) {
2083
- const results = await importModulesFromDir(_dir, this.moduleSuffix);
2084
- modules.push(...results.map(({ module }) => module.default));
2085
- }
2086
- for (const module of modules) {
2087
- const commandName = "builder" in module ? module.builder.name : module.options.name;
2088
- this.commands.set(commandName, module);
2089
- }
2090
- let moduleType;
2091
- switch (this.type) {
2092
- case 0 /* Slash */:
2093
- moduleType = "Slash Commands";
2094
- break;
2095
- case 2 /* Context */:
2096
- moduleType = "Context Commands";
2097
- break;
2098
- case 1 /* Prefix */:
2099
- moduleType = "Prefix Commands";
2100
- break;
2101
- }
2102
- this.client.logger.moduleLoaded(moduleType, modules.length);
2103
- return this.commands;
2104
- }
2105
2032
  };
2106
2033
  var SlashCommandManager = class extends BaseCommandManager {
2107
2034
  constructor(client) {
2108
- super(client, 0 /* Slash */, client.config.app.moduleSuffixes.slashCommand);
2035
+ super(client, 0 /* Slash */);
2109
2036
  }
2110
2037
  };
2111
2038
  var ContextCommandManager = class extends BaseCommandManager {
2112
2039
  constructor(client) {
2113
- super(client, 2 /* Context */, client.config.app.moduleSuffixes.contextCommand);
2040
+ super(client, 2 /* Context */);
2114
2041
  }
2115
2042
  };
2116
2043
  var PrefixCommandManager = class extends BaseCommandManager {
2117
2044
  constructor(client) {
2118
- super(client, 1 /* Prefix */, client.config.app.moduleSuffixes.prefixCommand);
2045
+ super(client, 1 /* Prefix */);
2119
2046
  }
2120
2047
  };
2121
2048
  var CommandManager = class {
@@ -2133,7 +2060,7 @@ var CommandManager = class {
2133
2060
  return [...this.slash.getAll(options), ...this.context.getAll(options)];
2134
2061
  }
2135
2062
  async registerGlobal(options = {}) {
2136
- const client = await this.client.waitForReady();
2063
+ const client = await Vimcord.getReadyInstance(this.client.clientId);
2137
2064
  if (!client.rest) {
2138
2065
  console.error(`[CommandManager] \u2716 Failed to register app commands globally: REST is not initialized`);
2139
2066
  return;
@@ -2157,7 +2084,7 @@ var CommandManager = class {
2157
2084
  }
2158
2085
  }
2159
2086
  async unregisterGlobal() {
2160
- const client = await this.client.waitForReady();
2087
+ const client = await Vimcord.getReadyInstance(this.client.clientId);
2161
2088
  if (!client.rest) {
2162
2089
  console.error(`[CommandManager] \u2716 Failed to remove app commands globally: REST is not initialized`);
2163
2090
  return;
@@ -2170,7 +2097,7 @@ var CommandManager = class {
2170
2097
  }
2171
2098
  }
2172
2099
  async registerGuild(options = {}) {
2173
- const client = await this.client.waitForReady();
2100
+ const client = await Vimcord.getReadyInstance(this.client.clientId);
2174
2101
  if (!client.rest) {
2175
2102
  console.error(`[CommandManager] \u2716 Failed to register app commands by guild: REST is not initialized`);
2176
2103
  return;
@@ -2202,7 +2129,7 @@ var CommandManager = class {
2202
2129
  );
2203
2130
  }
2204
2131
  async unregisterGuild(options = {}) {
2205
- const client = await this.client.waitForReady();
2132
+ const client = await Vimcord.getReadyInstance(this.client.clientId);
2206
2133
  if (!client.rest) {
2207
2134
  console.error(`[CommandManager] \u2716 Failed to register app commands by guild: REST is not initialized`);
2208
2135
  return;
@@ -2219,12 +2146,13 @@ var CommandManager = class {
2219
2146
 
2220
2147
  // src/modules/event.manager.ts
2221
2148
  import { Events } from "discord.js";
2222
- var EventManager = class {
2223
- client;
2224
- events = /* @__PURE__ */ new Map();
2149
+ var EventManager = class extends ModuleImporter {
2150
+ items = /* @__PURE__ */ new Map();
2151
+ itemSuffix = "event";
2152
+ itemName = "Event Handlers";
2225
2153
  logger;
2226
2154
  constructor(client) {
2227
- this.client = client;
2155
+ super(client);
2228
2156
  this.logger = new Logger({ prefixEmoji: "\u{1F4CB}", prefix: `EventManager (i${this.client.clientId})` });
2229
2157
  for (const event of Object.values(Events)) {
2230
2158
  client.on(
@@ -2233,105 +2161,361 @@ var EventManager = class {
2233
2161
  );
2234
2162
  }
2235
2163
  }
2236
- register(...events) {
2237
- for (const event of events) {
2238
- this.events.set(event.name, event);
2239
- if (this.client.config.app.verbose) {
2240
- this.logger.debug(`'${event.name}' registered for EventType '${event.event}'`);
2241
- }
2242
- }
2164
+ getName(module) {
2165
+ return module.name;
2166
+ }
2167
+ register(...events) {
2168
+ for (const event of events) {
2169
+ this.items.set(event.name, event);
2170
+ if (this.client.config.app.verbose) {
2171
+ this.logger.debug(`'${event.name}' registered for EventType '${event.event}'`);
2172
+ }
2173
+ }
2174
+ }
2175
+ unregister(...names) {
2176
+ for (const name of names) {
2177
+ const event = this.items.get(name);
2178
+ if (!event) continue;
2179
+ this.items.delete(name);
2180
+ if (this.client.config.app.verbose) {
2181
+ this.logger.debug(`'${event.name}' unregistered for EventType '${event.event}'`);
2182
+ }
2183
+ }
2184
+ }
2185
+ clear() {
2186
+ this.items.forEach((e) => this.unregister(e.name));
2187
+ this.items.clear();
2188
+ }
2189
+ get(name) {
2190
+ return this.items.get(name);
2191
+ }
2192
+ getByTag(tag) {
2193
+ return Array.from(this.items.values()).filter((event) => event.metadata?.tags?.includes(tag));
2194
+ }
2195
+ getByCategory(category) {
2196
+ return Array.from(this.items.values()).filter((event) => event.metadata?.category?.includes(category));
2197
+ }
2198
+ getByEvent(eventType) {
2199
+ return Array.from(this.items.values()).filter((event) => event.event === eventType);
2200
+ }
2201
+ async executeEvents(eventType, ...args) {
2202
+ const events = this.getByEvent(eventType);
2203
+ if (!events.length) return;
2204
+ const sortedEvents = events.sort((a, b) => b.priority - a.priority);
2205
+ await Promise.all(
2206
+ sortedEvents.map(async (event) => {
2207
+ try {
2208
+ await event.execute?.(this.client, ...args);
2209
+ if (event.once) {
2210
+ this.unregister(event.name);
2211
+ }
2212
+ } catch (err) {
2213
+ this.logger.error(`'${event.name}' failed to execute`, err);
2214
+ }
2215
+ })
2216
+ );
2217
+ }
2218
+ };
2219
+
2220
+ // src/tools/utils.ts
2221
+ var MENTION_OR_SNOWFLAKE_REGEX = /<@[#&]?[\d]{6,}>|[\d]{6,}/;
2222
+ var fetchUserPromises = /* @__PURE__ */ new Map();
2223
+ var fetchGuildPromises = /* @__PURE__ */ new Map();
2224
+ var fetchMemberPromises = /* @__PURE__ */ new Map();
2225
+ var fetchChannelPromises = /* @__PURE__ */ new Map();
2226
+ var fetchMessagePromises = /* @__PURE__ */ new Map();
2227
+ var fetchRolePromises = /* @__PURE__ */ new Map();
2228
+ function createCachedFetch(cache, fetchFn, cacheKey) {
2229
+ const cached = cache.get(cacheKey);
2230
+ if (cached) return cached;
2231
+ const promise = fetchFn().finally(() => cache.delete(cacheKey));
2232
+ cache.set(cacheKey, promise);
2233
+ return promise;
2234
+ }
2235
+ function __zero(str) {
2236
+ return str?.length ? str : "0";
2237
+ }
2238
+ function isMentionOrSnowflake(str) {
2239
+ return str ? MENTION_OR_SNOWFLAKE_REGEX.test(str) : false;
2240
+ }
2241
+ function cleanMention(str) {
2242
+ return str ? str.replaceAll(/[<@#&>]/g, "").trim() : void 0;
2243
+ }
2244
+ async function getMessageMention(message, content, type, index = 0, idOnly) {
2245
+ const args = content?.split(" ");
2246
+ const arg = isMentionOrSnowflake(args?.[index]) ? cleanMention(args?.[index]) : void 0;
2247
+ switch (type) {
2248
+ case "user": {
2249
+ const userMention2 = message.mentions.users.at(index) || null;
2250
+ if (!userMention2 && arg) {
2251
+ return idOnly ? arg : await fetchUser(message.client, arg);
2252
+ }
2253
+ return idOnly ? userMention2?.id || null : userMention2;
2254
+ }
2255
+ case "member": {
2256
+ if (!message.guild) return null;
2257
+ const member = await fetchMember(message.guild, message.mentions.users.at(index)?.id ?? arg);
2258
+ return idOnly ? member?.id || null : member;
2259
+ }
2260
+ case "channel": {
2261
+ const channelMention = message.mentions.channels.at(index) || null;
2262
+ if (!channelMention && arg) {
2263
+ if (idOnly) return arg;
2264
+ const channel = message.guild ? await fetchChannel(message.guild, arg) : message.client.channels.cache.get(__zero(arg)) ?? message.client.channels.fetch(__zero(arg));
2265
+ return channel;
2266
+ }
2267
+ return idOnly ? channelMention?.id || null : channelMention;
2268
+ }
2269
+ case "role": {
2270
+ const roleMention = message.mentions.roles.at(index) || null;
2271
+ if (!roleMention && arg) {
2272
+ if (idOnly) return arg;
2273
+ return message.guild ? await fetchRole(message.guild, arg) : null;
2274
+ }
2275
+ return idOnly ? roleMention?.id || null : roleMention;
2276
+ }
2277
+ default:
2278
+ return null;
2279
+ }
2280
+ }
2281
+ function getFirstMentionId(options) {
2282
+ let mentionId = "";
2283
+ if (options.message) {
2284
+ switch (options.type) {
2285
+ case "user":
2286
+ mentionId = options.message.mentions.users.first()?.id || "";
2287
+ break;
2288
+ case "member":
2289
+ mentionId = options.message.mentions.members?.first()?.id || "";
2290
+ break;
2291
+ case "channel":
2292
+ mentionId = options.message.mentions.channels.first()?.id || "";
2293
+ break;
2294
+ case "role":
2295
+ mentionId = options.message.mentions.roles.first()?.id || "";
2296
+ break;
2297
+ }
2298
+ }
2299
+ const firstArg = options.content?.split(" ")[0] || "";
2300
+ return mentionId || isMentionOrSnowflake(firstArg) ? cleanMention(firstArg) : "";
2301
+ }
2302
+ async function fetchUser(client, userId) {
2303
+ if (!userId) return null;
2304
+ const key = `${client.user.id}-${userId}`;
2305
+ const cached = client.users.cache.get(__zero(userId));
2306
+ if (cached) return cached;
2307
+ return createCachedFetch(fetchUserPromises, () => client.users.fetch(__zero(userId)).catch(() => null), key);
2308
+ }
2309
+ async function fetchGuild(client, guildId) {
2310
+ if (!guildId) return null;
2311
+ const key = `${client.user.id}-${guildId}`;
2312
+ const cached = client.guilds.cache.get(__zero(guildId));
2313
+ if (cached) return cached;
2314
+ return createCachedFetch(fetchGuildPromises, () => client.guilds.fetch(__zero(guildId)).catch(() => null), key);
2315
+ }
2316
+ async function fetchMember(guild, memberId) {
2317
+ if (!memberId) return null;
2318
+ const key = `${guild.id}-${memberId}`;
2319
+ const cached = guild.members.cache.get(__zero(memberId));
2320
+ if (cached) return cached;
2321
+ return createCachedFetch(fetchMemberPromises, () => guild.members.fetch(__zero(memberId)).catch(() => null), key);
2322
+ }
2323
+ async function fetchChannel(guild, channelId, type) {
2324
+ if (!channelId) return null;
2325
+ const key = `${guild.id}-${channelId}`;
2326
+ const cached = guild.channels.cache.get(__zero(channelId)) ?? null;
2327
+ if (cached) {
2328
+ if (type && cached.type !== type) return null;
2329
+ return cached;
2330
+ }
2331
+ const channel = await createCachedFetch(
2332
+ fetchChannelPromises,
2333
+ () => guild.channels.fetch(__zero(channelId)).catch(() => null),
2334
+ key
2335
+ );
2336
+ if (type && channel?.type !== type) return null;
2337
+ return channel;
2338
+ }
2339
+ async function fetchMessage(channel, messageId) {
2340
+ if (!messageId) return null;
2341
+ const key = `${channel.guild.id}-${messageId}`;
2342
+ const cached = channel.messages.cache.get(__zero(messageId));
2343
+ if (cached) return cached;
2344
+ return createCachedFetch(fetchMessagePromises, () => channel.messages.fetch(__zero(messageId)).catch(() => null), key);
2345
+ }
2346
+ async function fetchRole(guild, roleId) {
2347
+ if (!roleId) return null;
2348
+ const key = `${guild.id}-${roleId}`;
2349
+ const cached = guild.roles.cache.get(__zero(roleId));
2350
+ if (cached) return cached;
2351
+ return createCachedFetch(fetchRolePromises, () => guild.roles.fetch(__zero(roleId)).catch(() => null), key);
2352
+ }
2353
+
2354
+ // src/types/status.ts
2355
+ import { ActivityType } from "discord.js";
2356
+ import _6 from "lodash";
2357
+ var StatusType = /* @__PURE__ */ ((StatusType2) => {
2358
+ StatusType2["DND"] = "dnd";
2359
+ StatusType2["Idle"] = "idle";
2360
+ StatusType2["Online"] = "online";
2361
+ StatusType2["Invisible"] = "invisible";
2362
+ return StatusType2;
2363
+ })(StatusType || {});
2364
+ var defaultPresence = {
2365
+ production: {
2366
+ interval: 6e4,
2367
+ randomize: false,
2368
+ activity: [
2369
+ { status: "online" /* Online */, type: ActivityType.Custom, name: "Need help? Use /help or !help" },
2370
+ { status: "online" /* Online */, type: ActivityType.Custom, name: "Join our community!" },
2371
+ { status: "online" /* Online */, type: ActivityType.Watching, name: "\u2728 $GUILD_COUNT servers" }
2372
+ ]
2373
+ },
2374
+ development: {
2375
+ activity: { status: "dnd" /* DND */, type: ActivityType.Custom, name: "In development!" }
2376
+ }
2377
+ };
2378
+ function createVimcordStatusConfig(options = {}) {
2379
+ return _6.merge(defaultPresence, options);
2380
+ }
2381
+
2382
+ // src/modules/status.manager.ts
2383
+ import EventEmitter from "events";
2384
+ import { $ as $2 } from "qznt";
2385
+ var StatusManager = class {
2386
+ client;
2387
+ logger;
2388
+ emitter = new EventEmitter();
2389
+ lastActivity = null;
2390
+ lastActivityIndex = 0;
2391
+ task = null;
2392
+ constructor(client) {
2393
+ this.client = client;
2394
+ this.logger = new Logger({ prefixEmoji: "\u{1F4AC}", prefix: `StatusManager (i${this.client.clientId})` });
2395
+ this.emitter.on("changed", (activity) => {
2396
+ if (this.client.config.app.verbose) {
2397
+ this.logger.debug(`Status changed to '${activity.name}'`);
2398
+ }
2399
+ });
2400
+ this.emitter.on("cleared", () => {
2401
+ if (this.client.config.app.verbose) {
2402
+ this.logger.debug("Status cleared");
2403
+ }
2404
+ });
2405
+ }
2406
+ clearData() {
2407
+ this.task?.stop();
2408
+ this.task = null;
2409
+ this.lastActivity = null;
2410
+ this.lastActivityIndex = 0;
2411
+ return this;
2412
+ }
2413
+ async getReadyClient() {
2414
+ const client = await Vimcord.getReadyInstance(this.client.clientId);
2415
+ if (!client.user) throw new Error("Cannot manage the client's activity when its user is not hydrated");
2416
+ return client;
2417
+ }
2418
+ async formatActivityName(name) {
2419
+ name = name.replace("$USER_COUNT", $2.format.number(this.client.users.cache.size)).replace("$GUILD_COUNT", $2.format.number(this.client.guilds.cache.size)).replace(
2420
+ "$INVITE",
2421
+ this.client.config.staff.guild.inviteUrl ? this.client.config.staff.guild.inviteUrl : "<STAFF_INVITE_URL_NOT_SET>"
2422
+ );
2423
+ if (name.includes("$STAFF_GUILD_MEMBER_COUNT")) {
2424
+ await fetchGuild(this.client, this.client.config.staff.guild.id).then((guild) => {
2425
+ if (!guild) return name = name.replace("$STAFF_GUILD_MEMBER_COUNT", "<STAFF_GUILD_NOT_FOUND>");
2426
+ name = name.replace("$STAFF_GUILD_MEMBER_COUNT", $2.format.number(guild.members.cache.size));
2427
+ }).catch((err) => this.logger.error("Failed to fetch the staff guild", err));
2428
+ }
2429
+ return name;
2430
+ }
2431
+ async setActivity(activity) {
2432
+ const client = await this.getReadyClient();
2433
+ activity.name = await this.formatActivityName(activity.name);
2434
+ client.user.setStatus(activity.status);
2435
+ client.user.setActivity({ name: activity.name, type: activity.type, url: activity.streamUrl });
2436
+ this.emitter.emit("changed", activity);
2243
2437
  }
2244
- unregister(...names) {
2245
- for (const name of names) {
2246
- const event = this.events.get(name);
2247
- if (!event) continue;
2248
- this.events.delete(name);
2249
- if (this.client.config.app.verbose) {
2250
- this.logger.debug(`'${event.name}' unregistered for EventType '${event.event}'`);
2251
- }
2438
+ async statusRotationTask(clientStatus) {
2439
+ let activity;
2440
+ if (clientStatus.randomize && Array.isArray(clientStatus.activity)) {
2441
+ activity = $2.rnd.choice(clientStatus.activity, { not: this.lastActivity });
2442
+ this.lastActivity = activity;
2443
+ } else {
2444
+ const activityIndex = (this.lastActivityIndex + 1) % clientStatus.activity.length;
2445
+ this.lastActivityIndex = activityIndex;
2446
+ activity = clientStatus.activity[activityIndex];
2252
2447
  }
2448
+ await this.setActivity(activity);
2449
+ this.emitter.emit("rotation", activity);
2253
2450
  }
2254
- clear() {
2255
- this.events.forEach((e) => this.unregister(e.name));
2256
- this.events.clear();
2257
- }
2258
- get(name) {
2259
- return this.events.get(name);
2260
- }
2261
- getByTag(tag) {
2262
- return Array.from(this.events.values()).filter((event) => event.metadata?.tags?.includes(tag));
2451
+ async scheduleStatusRotation(clientStatus) {
2452
+ if (!clientStatus.interval) throw new Error("Cannot create client activity interval without interval time");
2453
+ this.task?.stop();
2454
+ this.task = null;
2455
+ this.task = new $2.Loop(() => this.statusRotationTask(clientStatus), $2.math.ms(clientStatus.interval), true);
2456
+ this.start();
2263
2457
  }
2264
- getByCategory(category) {
2265
- return Array.from(this.events.values()).filter((event) => event.metadata?.category?.includes(category));
2458
+ start() {
2459
+ if (this.task) {
2460
+ this.task.start();
2461
+ this.emitter.emit("started", this.task);
2462
+ }
2463
+ return this;
2266
2464
  }
2267
- getByEvent(eventType) {
2268
- return Array.from(this.events.values()).filter((event) => event.event === eventType);
2465
+ pause() {
2466
+ if (this.task) {
2467
+ this.task.stop();
2468
+ this.emitter.emit("paused", this.task);
2469
+ }
2470
+ return this;
2269
2471
  }
2270
- async executeEvents(eventType, ...args) {
2271
- const events = this.getByEvent(eventType);
2272
- if (!events.length) return;
2273
- const sortedEvents = events.sort((a, b) => b.priority - a.priority);
2274
- await Promise.all(
2275
- sortedEvents.map(async (event) => {
2276
- try {
2277
- await event.execute?.(this.client, ...args);
2278
- if (event.once) {
2279
- this.unregister(event.name);
2280
- }
2281
- } catch (err) {
2282
- this.logger.error(`'${event.name}' failed to execute`, err);
2283
- }
2284
- })
2285
- );
2472
+ async set(status) {
2473
+ const statusConfig = createVimcordStatusConfig(status);
2474
+ let clientStatus;
2475
+ if (this.client.config.app.devMode) {
2476
+ clientStatus = statusConfig.development;
2477
+ } else {
2478
+ clientStatus = statusConfig.production;
2479
+ }
2480
+ if (!clientStatus.interval) {
2481
+ await this.setActivity(Array.isArray(clientStatus.activity) ? clientStatus.activity[0] : clientStatus.activity);
2482
+ } else {
2483
+ await this.scheduleStatusRotation(clientStatus);
2484
+ }
2485
+ return this;
2286
2486
  }
2287
- /** Import event modules that end with `.event` */
2288
- async importFrom(dir, replaceAll) {
2289
- dir = Array.isArray(dir) ? dir : [dir];
2290
- const eventModules = await Promise.all(
2291
- dir.map((dir2) => importModulesFromDir(dir2, "event"))
2292
- );
2293
- if (replaceAll) {
2294
- this.clear();
2295
- }
2296
- let importedEvents = 0;
2297
- let ignoredEvents = 0;
2298
- for (const event of eventModules.flat()) {
2299
- if (!event.module.default.enabled) {
2300
- ignoredEvents++;
2301
- } else {
2302
- importedEvents++;
2303
- }
2304
- this.register(event.module.default);
2487
+ async destroy() {
2488
+ if (this.task) {
2489
+ this.task.stop();
2490
+ this.task = null;
2491
+ this.emitter.emit("destroyed");
2492
+ await this.clear();
2305
2493
  }
2306
- this.client.logger.moduleLoaded("Event Handlers", importedEvents, ignoredEvents);
2307
- return this.events;
2494
+ return this;
2495
+ }
2496
+ async clear() {
2497
+ const client = await this.getReadyClient();
2498
+ this.clearData();
2499
+ client.user.setActivity({ name: "" });
2500
+ this.emitter.emit("cleared");
2501
+ return this;
2308
2502
  }
2309
2503
  };
2310
2504
 
2311
- // package.json
2312
- var version = "1.0.36";
2313
-
2314
- // src/client.ts
2315
- import { randomUUID as randomUUID3 } from "crypto";
2316
- import { $ as $4 } from "qznt";
2317
-
2318
- // src/utils/VimcordCLI.ts
2505
+ // src/utils/vimcord.cli.ts
2319
2506
  import { createInterface } from "readline";
2320
2507
  import { $ as $3 } from "qznt";
2321
-
2322
- // src/utils/clientUtils.ts
2323
- function useClient(clientId = 0) {
2324
- return Vimcord.instances.get(clientId);
2325
- }
2326
- async function useReadyClient(clientId = 0) {
2327
- return useClient(clientId)?.waitForReady();
2328
- }
2329
- function createClient(options, features = {}, config = {}) {
2330
- return new Vimcord(options, features, config);
2331
- }
2332
-
2333
- // src/utils/VimcordCLI.ts
2334
- var VimcordCLI = class {
2508
+ var VimcordCLI = class _VimcordCLI {
2509
+ static mode = "off";
2510
+ static setMode(mode) {
2511
+ if (_VimcordCLI.mode === mode) return;
2512
+ _VimcordCLI.mode = mode;
2513
+ if (mode === "on") {
2514
+ CLI.logger.log(`~ Type ${CLI.options.prefix}help to view available commands`);
2515
+ } else {
2516
+ CLI.logger.log(`~ [MODE] Now set to "${mode}"`);
2517
+ }
2518
+ }
2335
2519
  rl;
2336
2520
  options;
2337
2521
  commands = /* @__PURE__ */ new Map();
@@ -2344,6 +2528,7 @@ var VimcordCLI = class {
2344
2528
  terminal: false
2345
2529
  });
2346
2530
  this.rl.on("line", (line) => {
2531
+ if (_VimcordCLI.mode !== "on") return;
2347
2532
  const { isCommand, commandName, content, args } = this.parseLine(line);
2348
2533
  if (!isCommand) return;
2349
2534
  const command = this.commands.get(commandName);
@@ -2400,7 +2585,6 @@ var VimcordCLI = class {
2400
2585
  return true;
2401
2586
  }
2402
2587
  };
2403
- var initCalled = false;
2404
2588
  var CLI = new VimcordCLI({ prefix: "/" });
2405
2589
  CLI.addCommand("help", "View information about a command, or the available CLI options", (args) => {
2406
2590
  const prefix = CLI.options.prefix;
@@ -2507,16 +2691,81 @@ CLI.addCommand("cmds", "List the loaded commands", async (args, content) => {
2507
2691
  return CLI.logger.error(`'${mode}' is not a valid option. Valid options: [slash|prefix|ctx]`);
2508
2692
  }
2509
2693
  });
2510
- function initCLI() {
2511
- if (initCalled) return;
2512
- CLI.logger.log(`~ Type ${CLI.options.prefix}help to view available commands`);
2513
- initCalled = true;
2514
- }
2515
2694
 
2516
- // src/client.ts
2517
- import chalk2 from "chalk";
2695
+ // src/client/Vimcord.ts
2696
+ import { Client as Client2 } from "discord.js";
2697
+ import { configDotenv } from "dotenv";
2698
+ import { randomUUID as randomUUID3 } from "crypto";
2699
+ import EventEmitter2 from "events";
2700
+ import { $ as $4 } from "qznt";
2518
2701
  var Vimcord = class _Vimcord extends Client2 {
2519
2702
  static instances = /* @__PURE__ */ new Map();
2703
+ static emitter = new EventEmitter2();
2704
+ clientStartingPromise = null;
2705
+ static create(optionsOrConfig, features, config) {
2706
+ if ("options" in optionsOrConfig) {
2707
+ const { options, features: features2, config: config2 } = optionsOrConfig;
2708
+ return new _Vimcord(options, features2, config2);
2709
+ } else {
2710
+ return new _Vimcord(optionsOrConfig, features, config);
2711
+ }
2712
+ }
2713
+ /**
2714
+ * Returns an instance of Vimcord.
2715
+ * @param clientId [default: 0]
2716
+ */
2717
+ static getInstance(clientId) {
2718
+ if (clientId === void 0) {
2719
+ return _Vimcord.instances.values().next().value;
2720
+ }
2721
+ return _Vimcord.instances.get(clientId);
2722
+ }
2723
+ /**
2724
+ * Waits for a Vimcord instance to be ready.
2725
+ * @param clientId [default: 0]
2726
+ * @param timeoutMs [default: 60000]
2727
+ */
2728
+ static async getReadyInstance(clientId, timeoutMs = 6e4) {
2729
+ const client = _Vimcord.getInstance(clientId);
2730
+ if (client?.isReady()) {
2731
+ _Vimcord.emitter.emit("ready", client);
2732
+ return client;
2733
+ }
2734
+ if (client) {
2735
+ return new Promise((resolve, reject) => {
2736
+ const timeout = setTimeout(() => {
2737
+ _Vimcord.emitter.off("ready", listener);
2738
+ reject(new Error(`Client (i${clientId ?? 0}) timed out waiting for ready`));
2739
+ }, timeoutMs);
2740
+ const listener = (c) => {
2741
+ if (c.clientId === (clientId ?? 0)) {
2742
+ clearTimeout(timeout);
2743
+ _Vimcord.emitter.off("ready", listener);
2744
+ resolve(c);
2745
+ }
2746
+ };
2747
+ client.once("clientReady", () => {
2748
+ clearTimeout(timeout);
2749
+ _Vimcord.emitter.emit("ready", client);
2750
+ resolve(client);
2751
+ });
2752
+ });
2753
+ }
2754
+ return new Promise((resolve, reject) => {
2755
+ const timeout = setTimeout(() => {
2756
+ _Vimcord.emitter.off("ready", listener);
2757
+ reject(new Error(`Vimcord instance (i${clientId ?? 0}) failed to initialize within ${timeoutMs / 1e3}s.`));
2758
+ }, timeoutMs);
2759
+ const listener = (c) => {
2760
+ if (c.clientId === (clientId ?? 0)) {
2761
+ clearTimeout(timeout);
2762
+ _Vimcord.emitter.off("ready", listener);
2763
+ resolve(c);
2764
+ }
2765
+ };
2766
+ _Vimcord.emitter.on("ready", listener);
2767
+ });
2768
+ }
2520
2769
  uuid = randomUUID3();
2521
2770
  clientId = _Vimcord.instances.size;
2522
2771
  clientOptions;
@@ -2526,229 +2775,181 @@ var Vimcord = class _Vimcord extends Client2 {
2526
2775
  events;
2527
2776
  commands;
2528
2777
  db;
2529
- // Configure custom logger
2530
- logger = new Logger({ prefixEmoji: "\u26A1", prefix: `vimcord (i${this.clientId})` }).extend({
2531
- clientBanner(client) {
2532
- if (client.config.app.disableBanner) return;
2533
- const border = "\u2550".repeat(50);
2534
- console.log(chalk2.hex(this.colors.primary)(`
2535
- \u2554${border}\u2557`));
2536
- console.log(
2537
- chalk2.hex(this.colors.primary)("\u2551") + chalk2.bold.hex(this.colors.text)(
2538
- ` \u{1F680} ${client.config.app.name} v${client.config.app.appVersion}`.padEnd(
2539
- 50 - (client.config.app.devMode ? 12 : 0)
2540
- )
2541
- ) + chalk2.hex(this.colors.primary)(
2542
- `${client.config.app.devMode ? chalk2.hex(this.colors.warn)("devMode \u26A0\uFE0F ") : ""}\u2551`
2543
- )
2544
- );
2545
- console.log(chalk2.hex(this.colors.primary)(`\u2551${"".padEnd(50)}\u2551`));
2546
- console.log(
2547
- chalk2.hex(this.colors.primary)("\u2551") + chalk2.hex(this.colors.muted)(
2548
- ` # Powered by Vimcord v${version}`.padEnd(50 - 3 - `${client.clientId}`.length)
2549
- ) + chalk2.hex(this.colors.primary)(`${chalk2.hex(this.colors.muted)(`i${client.clientId}`)} \u2551`)
2550
- );
2551
- console.log(chalk2.hex(this.colors.primary)(`\u255A${border}\u255D
2552
- `));
2553
- },
2554
- clientReady(clientTag, guildCount) {
2555
- console.log(
2556
- this.formatTimestamp(),
2557
- this.formatPrefix(),
2558
- chalk2.hex(this.colors.success)("\u{1F916} READY"),
2559
- chalk2.white(`Connected as ${chalk2.bold.hex(this.colors.primary)(clientTag)}`),
2560
- chalk2.hex(this.colors.muted)(`\u2022 ${guildCount} guilds`)
2561
- );
2562
- },
2563
- moduleLoaded(moduleName, count, ignoredCount) {
2564
- const countText = count ? chalk2.hex(this.colors.muted)(`(${count} items)`) : "";
2565
- console.log(
2566
- this.formatTimestamp(),
2567
- this.formatPrefix(),
2568
- chalk2.hex("#9B59B6")("\u{1F4E6} MODULE"),
2569
- chalk2.hex(this.colors.warn)(`${moduleName} loaded`),
2570
- ignoredCount ? chalk2.hex(this.colors.muted)(`(${ignoredCount} ignored)`) : "",
2571
- countText
2572
- );
2573
- },
2574
- commandExecuted(commandName, username, guildName) {
2575
- const location = guildName ? `in ${chalk2.hex(this.colors.muted)(guildName)}` : "in DMs";
2576
- console.log(
2577
- this.formatTimestamp(),
2578
- this.formatPrefix(),
2579
- chalk2.hex("#87CEEB")("\u{1F4DD} COMMAND"),
2580
- chalk2.hex(this.colors.warn)(`/${commandName}`),
2581
- chalk2.white(`used by ${chalk2.bold(username)}`),
2582
- chalk2.hex(this.colors.muted)(location)
2583
- );
2584
- },
2585
- database(action, details) {
2586
- console.log(
2587
- this.formatTimestamp(),
2588
- this.formatPrefix(),
2589
- chalk2.hex("#FF6B9D")("\u{1F5C4}\uFE0F DATABASE"),
2590
- chalk2.white(action),
2591
- details ? chalk2.hex(this.colors.muted)(details) : ""
2592
- );
2593
- }
2594
- });
2595
- clientStartingPromise = null;
2778
+ logger = clientLoggerFactory(this);
2779
+ error;
2596
2780
  constructor(options, features = {}, config = {}) {
2597
2781
  super(options);
2598
2782
  this.clientOptions = options;
2599
2783
  this.features = features;
2600
- if (this.features.useEnv) {
2601
- if (typeof this.features.useEnv === "object") {
2602
- dotEnv.config({ quiet: true, ...this.features.useEnv });
2603
- } else {
2604
- dotEnv.config({ quiet: true });
2605
- }
2606
- }
2784
+ this.config = defineVimcordConfig(config);
2785
+ this.error = new ErrorHandler(this);
2607
2786
  if (this.features.useGlobalErrorHandlers) {
2608
- process.on("uncaughtException", (err) => this.logger.error("Uncaught Exception", err));
2609
- process.on("unhandledRejection", (err) => this.logger.error("Unhandled Rejection", err));
2610
- process.on("exit", (code) => this.logger.debug(`Process exited with code ${code}`));
2611
- this.on("error", (err) => this.logger.error("Client Error", err));
2612
- this.on("shardError", (err) => this.logger.error("Client Shard Error", err));
2613
- }
2614
- this.config = {
2615
- app: createAppConfig(config.app),
2616
- staff: createStaffConfig(config.staff),
2617
- slashCommands: createSlashCommandConfig(config.slashCommands),
2618
- prefixCommands: createPrefixCommandConfig(config.prefixCommands),
2619
- contextCommands: createContextCommandConfig(config.contextCommands)
2620
- };
2787
+ this.error.setupGlobalHandlers();
2788
+ }
2621
2789
  this.status = new StatusManager(this);
2622
2790
  this.events = new EventManager(this);
2623
2791
  this.commands = new CommandManager(this);
2624
2792
  this.logger.clientBanner(this);
2625
- this.once("clientReady", (client) => {
2626
- this.logger.clientReady(client.user.tag, client.guilds.cache.size);
2627
- });
2793
+ this.once("clientReady", (client) => this.logger.clientReady(client.user.tag, client.guilds.cache.size));
2628
2794
  _Vimcord.instances.set(this.clientId, this);
2629
- initCLI();
2795
+ if (this.config.app.enableCLI) {
2796
+ VimcordCLI.setMode("on");
2797
+ }
2630
2798
  }
2631
- /** Returns the options, features, and config of this client. */
2632
- toJSON() {
2633
- return {
2634
- options: this.clientOptions,
2635
- features: this.features,
2636
- config: this.config
2637
- };
2799
+ /** Current app name */
2800
+ // prettier-ignore
2801
+ get $name() {
2802
+ return this.config.app.name;
2638
2803
  }
2639
- /** Makes a clone of this client. */
2640
- clone() {
2641
- const { options, features, config } = this.toJSON();
2642
- return new _Vimcord(options, features, config);
2804
+ // prettier-ignore
2805
+ set $name(name) {
2806
+ this.config.app.name = name;
2643
2807
  }
2644
- configureApp(options = {}) {
2645
- this.config.app = createAppConfig(options);
2646
- if (this.features.hookToolsDevMode) {
2647
- globalToolsConfig.devMode = this.config.app.devMode;
2648
- }
2649
- return this;
2808
+ /** Current app version */
2809
+ // prettier-ignore
2810
+ get $version() {
2811
+ return this.config.app.version;
2650
2812
  }
2651
- configureStaff(options = {}) {
2652
- this.config.staff = createStaffConfig(options);
2653
- return this;
2813
+ // prettier-ignore
2814
+ set $version(version2) {
2815
+ this.config.app.version = version2;
2654
2816
  }
2655
- configureSlashCommands(options = {}) {
2656
- this.config.slashCommands = createSlashCommandConfig(options);
2657
- return this;
2817
+ /** Current dev mode state */
2818
+ // prettier-ignore
2819
+ get $devMode() {
2820
+ return this.config.app.devMode;
2658
2821
  }
2659
- configurePrefixCommands(options = {}) {
2660
- this.config.prefixCommands = createPrefixCommandConfig(options);
2661
- return this;
2822
+ // prettier-ignore
2823
+ set $devMode(mode) {
2824
+ this.config.app.devMode = mode;
2662
2825
  }
2663
- configureContextCommands(options = {}) {
2664
- this.config.contextCommands = createContextCommandConfig(options);
2665
- return this;
2826
+ /** Current verbose mode state */
2827
+ // prettier-ignore
2828
+ get $verboseMode() {
2829
+ return this.config.app.verbose;
2666
2830
  }
2667
- async importEventModules(dir, replaceAll) {
2668
- await this.events.importFrom(dir, replaceAll);
2669
- return this;
2831
+ // prettier-ignore
2832
+ set $verboseMode(mode) {
2833
+ this.config.app.verbose = mode;
2670
2834
  }
2671
- async importSlashCommandModules(dir, replaceAll) {
2672
- await this.commands.slash.importFrom(dir, replaceAll);
2673
- return this;
2835
+ /** Returns the options, features, and config of this client. */
2836
+ toJSON() {
2837
+ return { options: this.clientOptions, features: this.features, config: this.config };
2674
2838
  }
2675
- async importPrefixCommandModules(dir, replaceAll) {
2676
- await this.commands.prefix.importFrom(dir, replaceAll);
2677
- return this;
2839
+ /** Makes a clone of this client. */
2840
+ clone() {
2841
+ const { options, features, config } = this.toJSON();
2842
+ return new _Vimcord(options, features, config);
2678
2843
  }
2679
- async importContextCommandModules(dir, replaceAll) {
2680
- await this.commands.context.importFrom(dir, replaceAll);
2844
+ /**
2845
+ * Modifies a client config.
2846
+ * @param type The type of config to modify.
2847
+ * @param options The options to set for the config.
2848
+ */
2849
+ configure(type, options = {}) {
2850
+ this.config[type] = configSetters[type](options, this.config[type]);
2681
2851
  return this;
2682
2852
  }
2683
- async useDatabase(db) {
2684
- this.db = db;
2685
- this.logger.database("Using", db.moduleName);
2686
- return this.db.connect();
2687
- }
2688
- async waitForReady() {
2689
- if (this.isReady()) return this;
2690
- return new Promise((resolve, reject) => {
2691
- const timeout = setTimeout(
2692
- () => reject(new Error(`Client (i${this.clientId}) timed out waiting for ready`)),
2693
- 6e4
2694
- );
2695
- this.once("clientReady", () => {
2696
- clearTimeout(timeout);
2697
- resolve(this);
2698
- });
2699
- });
2700
- }
2853
+ /** Builds the client by importing modules and registering builtin handlers. */
2701
2854
  async build() {
2702
- this.configureApp(this.config.app);
2703
- this.configureStaff(this.config.staff);
2704
- this.configureSlashCommands(this.config.slashCommands);
2705
- this.configurePrefixCommands(this.config.prefixCommands);
2706
- this.configureContextCommands(this.config.contextCommands);
2855
+ this.configure("app", this.config.app);
2856
+ this.configure("staff", this.config.staff);
2857
+ this.configure("slashCommands", this.config.slashCommands);
2858
+ this.configure("prefixCommands", this.config.prefixCommands);
2859
+ this.configure("contextCommands", this.config.contextCommands);
2707
2860
  if (this.features.importModules) {
2708
2861
  const importModules = this.features.importModules;
2709
2862
  await Promise.all([
2710
- importModules.events && this.importEventModules(importModules.events),
2711
- importModules.slashCommands && this.importSlashCommandModules(importModules.slashCommands),
2712
- importModules.prefixCommands && this.importPrefixCommandModules(importModules.prefixCommands),
2713
- importModules.contextCommands && this.importContextCommandModules(importModules.contextCommands)
2863
+ importModules.events && this.importModules("events", importModules.events),
2864
+ importModules.slashCommands && this.importModules("slashCommands", importModules.slashCommands),
2865
+ importModules.prefixCommands && this.importModules("prefixCommands", importModules.prefixCommands),
2866
+ importModules.contextCommands && this.importModules("contextCommands", importModules.contextCommands)
2714
2867
  ]);
2715
2868
  }
2716
2869
  if (this.features.useDefaultSlashCommandHandler) {
2717
- this.events.register(BUILTIN_SlashCommandHandler);
2870
+ this.events.register(slashCommandHandler);
2718
2871
  }
2719
2872
  if (this.features.useDefaultContextCommandHandler) {
2720
- this.events.register(BUILTIN_ContextCommandHandler);
2873
+ this.events.register(contextCommandHandler);
2721
2874
  }
2722
2875
  if (this.features.useDefaultPrefixCommandHandler) {
2723
- this.events.register(BUILTIN_PrefixCommandHandler);
2876
+ this.events.register(prefixCommandHandler);
2724
2877
  }
2725
2878
  return this;
2726
2879
  }
2727
- async start(tokenOrPreHook, preHook) {
2880
+ /**
2881
+ * Imports modules into the client.
2882
+ * @param type The type of modules to import.
2883
+ * @param options The options to import the module with.
2884
+ * @param set Replaces already imported modules with the ones found.
2885
+ */
2886
+ async importModules(type, options, set) {
2887
+ await moduleImporters[type](this, options, set);
2888
+ return this;
2889
+ }
2890
+ /**
2891
+ * Allows Vimcord to handle environment variables using [dotenv](https://www.npmjs.com/package/dotenv).
2892
+ * @param options Options for dotenv
2893
+ * @see https://www.npmjs.com/package/dotenv
2894
+ */
2895
+ useEnv(options) {
2896
+ this.logger.database("Using", "dotenv");
2897
+ configDotenv({ quiet: true, ...options });
2898
+ return this;
2899
+ }
2900
+ /**
2901
+ * Connects to a database.
2902
+ * @param db The database manager to use.
2903
+ */
2904
+ async useDatabase(db) {
2905
+ this.db = db;
2906
+ this.logger.database("Using", db.moduleName);
2907
+ return this.db.connect();
2908
+ }
2909
+ /**
2910
+ * Fetches a user from the client, checking the cache first.
2911
+ * @param userId The ID of the user to fetch.
2912
+ */
2913
+ async fetchUser(userId) {
2914
+ const client = await _Vimcord.getReadyInstance(this.clientId);
2915
+ return fetchUser(client, userId);
2916
+ }
2917
+ /**
2918
+ * Fetches a guild from the client, checking the cache first.
2919
+ * @param guildId The ID of the guild to fetch.
2920
+ */
2921
+ async fetchGuild(guildId) {
2922
+ const client = await _Vimcord.getReadyInstance(this.clientId);
2923
+ return fetchGuild(client, guildId);
2924
+ }
2925
+ async start(tokenOrPreHook, callback) {
2728
2926
  if (this.clientStartingPromise) return this.clientStartingPromise;
2729
- const main = async () => {
2927
+ const execute = async () => {
2730
2928
  let token = typeof tokenOrPreHook === "string" ? tokenOrPreHook : void 0;
2731
- token ??= this.config.app.devMode ? process.env.TOKEN_DEV : process.env.TOKEN;
2929
+ token ??= this.$devMode ? process.env.TOKEN_DEV : process.env.TOKEN;
2732
2930
  if (!token) {
2733
2931
  throw new Error(
2734
- `TOKEN Missing: ${this.config.app.devMode ? "devMode is enabled, but TOKEN_DEV is not set" : "TOKEN not set"}`
2932
+ `TOKEN Missing: ${this.$devMode ? "devMode is enabled, but TOKEN_DEV is not set" : "TOKEN not set"}`
2735
2933
  );
2736
2934
  }
2737
2935
  await this.build();
2738
2936
  try {
2937
+ const stopLoader = this.logger.loader("Connecting to Discord...");
2938
+ const loginResult = await $4.async.retry(() => super.login(token), {
2939
+ retries: this.features.maxLoginAttempts ?? 3,
2940
+ delay: 1e3
2941
+ });
2942
+ stopLoader("Connected to Discord ");
2943
+ this.$verboseMode && this.logger.debug("Waiting for ready...");
2739
2944
  if (typeof tokenOrPreHook === "function") {
2740
2945
  await tokenOrPreHook(this);
2741
2946
  } else {
2742
- await preHook?.(this);
2947
+ await callback?.(this);
2743
2948
  }
2744
- const stopLoader = this.logger.loader("Connecting to Discord...");
2745
- const loginResult = await $4.async.retry(() => super.login(token), this.features.loginAttempts ?? 3, 1e3);
2746
- stopLoader("Connected to Discord ");
2747
- this.config.app.verbose && this.logger.debug("\u23F3 Waiting for ready...");
2748
2949
  return loginResult;
2749
2950
  } catch (err) {
2750
2951
  this.logger.error(
2751
- `Failed to log into Discord after ${this.features.loginAttempts} attempt(s))`,
2952
+ `Failed to log into Discord after ${this.features.maxLoginAttempts} attempt(s)`,
2752
2953
  err
2753
2954
  );
2754
2955
  return null;
@@ -2756,29 +2957,27 @@ var Vimcord = class _Vimcord extends Client2 {
2756
2957
  this.clientStartingPromise = null;
2757
2958
  }
2758
2959
  };
2759
- this.clientStartingPromise = main();
2960
+ this.clientStartingPromise = execute();
2760
2961
  return this.clientStartingPromise;
2761
2962
  }
2963
+ /** Destroys the client and disconnects from Discord. */
2762
2964
  async kill() {
2763
2965
  await super.destroy();
2764
2966
  _Vimcord.instances.delete(this.clientId);
2765
- this.logger.debug("\u{1F6AA} Logged out of Discord");
2766
- }
2767
- /** Shortcut for {@link fetchUser tools.fetchUser} */
2768
- async fetchUser(id) {
2769
- const client = await this.waitForReady();
2770
- return fetchUser(client, id);
2771
- }
2772
- /** Shortcut for {@link fetchGuild tools.fetchGuild} */
2773
- async fetchGuild(id) {
2774
- const client = await this.waitForReady();
2775
- return fetchGuild(client, id);
2967
+ this.logger.debug("Logged out of Discord");
2776
2968
  }
2777
2969
  };
2778
2970
 
2779
- // src/modules/db/mongo/mongo.ts
2971
+ // src/db/mongo/mongo-schema.builder.ts
2972
+ import {
2973
+ Schema
2974
+ } from "mongoose";
2975
+ import { randomBytes } from "crypto";
2976
+ import { $ as $6 } from "qznt";
2977
+
2978
+ // src/db/mongo/mongo.database.ts
2780
2979
  import mongoose, { ConnectionStates } from "mongoose";
2781
- import EventEmitter2 from "events";
2980
+ import EventEmitter3 from "events";
2782
2981
  import { $ as $5 } from "qznt";
2783
2982
  try {
2784
2983
  import("mongoose");
@@ -2787,7 +2986,7 @@ try {
2787
2986
  }
2788
2987
  var MongoDatabase = class _MongoDatabase {
2789
2988
  static instances = /* @__PURE__ */ new Map();
2790
- static emitter = new EventEmitter2();
2989
+ static emitter = new EventEmitter3();
2791
2990
  moduleName = "MongoDatabase";
2792
2991
  clientId;
2793
2992
  client;
@@ -2872,10 +3071,9 @@ var MongoDatabase = class _MongoDatabase {
2872
3071
  this.isConnecting = true;
2873
3072
  try {
2874
3073
  const stopLoader = this.client.logger.loader("Connecting to MongoDB...");
2875
- await $5.async.retry(
2876
- () => this.mongoose.connect(connectionUri, { autoIndex: true, ...connectionOptions }),
2877
- maxRetries
2878
- );
3074
+ await $5.async.retry(() => this.mongoose.connect(connectionUri, { autoIndex: true, ...connectionOptions }), {
3075
+ retries: maxRetries
3076
+ });
2879
3077
  _MongoDatabase.emitter.emit("ready", this);
2880
3078
  stopLoader("Connected to MongoDB ");
2881
3079
  } catch (err) {
@@ -2906,12 +3104,7 @@ var MongoDatabase = class _MongoDatabase {
2906
3104
  }
2907
3105
  };
2908
3106
 
2909
- // src/modules/db/mongo/mongoSchema.builder.ts
2910
- import {
2911
- Schema
2912
- } from "mongoose";
2913
- import { randomBytes } from "crypto";
2914
- import { $ as $6 } from "qznt";
3107
+ // src/db/mongo/mongo-schema.builder.ts
2915
3108
  try {
2916
3109
  import("mongoose");
2917
3110
  } catch {
@@ -2997,10 +3190,13 @@ var MongoSchemaBuilder = class _MongoSchemaBuilder {
2997
3190
  * @param maxRetries [default: 3]
2998
3191
  */
2999
3192
  async execute(fn, maxRetries = 3) {
3000
- return await $6.async.retry(async () => {
3001
- const model = await this.getModel();
3002
- return await fn(model);
3003
- }, maxRetries);
3193
+ return await $6.async.retry(
3194
+ async () => {
3195
+ const model = await this.getModel();
3196
+ return await fn(model);
3197
+ },
3198
+ { retries: maxRetries }
3199
+ );
3004
3200
  }
3005
3201
  async startSession(options) {
3006
3202
  return await this.execute(async () => {
@@ -3630,7 +3826,7 @@ import {
3630
3826
  EmbedBuilder as EmbedBuilder2,
3631
3827
  StringSelectMenuBuilder as StringSelectMenuBuilder2
3632
3828
  } from "discord.js";
3633
- import EventEmitter3 from "events";
3829
+ import EventEmitter4 from "events";
3634
3830
  var PaginationType = /* @__PURE__ */ ((PaginationType2) => {
3635
3831
  PaginationType2[PaginationType2["Short"] = 0] = "Short";
3636
3832
  PaginationType2[PaginationType2["ShortJump"] = 1] = "ShortJump";
@@ -3675,7 +3871,7 @@ var Paginator = class {
3675
3871
  config;
3676
3872
  data;
3677
3873
  events;
3678
- eventEmitter = new EventEmitter3();
3874
+ eventEmitter = new EventEmitter4();
3679
3875
  constructor(options = {}) {
3680
3876
  this.config = options.config ? createToolsConfig(options.config) : globalToolsConfig;
3681
3877
  this.options = {
@@ -4291,9 +4487,6 @@ async function prompt(handler, options, sendOptions) {
4291
4487
  return await p.awaitResponse();
4292
4488
  }
4293
4489
  export {
4294
- BUILTIN_ContextCommandHandler,
4295
- BUILTIN_PrefixCommandHandler,
4296
- BUILTIN_SlashCommandHandler,
4297
4490
  BaseCommandBuilder,
4298
4491
  BaseCommandManager,
4299
4492
  BetterCollector,
@@ -4306,13 +4499,16 @@ export {
4306
4499
  CommandType,
4307
4500
  ContextCommandBuilder,
4308
4501
  ContextCommandManager,
4502
+ DEFAULT_MODULE_SUFFIXES,
4309
4503
  DynaSend,
4504
+ ErrorHandler,
4310
4505
  EventBuilder,
4311
4506
  EventManager,
4312
4507
  LOGGER_COLORS,
4313
4508
  LogLevel,
4314
4509
  Logger,
4315
4510
  MissingPermissionReason,
4511
+ ModuleImporter,
4316
4512
  MongoDatabase,
4317
4513
  MongoSchemaBuilder,
4318
4514
  PaginationTimeoutType,
@@ -4332,8 +4528,12 @@ export {
4332
4528
  VimcordCLI,
4333
4529
  __zero,
4334
4530
  cleanMention,
4531
+ clientLoggerFactory,
4532
+ configSetters,
4533
+ contextCommandHandler,
4335
4534
  createAppConfig,
4336
4535
  createClient,
4536
+ createConfigFactory,
4337
4537
  createContextCommandConfig,
4338
4538
  createMongoPlugin,
4339
4539
  createMongoSchema,
@@ -4342,7 +4542,10 @@ export {
4342
4542
  createStaffConfig,
4343
4543
  createToolsConfig,
4344
4544
  createVimcordStatusConfig,
4545
+ defineClientOptions,
4345
4546
  defineGlobalToolsConfig,
4547
+ defineVimcordConfig,
4548
+ defineVimcordFeatures,
4346
4549
  dynaSend,
4347
4550
  fetchChannel,
4348
4551
  fetchGuild,
@@ -4350,17 +4553,20 @@ export {
4350
4553
  fetchMessage,
4351
4554
  fetchRole,
4352
4555
  fetchUser,
4353
- getCallerFileName,
4556
+ getDevMode,
4354
4557
  getFirstMentionId,
4355
4558
  getMessageMention,
4559
+ getPackageJson,
4356
4560
  getProcessDir,
4357
4561
  globalToolsConfig,
4358
4562
  importModulesFromDir,
4359
- initCLI,
4360
4563
  isMentionOrSnowflake,
4361
4564
  logger,
4565
+ moduleImporters,
4566
+ prefixCommandHandler,
4362
4567
  prompt,
4363
4568
  sendCommandErrorEmbed,
4569
+ slashCommandHandler,
4364
4570
  useClient,
4365
4571
  useReadyClient,
4366
4572
  validateCommandPermissions