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.cjs CHANGED
@@ -30,9 +30,6 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
- BUILTIN_ContextCommandHandler: () => BUILTIN_ContextCommandHandler,
34
- BUILTIN_PrefixCommandHandler: () => BUILTIN_PrefixCommandHandler,
35
- BUILTIN_SlashCommandHandler: () => BUILTIN_SlashCommandHandler,
36
33
  BaseCommandBuilder: () => BaseCommandBuilder,
37
34
  BaseCommandManager: () => BaseCommandManager,
38
35
  BetterCollector: () => BetterCollector,
@@ -45,13 +42,16 @@ __export(index_exports, {
45
42
  CommandType: () => CommandType,
46
43
  ContextCommandBuilder: () => ContextCommandBuilder,
47
44
  ContextCommandManager: () => ContextCommandManager,
45
+ DEFAULT_MODULE_SUFFIXES: () => DEFAULT_MODULE_SUFFIXES,
48
46
  DynaSend: () => DynaSend,
47
+ ErrorHandler: () => ErrorHandler,
49
48
  EventBuilder: () => EventBuilder,
50
49
  EventManager: () => EventManager,
51
50
  LOGGER_COLORS: () => LOGGER_COLORS,
52
51
  LogLevel: () => LogLevel,
53
52
  Logger: () => Logger,
54
53
  MissingPermissionReason: () => MissingPermissionReason,
54
+ ModuleImporter: () => ModuleImporter,
55
55
  MongoDatabase: () => MongoDatabase,
56
56
  MongoSchemaBuilder: () => MongoSchemaBuilder,
57
57
  PaginationTimeoutType: () => PaginationTimeoutType,
@@ -71,8 +71,12 @@ __export(index_exports, {
71
71
  VimcordCLI: () => VimcordCLI,
72
72
  __zero: () => __zero,
73
73
  cleanMention: () => cleanMention,
74
+ clientLoggerFactory: () => clientLoggerFactory,
75
+ configSetters: () => configSetters,
76
+ contextCommandHandler: () => contextCommandHandler,
74
77
  createAppConfig: () => createAppConfig,
75
78
  createClient: () => createClient,
79
+ createConfigFactory: () => createConfigFactory,
76
80
  createContextCommandConfig: () => createContextCommandConfig,
77
81
  createMongoPlugin: () => createMongoPlugin,
78
82
  createMongoSchema: () => createMongoSchema,
@@ -81,7 +85,10 @@ __export(index_exports, {
81
85
  createStaffConfig: () => createStaffConfig,
82
86
  createToolsConfig: () => createToolsConfig,
83
87
  createVimcordStatusConfig: () => createVimcordStatusConfig,
88
+ defineClientOptions: () => defineClientOptions,
84
89
  defineGlobalToolsConfig: () => defineGlobalToolsConfig,
90
+ defineVimcordConfig: () => defineVimcordConfig,
91
+ defineVimcordFeatures: () => defineVimcordFeatures,
85
92
  dynaSend: () => dynaSend,
86
93
  fetchChannel: () => fetchChannel,
87
94
  fetchGuild: () => fetchGuild,
@@ -89,23 +96,29 @@ __export(index_exports, {
89
96
  fetchMessage: () => fetchMessage,
90
97
  fetchRole: () => fetchRole,
91
98
  fetchUser: () => fetchUser,
92
- getCallerFileName: () => getCallerFileName,
99
+ getDevMode: () => getDevMode,
93
100
  getFirstMentionId: () => getFirstMentionId,
94
101
  getMessageMention: () => getMessageMention,
102
+ getPackageJson: () => getPackageJson,
95
103
  getProcessDir: () => getProcessDir,
96
104
  globalToolsConfig: () => globalToolsConfig,
97
105
  importModulesFromDir: () => importModulesFromDir,
98
- initCLI: () => initCLI,
99
106
  isMentionOrSnowflake: () => isMentionOrSnowflake,
100
107
  logger: () => logger,
108
+ moduleImporters: () => moduleImporters,
109
+ prefixCommandHandler: () => prefixCommandHandler,
101
110
  prompt: () => prompt,
102
111
  sendCommandErrorEmbed: () => sendCommandErrorEmbed,
112
+ slashCommandHandler: () => slashCommandHandler,
103
113
  useClient: () => useClient,
104
114
  useReadyClient: () => useReadyClient,
105
115
  validateCommandPermissions: () => validateCommandPermissions
106
116
  });
107
117
  module.exports = __toCommonJS(index_exports);
108
118
 
119
+ // src/modules/validators/permissions.validator.ts
120
+ var import_discord = require("discord.js");
121
+
109
122
  // src/types/command.base.ts
110
123
  var CommandType = /* @__PURE__ */ ((CommandType2) => {
111
124
  CommandType2[CommandType2["Slash"] = 0] = "Slash";
@@ -133,8 +146,7 @@ var RateLimitScope = /* @__PURE__ */ ((RateLimitScope2) => {
133
146
  return RateLimitScope2;
134
147
  })(RateLimitScope || {});
135
148
 
136
- // src/validators/permissions.validator.ts
137
- var import_discord = require("discord.js");
149
+ // src/modules/validators/permissions.validator.ts
138
150
  function __existsAndTrue(value) {
139
151
  return value !== void 0 && value;
140
152
  }
@@ -233,8 +245,8 @@ function validateCommandPermissions(permissions, client, user, command) {
233
245
  }
234
246
 
235
247
  // src/builders/baseCommand.builder.ts
236
- var import_node_crypto = require("crypto");
237
248
  var import_lodash = __toESM(require("lodash"));
249
+ var import_node_crypto = require("crypto");
238
250
  var BaseCommandBuilder = class {
239
251
  uuid = (0, import_node_crypto.randomUUID)();
240
252
  commandType;
@@ -524,55 +536,199 @@ var ContextCommandBuilder = class extends BaseCommandBuilder {
524
536
  }
525
537
  };
526
538
 
527
- // src/utils/dir.ts
528
- var import_node_path = __toESM(require("path"));
529
- var import_qznt = require("qznt");
530
- function getCallerFileName() {
531
- const stack = new Error().stack?.split("\n");
532
- return stack?.at(4)?.split("at file")?.at(1)?.split("/").at(-1)?.split(":").at(0)?.split(".").at(0);
533
- }
534
- function getProcessDir() {
535
- const mainPath = process.argv[1];
536
- if (!mainPath) return "";
537
- return import_node_path.default.dirname(mainPath);
538
- }
539
- async function importModulesFromDir(dir, suffix) {
540
- const cwd = getProcessDir();
541
- const MODULE_RELATIVE_PATH = import_node_path.default.join(cwd, dir);
542
- const MODULE_LOG_PATH = dir;
543
- const files = import_qznt.$.fs.readDir(MODULE_RELATIVE_PATH).filter((fn) => fn.endsWith(`${suffix ? `.${suffix}` : ""}.js`) || fn.endsWith(`${suffix ? `.${suffix}` : ""}.ts`));
544
- if (!files.length) {
545
- return [];
539
+ // src/builders/event.builder.ts
540
+ var import_node_crypto2 = require("crypto");
541
+ var import_lodash3 = __toESM(require("lodash"));
542
+
543
+ // src/tools/Logger.ts
544
+ var import_chalk = __toESM(require("chalk"));
545
+ var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
546
+ LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
547
+ LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
548
+ LogLevel2[LogLevel2["SUCCESS"] = 2] = "SUCCESS";
549
+ LogLevel2[LogLevel2["WARN"] = 3] = "WARN";
550
+ LogLevel2[LogLevel2["ERROR"] = 4] = "ERROR";
551
+ return LogLevel2;
552
+ })(LogLevel || {});
553
+ var LOGGER_COLORS = {
554
+ primary: "#5865F2",
555
+ success: "#57F287",
556
+ warn: "#FEE75C",
557
+ danger: "#ED4245",
558
+ muted: "#747F8D",
559
+ text: "#FFFFFF"
560
+ };
561
+ var Logger = class {
562
+ logPrefixEmoji;
563
+ logPrefix;
564
+ minLevel;
565
+ showTimestamp;
566
+ colorScheme;
567
+ constructor(options) {
568
+ const { prefixEmoji = null, prefix = null, minLevel = 0 /* DEBUG */, showTimestamp = true } = options || {};
569
+ this.logPrefixEmoji = prefixEmoji;
570
+ this.logPrefix = prefix;
571
+ this.minLevel = minLevel;
572
+ this.showTimestamp = showTimestamp;
573
+ this.colorScheme = {
574
+ ...LOGGER_COLORS,
575
+ ...options?.colors
576
+ };
546
577
  }
547
- const modules = await Promise.all(
548
- files.map(async (fn) => {
549
- let _path = import_node_path.default.join(MODULE_RELATIVE_PATH, fn);
550
- let _logPath = `./${import_node_path.default.join(MODULE_LOG_PATH, fn)}`;
551
- let _module;
552
- try {
553
- delete require.cache[require.resolve(_path)];
554
- _module = require(_path);
555
- } catch (err) {
556
- console.warn(`Failed to import module at '${_logPath}'`, err);
557
- _module = null;
578
+ formatTimestamp() {
579
+ if (!this.showTimestamp) return "";
580
+ const now = /* @__PURE__ */ new Date();
581
+ const time = now.toLocaleTimeString("en-US", {
582
+ hour12: false,
583
+ hour: "2-digit",
584
+ minute: "2-digit",
585
+ second: "2-digit"
586
+ });
587
+ return import_chalk.default.hex(this.colorScheme.muted)(`[${time}]`);
588
+ }
589
+ formatPrefix() {
590
+ if (!this.logPrefix) return "";
591
+ return import_chalk.default.bold.hex(this.colorScheme.primary)(
592
+ `${this.logPrefixEmoji ? `${this.logPrefixEmoji} ` : ""}${this.logPrefix}`
593
+ );
594
+ }
595
+ shouldLog(level) {
596
+ return level >= this.minLevel;
597
+ }
598
+ get prefixEmoji() {
599
+ return this.logPrefixEmoji;
600
+ }
601
+ get prefix() {
602
+ return this.logPrefix;
603
+ }
604
+ get colors() {
605
+ return this.colorScheme;
606
+ }
607
+ extend(extras) {
608
+ for (const [key, fn] of Object.entries(extras)) {
609
+ if (typeof fn === "function") {
610
+ this[key] = function(...args) {
611
+ return fn.call(this, ...args);
612
+ };
558
613
  }
559
- return { module: _module, path: _logPath };
560
- })
561
- );
562
- const filteredModules = modules.filter((m) => m.module);
563
- if (!filteredModules.length) {
564
- console.warn(`No valid modules were found in directory '${dir}'`);
614
+ }
615
+ return this;
565
616
  }
566
- return filteredModules;
567
- }
617
+ setPrefix(prefix) {
618
+ this.logPrefix = prefix;
619
+ return this;
620
+ }
621
+ setPrefixEmoji(prefixEmoji) {
622
+ this.logPrefixEmoji = prefixEmoji;
623
+ return this;
624
+ }
625
+ setMinLevel(minLevel) {
626
+ this.minLevel = minLevel;
627
+ return this;
628
+ }
629
+ setShowTimestamp(show) {
630
+ this.showTimestamp = show;
631
+ return this;
632
+ }
633
+ setColors(colors) {
634
+ this.colorScheme = {
635
+ ...LOGGER_COLORS,
636
+ ...colors
637
+ };
638
+ return this;
639
+ }
640
+ log(message, ...args) {
641
+ console.log(this.formatTimestamp(), this.formatPrefix(), message, ...args);
642
+ }
643
+ debug(message, ...args) {
644
+ if (!this.shouldLog(0 /* DEBUG */)) return;
645
+ console.log(
646
+ this.formatTimestamp(),
647
+ this.formatPrefix(),
648
+ import_chalk.default.hex(this.colorScheme.muted)("DEBUG"),
649
+ import_chalk.default.dim(message),
650
+ ...args
651
+ );
652
+ }
653
+ info(message, ...args) {
654
+ if (!this.shouldLog(1 /* INFO */)) return;
655
+ console.log(this.formatTimestamp(), this.formatPrefix(), import_chalk.default.hex("#87CEEB")("INFO"), message, ...args);
656
+ }
657
+ success(message, ...args) {
658
+ if (!this.shouldLog(2 /* SUCCESS */)) return;
659
+ console.log(
660
+ this.formatTimestamp(),
661
+ this.formatPrefix(),
662
+ import_chalk.default.bold.hex(this.colorScheme.success)("\u2713 SUCCESS"),
663
+ import_chalk.default.hex(this.colorScheme.success)(message),
664
+ ...args
665
+ );
666
+ }
667
+ warn(message, ...args) {
668
+ if (!this.shouldLog(3 /* WARN */)) return;
669
+ console.warn(
670
+ this.formatTimestamp(),
671
+ this.formatPrefix(),
672
+ import_chalk.default.bold.hex(this.colorScheme.warn)("\u26A0 WARN"),
673
+ import_chalk.default.hex(this.colorScheme.warn)(message),
674
+ ...args
675
+ );
676
+ }
677
+ error(message, error, ...args) {
678
+ if (!this.shouldLog(4 /* ERROR */)) return;
679
+ console.error(
680
+ this.formatTimestamp(),
681
+ this.formatPrefix(),
682
+ import_chalk.default.bold.hex(this.colorScheme.danger)("\u2715 ERROR"),
683
+ import_chalk.default.hex(this.colorScheme.danger)(message),
684
+ ...args
685
+ );
686
+ if (error && error.stack) {
687
+ console.error(import_chalk.default.dim(error.stack));
688
+ }
689
+ }
690
+ loader(message) {
691
+ const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
692
+ let i = 0;
693
+ const interval = setInterval(() => {
694
+ process.stdout.write(
695
+ `\r${this.formatTimestamp()} ${this.formatPrefix()} ${import_chalk.default.hex(this.colorScheme.warn)(frames[i])} ${message}`
696
+ );
697
+ i = (i + 1) % frames.length;
698
+ }, 100);
699
+ return (newMessage) => {
700
+ clearInterval(interval);
701
+ process.stdout.write(
702
+ `\r${this.formatTimestamp()} ${this.formatPrefix()} ${import_chalk.default.hex(this.colorScheme.success)("\u2713")} ${newMessage || message}
703
+ `
704
+ );
705
+ };
706
+ }
707
+ table(title, data) {
708
+ console.log(this.formatTimestamp(), this.formatPrefix(), import_chalk.default.bold(title));
709
+ Object.entries(data).forEach(([key, value]) => {
710
+ const formattedKey = import_chalk.default.hex(this.colorScheme.warn)(` ${key}`);
711
+ const formattedValue = import_chalk.default.hex(this.colorScheme.muted)(value);
712
+ console.log(`${formattedKey.padEnd(25)} ${formattedValue}`);
713
+ });
714
+ }
715
+ section(title) {
716
+ const line = "\u2500".repeat(Math.max(30, title.length + 4));
717
+ console.log(import_chalk.default.hex(this.colorScheme.muted)(`
718
+ \u250C\u2500${line}\u2500\u2510`));
719
+ console.log(
720
+ import_chalk.default.hex(this.colorScheme.muted)("\u2502 ") + import_chalk.default.bold.hex(this.colorScheme.text)(title.padEnd(line.length)) + import_chalk.default.hex(this.colorScheme.muted)(" \u2502")
721
+ );
722
+ console.log(import_chalk.default.hex(this.colorScheme.muted)(`\u2514\u2500${line}\u2500\u2518`));
723
+ }
724
+ };
725
+ var logger = new Logger();
568
726
 
569
727
  // src/builders/event.builder.ts
570
- var import_node_crypto2 = require("crypto");
571
- var import_lodash3 = __toESM(require("lodash"));
572
728
  var EventBuilder = class _EventBuilder {
573
729
  uuid = (0, import_node_crypto2.randomUUID)();
574
730
  event;
575
- name = getCallerFileName() || this.uuid;
731
+ name = this.uuid;
576
732
  enabled;
577
733
  once;
578
734
  priority;
@@ -737,6 +893,7 @@ var EventBuilder = class _EventBuilder {
737
893
  return;
738
894
  }
739
895
  if (this.isRateLimited()) {
896
+ logger.warn(`Event '${this.name}' (${this.event}) is rate limited`);
740
897
  if (this.rateLimit?.onRateLimit) {
741
898
  return await this.rateLimit.onRateLimit(...args);
742
899
  }
@@ -757,7 +914,7 @@ var EventBuilder = class _EventBuilder {
757
914
  if (this.onError) {
758
915
  return await this.onError(err, ...args);
759
916
  }
760
- console.error(`Error in event '${this.name}':`, err);
917
+ logger.error(`Event execution error '${this.name}' (${this.event}):`, err);
761
918
  throw err;
762
919
  }
763
920
  }
@@ -923,27 +1080,121 @@ var SlashCommandBuilder = class extends BaseCommandBuilder {
923
1080
  }
924
1081
  };
925
1082
 
926
- // src/client.ts
927
- var import_discord11 = require("discord.js");
928
- var import_dotenv = __toESM(require("dotenv"));
1083
+ // src/modules/builtins/context-command.builtins.ts
1084
+ var contextCommandHandler = new EventBuilder({
1085
+ event: "interactionCreate",
1086
+ name: "ContextCommandHandler",
1087
+ async execute(client, interaction) {
1088
+ if (!interaction.isContextMenuCommand()) return;
1089
+ const command = client.commands.context.get(interaction.commandName);
1090
+ if (!command) {
1091
+ const content = `**${interaction.commandName}** is not a registered context command.`;
1092
+ if (interaction.replied || interaction.deferred) {
1093
+ return interaction.followUp({ content, flags: "Ephemeral" });
1094
+ }
1095
+ return interaction.reply({ content, flags: "Ephemeral" });
1096
+ }
1097
+ try {
1098
+ return await command.run(client, client, interaction);
1099
+ } catch (err) {
1100
+ await client.error.handleCommandError(err, interaction.guild, interaction);
1101
+ }
1102
+ }
1103
+ });
929
1104
 
930
- // src/configs/tools.config.ts
931
- var import_lodash5 = __toESM(require("lodash"));
932
- var globalToolsConfig = {
933
- devMode: false,
934
- embedColor: [],
935
- embedColorDev: [],
936
- timeouts: {
937
- collectorTimeout: 6e4,
938
- collectorIdle: 6e4,
939
- pagination: 6e4,
940
- prompt: 3e4,
941
- modalSubmit: 6e4
942
- },
943
- collector: {
944
- notAParticipantMessage: "You are not allowed to use this.",
945
- userLockMessage: "Please wait until your current action is finished.",
946
- notAParticipantWarningCooldown: 5e3
1105
+ // src/modules/builtins/prefix-command.builtins.ts
1106
+ var import_discord4 = require("discord.js");
1107
+ var prefixCommandHandler = new EventBuilder({
1108
+ event: "messageCreate",
1109
+ name: "PrefixCommandHandler",
1110
+ async execute(client, message) {
1111
+ if (message.author.bot || !message.guild) return;
1112
+ const config = client.config.prefixCommands;
1113
+ let activePrefix = config.defaultPrefix;
1114
+ if (config.guildPrefixResolver) {
1115
+ try {
1116
+ const customPrefix = await config.guildPrefixResolver(client, message.guild.id);
1117
+ if (customPrefix) activePrefix = customPrefix;
1118
+ } catch (err) {
1119
+ client.logger.error(`Error in guildPrefixResolver for guild ${message.guild.id}:`, err);
1120
+ }
1121
+ }
1122
+ let prefixUsed;
1123
+ if (message.content.startsWith(activePrefix)) {
1124
+ prefixUsed = activePrefix;
1125
+ } else if (config.allowMentionAsPrefix) {
1126
+ const mention = (0, import_discord4.userMention)(client.user.id);
1127
+ if (message.content.startsWith(mention)) {
1128
+ prefixUsed = message.content.startsWith(`${mention} `) ? `${mention} ` : mention;
1129
+ }
1130
+ }
1131
+ if (!prefixUsed) return;
1132
+ const contentWithoutPrefix = message.content.slice(prefixUsed.length).trim();
1133
+ const args = contentWithoutPrefix.split(/\s+/);
1134
+ const trigger = args.shift();
1135
+ if (!trigger) return;
1136
+ const command = client.commands.prefix.get(trigger);
1137
+ if (!command) return;
1138
+ message.content = args.join(" ");
1139
+ try {
1140
+ return await command.run(client, client, message);
1141
+ } catch (err) {
1142
+ await client.error.handleCommandError(err, message.guild, message);
1143
+ }
1144
+ }
1145
+ });
1146
+
1147
+ // src/modules/builtins/slash-command.builtins.ts
1148
+ var slashCommandHandler = new EventBuilder({
1149
+ event: "interactionCreate",
1150
+ name: "SlashCommandHandler",
1151
+ async execute(client, interaction) {
1152
+ if (!interaction.isChatInputCommand()) return;
1153
+ const command = client.commands.slash.get(interaction.commandName);
1154
+ if (!command) {
1155
+ const content = `**/\`${interaction.commandName}\`** is not a registered command.`;
1156
+ if (interaction.replied || interaction.deferred) {
1157
+ return interaction.followUp({ content, flags: "Ephemeral" });
1158
+ }
1159
+ return interaction.reply({ content, flags: "Ephemeral" });
1160
+ }
1161
+ try {
1162
+ return await command.run(client, client, interaction);
1163
+ } catch (err) {
1164
+ await client.error.handleCommandError(err, interaction.guild, interaction);
1165
+ }
1166
+ }
1167
+ });
1168
+
1169
+ // src/tools/BetterEmbed.ts
1170
+ var import_discord6 = require("discord.js");
1171
+
1172
+ // src/utils/config.factory.ts
1173
+ var import_lodash5 = __toESM(require("lodash"));
1174
+ function createConfigFactory(defaultConfig6, validate) {
1175
+ return (options = {}, existing) => {
1176
+ const result = import_lodash5.default.merge({}, defaultConfig6, existing, options);
1177
+ validate?.(result);
1178
+ return result;
1179
+ };
1180
+ }
1181
+
1182
+ // src/configs/tools.config.ts
1183
+ var globalToolsConfig = {
1184
+ devMode: false,
1185
+ embedColor: [],
1186
+ embedColorDev: [],
1187
+ timeouts: {
1188
+ collectorTimeout: 6e4,
1189
+ collectorIdle: 6e4,
1190
+ pagination: 6e4,
1191
+ prompt: 3e4,
1192
+ modalSubmit: 6e4
1193
+ },
1194
+ collector: {
1195
+ notAParticipantMessage: "You are not allowed to use this.",
1196
+ userLockMessage: "Please wait until your current action is finished.",
1197
+ notAParticipantWarningCooldown: 5e3
947
1198
  },
948
1199
  paginator: {
949
1200
  notAParticipantMessage: "You are not allowed to use this.",
@@ -964,95 +1215,13 @@ var globalToolsConfig = {
964
1215
  rejectLabel: "Cancel"
965
1216
  }
966
1217
  };
967
- function defineGlobalToolsConfig(options) {
968
- Object.assign(globalToolsConfig, import_lodash5.default.merge(globalToolsConfig, options));
969
- }
970
- function createToolsConfig(options) {
971
- return import_lodash5.default.merge(globalToolsConfig, options);
972
- }
973
-
974
- // src/configs/app.config.ts
975
- var import_lodash6 = __toESM(require("lodash"));
976
- var defaultConfig = {
977
- devMode: process.argv.includes("--dev"),
978
- name: "Discord Bot",
979
- appVersion: "1.0.0",
980
- verbose: false,
981
- disableBanner: false,
982
- moduleSuffixes: {
983
- slashCommand: "slash",
984
- contextCommand: "ctx",
985
- prefixCommand: "prefix",
986
- event: "event"
987
- }
988
- };
989
- function createAppConfig(options = {}) {
990
- return import_lodash6.default.merge(defaultConfig, options);
991
- }
992
-
993
- // src/configs/staff.config.ts
994
- var import_lodash7 = __toESM(require("lodash"));
995
- var defaultConfig2 = {
996
- ownerId: null,
997
- superUsers: [],
998
- superUserRoles: [],
999
- bypassers: [],
1000
- bypassesGuildAdmin: {
1001
- allBotStaff: false,
1002
- botOwner: false,
1003
- superUsers: false,
1004
- bypassers: false
1005
- },
1006
- guild: {
1007
- id: null,
1008
- inviteUrl: null,
1009
- channels: {}
1010
- }
1011
- };
1012
- function createStaffConfig(options = {}) {
1013
- return import_lodash7.default.merge(defaultConfig2, options);
1014
- }
1015
-
1016
- // src/configs/slashCommand.config.ts
1017
- var import_lodash8 = __toESM(require("lodash"));
1018
- var defaultConfig3 = {
1019
- logExecution: true
1020
- };
1021
- function createSlashCommandConfig(options = {}) {
1022
- return import_lodash8.default.merge(defaultConfig3, options);
1023
- }
1024
-
1025
- // src/configs/prefixCommand.config.ts
1026
- var import_lodash9 = __toESM(require("lodash"));
1027
- var defaultConfig4 = {
1028
- enabled: true,
1029
- defaultPrefix: "!",
1030
- allowMentionAsPrefix: true,
1031
- allowCaseInsensitiveCommandNames: true,
1032
- logExecution: true
1033
- };
1034
- function createPrefixCommandConfig(options = {}) {
1035
- return import_lodash9.default.merge(defaultConfig4, options);
1036
- }
1037
-
1038
- // src/configs/contextCommand.config.ts
1039
- var import_lodash10 = __toESM(require("lodash"));
1040
- var defaultConfig5 = {
1041
- enabled: true,
1042
- logExecution: true
1218
+ var createToolsConfig = createConfigFactory(globalToolsConfig);
1219
+ var defineGlobalToolsConfig = (options) => {
1220
+ Object.assign(globalToolsConfig, createToolsConfig(options, globalToolsConfig));
1043
1221
  };
1044
- function createContextCommandConfig(options = {}) {
1045
- return import_lodash10.default.merge(defaultConfig5, options);
1046
- }
1047
-
1048
- // src/utils/sendCommandErrorEmbed.ts
1049
- var import_discord6 = require("discord.js");
1050
-
1051
- // src/tools/BetterEmbed.ts
1052
- var import_discord5 = require("discord.js");
1053
1222
 
1054
1223
  // src/tools/dynaSend.ts
1055
- var import_discord4 = require("discord.js");
1224
+ var import_discord5 = require("discord.js");
1056
1225
 
1057
1226
  // src/tools/types.ts
1058
1227
  var SendMethod = /* @__PURE__ */ ((SendMethod2) => {
@@ -1072,7 +1241,7 @@ var DynaSend = class {
1072
1241
  return Array.isArray(value) ? value : [value];
1073
1242
  }
1074
1243
  static isInteractionCallback(obj) {
1075
- return obj instanceof import_discord4.InteractionCallbackResponse;
1244
+ return obj instanceof import_discord5.InteractionCallbackResponse;
1076
1245
  }
1077
1246
  static filterFlags(flags, excludeFlags) {
1078
1247
  if (!flags) return void 0;
@@ -1080,26 +1249,26 @@ var DynaSend = class {
1080
1249
  return flagArray.filter((flag) => !excludeFlags.includes(flag));
1081
1250
  }
1082
1251
  static detectSendMethod(handler) {
1083
- if (handler instanceof import_discord4.BaseInteraction) {
1252
+ if (handler instanceof import_discord5.BaseInteraction) {
1084
1253
  return handler.replied || handler.deferred ? 1 /* EditReply */ : 0 /* Reply */;
1085
1254
  }
1086
- if (handler instanceof import_discord4.BaseChannel) return 3 /* Channel */;
1087
- if (handler instanceof import_discord4.Message) return 4 /* MessageReply */;
1088
- if (handler instanceof import_discord4.GuildMember || handler instanceof import_discord4.User) return 6 /* User */;
1255
+ if (handler instanceof import_discord5.BaseChannel) return 3 /* Channel */;
1256
+ if (handler instanceof import_discord5.Message) return 4 /* MessageReply */;
1257
+ if (handler instanceof import_discord5.GuildMember || handler instanceof import_discord5.User) return 6 /* User */;
1089
1258
  throw new Error("[DynaSend] Unable to determine send method for handler type");
1090
1259
  }
1091
1260
  static validateSendMethod(handler, method) {
1092
1261
  const interactionMethods = [0 /* Reply */, 1 /* EditReply */, 2 /* FollowUp */];
1093
- if (interactionMethods.includes(method) && !(handler instanceof import_discord4.BaseInteraction)) {
1262
+ if (interactionMethods.includes(method) && !(handler instanceof import_discord5.BaseInteraction)) {
1094
1263
  throw new TypeError(`[DynaSend] SendMethod '${SendMethod[method]}' requires BaseInteraction handler`);
1095
1264
  }
1096
- if (method === 3 /* Channel */ && !(handler instanceof import_discord4.BaseChannel)) {
1265
+ if (method === 3 /* Channel */ && !(handler instanceof import_discord5.BaseChannel)) {
1097
1266
  throw new TypeError(`[DynaSend] SendMethod '${SendMethod[method]}' requires BaseChannel handler`);
1098
1267
  }
1099
- if ([4 /* MessageReply */, 5 /* MessageEdit */].includes(method) && !(handler instanceof import_discord4.Message)) {
1268
+ if ([4 /* MessageReply */, 5 /* MessageEdit */].includes(method) && !(handler instanceof import_discord5.Message)) {
1100
1269
  throw new TypeError(`[DynaSend] SendMethod '${SendMethod[method]}' requires Message handler`);
1101
1270
  }
1102
- if (method === 6 /* User */ && !(handler instanceof import_discord4.GuildMember || handler instanceof import_discord4.User)) {
1271
+ if (method === 6 /* User */ && !(handler instanceof import_discord5.GuildMember || handler instanceof import_discord5.User)) {
1103
1272
  throw new TypeError(`[DynaSend] SendMethod '${SendMethod[method]}' requires User or GuildMember handler`);
1104
1273
  }
1105
1274
  }
@@ -1230,7 +1399,7 @@ async function dynaSend(handler, options) {
1230
1399
 
1231
1400
  // src/tools/BetterEmbed.ts
1232
1401
  var BetterEmbed = class _BetterEmbed {
1233
- embed = new import_discord5.EmbedBuilder();
1402
+ embed = new import_discord6.EmbedBuilder();
1234
1403
  data;
1235
1404
  config;
1236
1405
  /** A powerful wrapper for `EmbedBuilder` that introduces useful features
@@ -1307,8 +1476,8 @@ var BetterEmbed = class _BetterEmbed {
1307
1476
  applyContextFormatting(str) {
1308
1477
  if (!this.data.acf) return;
1309
1478
  const user = this.getContextUser();
1310
- const guildMember = user instanceof import_discord5.GuildMember ? user : null;
1311
- const actualUser = guildMember?.user || (user instanceof import_discord5.User ? user : null);
1479
+ const guildMember = user instanceof import_discord6.GuildMember ? user : null;
1480
+ const actualUser = guildMember?.user || (user instanceof import_discord6.User ? user : null);
1312
1481
  const client = this.getContextClient();
1313
1482
  const formatString = (str2) => {
1314
1483
  if (!str2 || !str2.includes("$")) return str2;
@@ -1498,24 +1667,25 @@ var BetterEmbed = class _BetterEmbed {
1498
1667
  }
1499
1668
  };
1500
1669
 
1501
- // src/utils/sendCommandErrorEmbed.ts
1670
+ // src/utils/command-error.utils.ts
1671
+ var import_discord7 = require("discord.js");
1502
1672
  async function sendCommandErrorEmbed(client, error, guild, messageOrInteraction) {
1503
1673
  if (!client.features.enableCommandErrorMessage) return null;
1504
1674
  const config = typeof client.features.enableCommandErrorMessage !== "boolean" ? client.features.enableCommandErrorMessage : void 0;
1505
1675
  const buttons = {
1506
- supportServer: new import_discord6.ButtonBuilder({
1676
+ supportServer: new import_discord7.ButtonBuilder({
1507
1677
  url: config?.inviteUrl || client.config.staff.guild.inviteUrl || "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
1508
1678
  // may or may not be a rickroll
1509
1679
  label: config?.inviteButtonLabel || "Support Support",
1510
- style: import_discord6.ButtonStyle.Link
1680
+ style: import_discord7.ButtonStyle.Link
1511
1681
  }),
1512
- details: new import_discord6.ButtonBuilder({
1682
+ details: new import_discord7.ButtonBuilder({
1513
1683
  customId: "btn_details",
1514
1684
  label: config?.detailButtonLabel || "Details",
1515
- style: import_discord6.ButtonStyle.Secondary
1685
+ style: import_discord7.ButtonStyle.Secondary
1516
1686
  })
1517
1687
  };
1518
- const actionRow = new import_discord6.ActionRowBuilder({
1688
+ const actionRow = new import_discord7.ActionRowBuilder({
1519
1689
  components: config?.inviteUrl && guild?.id !== (config.inviteUrl || client.config.staff.guild.id) ? [buttons.supportServer, buttons.details] : [buttons.details]
1520
1690
  });
1521
1691
  const embed_error = config?.embed?.(new BetterEmbed(), error, guild) || new BetterEmbed({
@@ -1523,19 +1693,20 @@ async function sendCommandErrorEmbed(client, error, guild, messageOrInteraction)
1523
1693
  title: "Something went wrong",
1524
1694
  description: "If you keep encountering this error, please report it."
1525
1695
  });
1526
- const msg = await embed_error.send(messageOrInteraction, {
1696
+ const msg = await dynaSend(messageOrInteraction, {
1697
+ embeds: [embed_error],
1527
1698
  components: [actionRow],
1528
1699
  flags: config?.ephemeral ? "Ephemeral" : void 0,
1529
1700
  deleteAfter: config?.deleteAfter
1530
1701
  });
1531
1702
  if (!msg) return null;
1532
1703
  const collector = msg.createMessageComponentCollector({
1533
- componentType: import_discord6.ComponentType.Button,
1704
+ componentType: import_discord7.ComponentType.Button,
1534
1705
  idle: config?.detailButtonIdleTimeout ?? 3e4,
1535
1706
  filter: (i) => i.customId === "btn_details"
1536
1707
  });
1537
1708
  collector.on("collect", (i) => {
1538
- const attachment = new import_discord6.AttachmentBuilder(Buffer.from(`${error.message}
1709
+ const attachment = new import_discord7.AttachmentBuilder(Buffer.from(`${error.message}
1539
1710
 
1540
1711
  ${error.stack}`), {
1541
1712
  name: "error.txt"
@@ -1544,537 +1715,335 @@ ${error.stack}`), {
1544
1715
  });
1545
1716
  collector.on("end", () => {
1546
1717
  buttons.details.setDisabled(true);
1547
- embed_error.send(messageOrInteraction, {
1548
- sendMethod: messageOrInteraction instanceof import_discord6.Message ? 5 /* MessageEdit */ : void 0,
1718
+ dynaSend(messageOrInteraction, {
1719
+ embeds: [embed_error],
1720
+ sendMethod: messageOrInteraction instanceof import_discord7.Message ? 5 /* MessageEdit */ : void 0,
1549
1721
  components: [actionRow]
1550
1722
  });
1551
1723
  });
1552
1724
  return msg;
1553
1725
  }
1554
1726
 
1555
- // src/modules/builtins/builtin.slashCommandHandler.ts
1556
- var BUILTIN_SlashCommandHandler = new EventBuilder({
1557
- event: "interactionCreate",
1558
- name: "SlashCommandHandler",
1559
- async execute(client, interaction) {
1560
- if (!interaction.isChatInputCommand()) return;
1561
- const command = client.commands.slash.get(interaction.commandName);
1562
- if (!command) {
1563
- const content = `**/\`${interaction.commandName}\`** is not a registered command.`;
1564
- if (interaction.replied || interaction.deferred) {
1565
- return interaction.followUp({ content, flags: "Ephemeral" });
1566
- }
1567
- return interaction.reply({ content, flags: "Ephemeral" });
1568
- }
1569
- try {
1570
- return await command.run(client, client, interaction);
1571
- } catch (err) {
1572
- await sendCommandErrorEmbed(client, err, interaction.guild, interaction);
1573
- throw err;
1574
- }
1727
+ // src/client/error-handler.ts
1728
+ var ErrorHandler = class {
1729
+ client;
1730
+ constructor(client) {
1731
+ this.client = client;
1575
1732
  }
1576
- });
1577
-
1578
- // src/modules/builtins/builtin.prefixCommandHandler.ts
1579
- var import_discord7 = require("discord.js");
1580
- var BUILTIN_PrefixCommandHandler = new EventBuilder({
1581
- event: "messageCreate",
1582
- name: "PrefixCommandHandler",
1583
- async execute(client, message) {
1584
- if (message.author.bot || !message.guild) return;
1585
- const config = client.config.prefixCommands;
1586
- let activePrefix = config.defaultPrefix;
1587
- if (config.guildPrefixResolver) {
1588
- try {
1589
- const customPrefix = await config.guildPrefixResolver(client, message.guild.id);
1590
- if (customPrefix) activePrefix = customPrefix;
1591
- } catch (err) {
1592
- client.logger.error(`Error in guildPrefixResolver for guild ${message.guild.id}:`, err);
1593
- }
1594
- }
1595
- let prefixUsed;
1596
- if (message.content.startsWith(activePrefix)) {
1597
- prefixUsed = activePrefix;
1598
- } else if (config.allowMentionAsPrefix) {
1599
- const mention = (0, import_discord7.userMention)(client.user.id);
1600
- if (message.content.startsWith(mention)) {
1601
- prefixUsed = message.content.startsWith(`${mention} `) ? `${mention} ` : mention;
1602
- }
1603
- }
1604
- if (!prefixUsed) return;
1605
- const contentWithoutPrefix = message.content.slice(prefixUsed.length).trim();
1606
- const args = contentWithoutPrefix.split(/\s+/);
1607
- const trigger = args.shift();
1608
- if (!trigger) return;
1609
- const command = client.commands.prefix.get(trigger);
1610
- if (!command) return;
1611
- message.content = args.join(" ");
1612
- try {
1613
- return await command.run(client, client, message);
1614
- } catch (err) {
1615
- await sendCommandErrorEmbed(client, err, message.guild, message);
1616
- throw err;
1617
- }
1733
+ /** Handles command errors - sends error embed to user, then rethrows. */
1734
+ async handleCommandError(error, guild, messageOrInteraction) {
1735
+ await sendCommandErrorEmbed(this.client, error, guild, messageOrInteraction);
1736
+ throw error;
1618
1737
  }
1619
- });
1738
+ /** Handles internal Vimcord errors - logs with [Vimcord] prefix. */
1739
+ handleVimcordError(error, context) {
1740
+ this.client.logger.error(`[Vimcord] [${context}]`, error);
1741
+ }
1742
+ /** Sets up global process error handlers. */
1743
+ setupGlobalHandlers() {
1744
+ process.on("uncaughtException", (err) => this.handleVimcordError(err, "Uncaught Exception"));
1745
+ process.on("unhandledRejection", (err) => this.handleVimcordError(err, "Unhandled Rejection"));
1746
+ process.on("exit", (code) => this.client.logger.debug(`Process exited with code ${code}`));
1747
+ this.client.on("error", (err) => this.handleVimcordError(err, "Client Error"));
1748
+ this.client.on("shardError", (err) => this.handleVimcordError(err, "Client Shard Error"));
1749
+ }
1750
+ };
1620
1751
 
1621
- // src/modules/builtins/builtin.contextCommandHandler.ts
1622
- var BUILTIN_ContextCommandHandler = new EventBuilder({
1623
- event: "interactionCreate",
1624
- name: "ContextCommandHandler",
1625
- async execute(client, interaction) {
1626
- if (!interaction.isContextMenuCommand()) return;
1627
- const command = client.commands.context.get(interaction.commandName);
1628
- if (!command) {
1629
- const content = `**${interaction.commandName}** is not a registered context command.`;
1630
- if (interaction.replied || interaction.deferred) {
1631
- return interaction.followUp({ content, flags: "Ephemeral" });
1632
- }
1633
- return interaction.reply({ content, flags: "Ephemeral" });
1634
- }
1635
- try {
1636
- return await command.run(client, client, interaction);
1637
- } catch (err) {
1638
- await sendCommandErrorEmbed(client, err, interaction.guild, interaction);
1639
- throw err;
1640
- }
1752
+ // src/client/vimcord.logger.ts
1753
+ var import_chalk2 = __toESM(require("chalk"));
1754
+
1755
+ // package.json
1756
+ var version = "1.0.37";
1757
+
1758
+ // src/client/vimcord.logger.ts
1759
+ var clientLoggerFactory = (client) => new Logger({ prefixEmoji: "\u26A1", prefix: `vimcord (i${client.clientId})` }).extend({
1760
+ clientBanner(client2) {
1761
+ if (client2.config.app.disableBanner) return;
1762
+ const border = "\u2550".repeat(50);
1763
+ console.log(import_chalk2.default.hex(this.colors.primary)(`
1764
+ \u2554${border}\u2557`));
1765
+ console.log(
1766
+ import_chalk2.default.hex(this.colors.primary)("\u2551") + import_chalk2.default.bold.hex(this.colors.text)(
1767
+ ` \u{1F680} ${client2.config.app.name} v${client2.$version}`.padEnd(50 - (client2.$devMode ? 12 : 0))
1768
+ ) + import_chalk2.default.hex(this.colors.primary)(`${client2.$devMode ? import_chalk2.default.hex(this.colors.warn)("devMode \u26A0\uFE0F ") : ""}\u2551`)
1769
+ );
1770
+ console.log(import_chalk2.default.hex(this.colors.primary)(`\u2551${"".padEnd(50)}\u2551`));
1771
+ console.log(
1772
+ import_chalk2.default.hex(this.colors.primary)("\u2551") + import_chalk2.default.hex(this.colors.muted)(
1773
+ ` # Powered by Vimcord v${version}`.padEnd(50 - 3 - `${client2.clientId}`.length)
1774
+ ) + import_chalk2.default.hex(this.colors.primary)(`${import_chalk2.default.hex(this.colors.muted)(`i${client2.clientId}`)} \u2551`)
1775
+ );
1776
+ console.log(import_chalk2.default.hex(this.colors.primary)(`\u255A${border}\u255D
1777
+ `));
1778
+ },
1779
+ clientReady(clientTag, guildCount) {
1780
+ console.log(
1781
+ this.formatTimestamp(),
1782
+ this.formatPrefix(),
1783
+ import_chalk2.default.hex(this.colors.success)("\u{1F916} READY"),
1784
+ import_chalk2.default.white(`Connected as ${import_chalk2.default.bold.hex(this.colors.primary)(clientTag)}`),
1785
+ import_chalk2.default.hex(this.colors.muted)(`\u2022 ${guildCount} guilds`)
1786
+ );
1787
+ },
1788
+ moduleLoaded(moduleName, count, ignoredCount) {
1789
+ const countText = count ? import_chalk2.default.hex(this.colors.muted)(`(${count} items)`) : "";
1790
+ console.log(
1791
+ this.formatTimestamp(),
1792
+ this.formatPrefix(),
1793
+ import_chalk2.default.hex("#9B59B6")("\u{1F4E6} MODULE"),
1794
+ import_chalk2.default.hex(this.colors.warn)(`${moduleName} loaded`),
1795
+ ignoredCount ? import_chalk2.default.hex(this.colors.muted)(`(${ignoredCount} ignored)`) : "",
1796
+ countText
1797
+ );
1798
+ },
1799
+ commandExecuted(commandName, username, guildName) {
1800
+ const location = guildName ? `in ${import_chalk2.default.hex(this.colors.muted)(guildName)}` : "in DMs";
1801
+ console.log(
1802
+ this.formatTimestamp(),
1803
+ this.formatPrefix(),
1804
+ import_chalk2.default.hex("#87CEEB")("\u{1F4DD} COMMAND"),
1805
+ import_chalk2.default.hex(this.colors.warn)(`/${commandName}`),
1806
+ import_chalk2.default.white(`used by ${import_chalk2.default.bold(username)}`),
1807
+ import_chalk2.default.hex(this.colors.muted)(location)
1808
+ );
1809
+ },
1810
+ database(action, details) {
1811
+ console.log(
1812
+ this.formatTimestamp(),
1813
+ this.formatPrefix(),
1814
+ import_chalk2.default.hex("#FF6B9D")("\u{1F5C4}\uFE0F DATABASE"),
1815
+ import_chalk2.default.white(action),
1816
+ details ? import_chalk2.default.hex(this.colors.muted)(details) : ""
1817
+ );
1641
1818
  }
1642
1819
  });
1643
1820
 
1644
- // src/tools/Logger.ts
1645
- var import_chalk = __toESM(require("chalk"));
1646
- var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
1647
- LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
1648
- LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
1649
- LogLevel2[LogLevel2["SUCCESS"] = 2] = "SUCCESS";
1650
- LogLevel2[LogLevel2["WARN"] = 3] = "WARN";
1651
- LogLevel2[LogLevel2["ERROR"] = 4] = "ERROR";
1652
- return LogLevel2;
1653
- })(LogLevel || {});
1654
- var LOGGER_COLORS = {
1655
- primary: "#5865F2",
1656
- success: "#57F287",
1657
- warn: "#FEE75C",
1658
- danger: "#ED4245",
1659
- muted: "#747F8D",
1660
- text: "#FFFFFF"
1821
+ // src/utils/process.utils.ts
1822
+ var import_node_fs = require("fs");
1823
+ var import_node_path = require("path");
1824
+ function getPackageJson() {
1825
+ return JSON.parse((0, import_node_fs.readFileSync)((0, import_node_path.join)(process.cwd(), "package.json"), "utf-8"));
1826
+ }
1827
+ function getDevMode() {
1828
+ return process.argv.includes("--dev");
1829
+ }
1830
+
1831
+ // src/configs/app.config.ts
1832
+ var defaultConfig = {
1833
+ name: "Discord Bot",
1834
+ version: getPackageJson()?.version ?? "1.0.0",
1835
+ devMode: getDevMode(),
1836
+ verbose: false,
1837
+ enableCLI: false,
1838
+ disableBanner: false
1661
1839
  };
1662
- var Logger = class {
1663
- logPrefixEmoji;
1664
- logPrefix;
1665
- minLevel;
1666
- showTimestamp;
1667
- colorScheme;
1668
- constructor(options) {
1669
- const { prefixEmoji = null, prefix = null, minLevel = 0 /* DEBUG */, showTimestamp = true } = options || {};
1670
- this.logPrefixEmoji = prefixEmoji;
1671
- this.logPrefix = prefix;
1672
- this.minLevel = minLevel;
1673
- this.showTimestamp = showTimestamp;
1674
- this.colorScheme = {
1675
- ...LOGGER_COLORS,
1676
- ...options?.colors
1677
- };
1678
- }
1679
- formatTimestamp() {
1680
- if (!this.showTimestamp) return "";
1681
- const now = /* @__PURE__ */ new Date();
1682
- const time = now.toLocaleTimeString("en-US", {
1683
- hour12: false,
1684
- hour: "2-digit",
1685
- minute: "2-digit",
1686
- second: "2-digit"
1687
- });
1688
- return import_chalk.default.hex(this.colorScheme.muted)(`[${time}]`);
1689
- }
1690
- formatPrefix() {
1691
- if (!this.logPrefix) return "";
1692
- return import_chalk.default.bold.hex(this.colorScheme.primary)(
1693
- `${this.logPrefixEmoji ? `${this.logPrefixEmoji} ` : ""}${this.logPrefix}`
1694
- );
1695
- }
1696
- shouldLog(level) {
1697
- return level >= this.minLevel;
1698
- }
1699
- get prefixEmoji() {
1700
- return this.logPrefixEmoji;
1701
- }
1702
- get prefix() {
1703
- return this.logPrefix;
1704
- }
1705
- get colors() {
1706
- return this.colorScheme;
1707
- }
1708
- extend(extras) {
1709
- for (const [key, fn] of Object.entries(extras)) {
1710
- if (typeof fn === "function") {
1711
- this[key] = function(...args) {
1712
- return fn.call(this, ...args);
1713
- };
1714
- }
1715
- }
1716
- return this;
1717
- }
1718
- setPrefix(prefix) {
1719
- this.logPrefix = prefix;
1720
- return this;
1721
- }
1722
- setPrefixEmoji(prefixEmoji) {
1723
- this.logPrefixEmoji = prefixEmoji;
1724
- return this;
1725
- }
1726
- setMinLevel(minLevel) {
1727
- this.minLevel = minLevel;
1728
- return this;
1729
- }
1730
- setShowTimestamp(show) {
1731
- this.showTimestamp = show;
1732
- return this;
1733
- }
1734
- setColors(colors) {
1735
- this.colorScheme = {
1736
- ...LOGGER_COLORS,
1737
- ...colors
1738
- };
1739
- return this;
1740
- }
1741
- log(message, ...args) {
1742
- console.log(this.formatTimestamp(), this.formatPrefix(), message, ...args);
1743
- }
1744
- debug(message, ...args) {
1745
- if (!this.shouldLog(0 /* DEBUG */)) return;
1746
- console.log(
1747
- this.formatTimestamp(),
1748
- this.formatPrefix(),
1749
- import_chalk.default.hex(this.colorScheme.muted)("DEBUG"),
1750
- import_chalk.default.dim(message),
1751
- ...args
1752
- );
1753
- }
1754
- info(message, ...args) {
1755
- if (!this.shouldLog(1 /* INFO */)) return;
1756
- console.log(this.formatTimestamp(), this.formatPrefix(), import_chalk.default.hex("#87CEEB")("INFO"), message, ...args);
1757
- }
1758
- success(message, ...args) {
1759
- if (!this.shouldLog(2 /* SUCCESS */)) return;
1760
- console.log(
1761
- this.formatTimestamp(),
1762
- this.formatPrefix(),
1763
- import_chalk.default.bold.hex(this.colorScheme.success)("\u2713 SUCCESS"),
1764
- import_chalk.default.hex(this.colorScheme.success)(message),
1765
- ...args
1766
- );
1767
- }
1768
- warn(message, ...args) {
1769
- if (!this.shouldLog(3 /* WARN */)) return;
1770
- console.warn(
1771
- this.formatTimestamp(),
1772
- this.formatPrefix(),
1773
- import_chalk.default.bold.hex(this.colorScheme.warn)("\u26A0 WARN"),
1774
- import_chalk.default.hex(this.colorScheme.warn)(message),
1775
- ...args
1776
- );
1777
- }
1778
- error(message, error, ...args) {
1779
- if (!this.shouldLog(4 /* ERROR */)) return;
1780
- console.error(
1781
- this.formatTimestamp(),
1782
- this.formatPrefix(),
1783
- import_chalk.default.bold.hex(this.colorScheme.danger)("\u2715 ERROR"),
1784
- import_chalk.default.hex(this.colorScheme.danger)(message),
1785
- ...args
1786
- );
1787
- if (error && error.stack) {
1788
- console.error(import_chalk.default.dim(error.stack));
1789
- }
1790
- }
1791
- loader(message) {
1792
- const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
1793
- let i = 0;
1794
- const interval = setInterval(() => {
1795
- process.stdout.write(
1796
- `\r${this.formatTimestamp()} ${this.formatPrefix()} ${import_chalk.default.hex(this.colorScheme.warn)(frames[i])} ${message}`
1797
- );
1798
- i = (i + 1) % frames.length;
1799
- }, 100);
1800
- return (newMessage) => {
1801
- clearInterval(interval);
1802
- process.stdout.write(
1803
- `\r${this.formatTimestamp()} ${this.formatPrefix()} ${import_chalk.default.hex(this.colorScheme.success)("\u2713")} ${newMessage || message}
1804
- `
1805
- );
1806
- };
1807
- }
1808
- table(title, data) {
1809
- console.log(this.formatTimestamp(), this.formatPrefix(), import_chalk.default.bold(title));
1810
- Object.entries(data).forEach(([key, value]) => {
1811
- const formattedKey = import_chalk.default.hex(this.colorScheme.warn)(` ${key}`);
1812
- const formattedValue = import_chalk.default.hex(this.colorScheme.muted)(value);
1813
- console.log(`${formattedKey.padEnd(25)} ${formattedValue}`);
1814
- });
1815
- }
1816
- section(title) {
1817
- const line = "\u2500".repeat(Math.max(30, title.length + 4));
1818
- console.log(import_chalk.default.hex(this.colorScheme.muted)(`
1819
- \u250C\u2500${line}\u2500\u2510`));
1820
- console.log(
1821
- import_chalk.default.hex(this.colorScheme.muted)("\u2502 ") + import_chalk.default.bold.hex(this.colorScheme.text)(title.padEnd(line.length)) + import_chalk.default.hex(this.colorScheme.muted)(" \u2502")
1822
- );
1823
- console.log(import_chalk.default.hex(this.colorScheme.muted)(`\u2514\u2500${line}\u2500\u2518`));
1824
- }
1840
+ var createAppConfig = createConfigFactory(defaultConfig, (config) => {
1841
+ if (!config.name) throw new Error("App name is required");
1842
+ defineGlobalToolsConfig({ devMode: config.devMode });
1843
+ });
1844
+
1845
+ // src/configs/contextCommand.config.ts
1846
+ var defaultConfig2 = {
1847
+ enabled: true,
1848
+ logExecution: true
1825
1849
  };
1826
- var logger = new Logger();
1850
+ var createContextCommandConfig = createConfigFactory(defaultConfig2);
1827
1851
 
1828
- // src/tools/utils.ts
1829
- function __zero(str) {
1830
- return str?.length ? str : "0";
1831
- }
1832
- function isMentionOrSnowflake(str) {
1833
- return str ? str.match(/<@[#&]?[\d]{6,}>/) || str.match(/\d{6,}/) ? true : false : false;
1834
- }
1835
- function cleanMention(str) {
1836
- return str ? str.replaceAll(/[<@#&>]/g, "").trim() : void 0;
1837
- }
1838
- async function getMessageMention(message, content, type, index = 0, idOnly) {
1839
- const args = content?.split(" ");
1840
- const arg = isMentionOrSnowflake(args?.[index]) ? cleanMention(args?.[index]) : void 0;
1841
- switch (type) {
1842
- case "user":
1843
- const userMention2 = message.mentions.users.at(index) || null;
1844
- if (!userMention2 && arg) {
1845
- return idOnly ? arg : await fetchUser(message.client, arg);
1846
- } else {
1847
- return idOnly ? userMention2?.id || null : userMention2;
1848
- }
1849
- case "member":
1850
- if (!message.guild) return null;
1851
- const member = await fetchMember(message.guild, message.mentions.users.at(index)?.id ?? arg);
1852
- return idOnly ? member?.id || null : member;
1853
- case "channel":
1854
- const channelMention = message.mentions.channels.at(index) || null;
1855
- if (!channelMention && arg) {
1856
- return idOnly ? arg : message.guild ? await fetchChannel(message.guild, arg) : message.client.channels.cache.get(__zero(arg)) ?? message.client.channels.fetch(__zero(arg));
1857
- } else {
1858
- return idOnly ? channelMention?.id || null : channelMention;
1859
- }
1860
- case "role":
1861
- const roleMention = message.mentions.roles.at(index) || null;
1862
- if (!roleMention && arg) {
1863
- return idOnly ? arg : message.guild ? await fetchRole(message.guild, arg) : null;
1864
- } else {
1865
- return idOnly ? roleMention?.id || null : roleMention;
1866
- }
1867
- default:
1868
- return null;
1852
+ // src/configs/prefixCommand.config.ts
1853
+ var defaultConfig3 = {
1854
+ enabled: true,
1855
+ defaultPrefix: "!",
1856
+ allowMentionAsPrefix: true,
1857
+ allowCaseInsensitiveCommandNames: true,
1858
+ logExecution: true
1859
+ };
1860
+ var createPrefixCommandConfig = createConfigFactory(defaultConfig3);
1861
+
1862
+ // src/configs/slashCommand.config.ts
1863
+ var defaultConfig4 = {
1864
+ logExecution: true
1865
+ };
1866
+ var createSlashCommandConfig = createConfigFactory(defaultConfig4);
1867
+
1868
+ // src/configs/staff.config.ts
1869
+ var defaultConfig5 = {
1870
+ ownerId: null,
1871
+ superUsers: [],
1872
+ superUserRoles: [],
1873
+ bypassers: [],
1874
+ bypassesGuildAdmin: {
1875
+ allBotStaff: false,
1876
+ botOwner: false,
1877
+ superUsers: false,
1878
+ bypassers: false
1879
+ },
1880
+ guild: {
1881
+ id: null,
1882
+ inviteUrl: null,
1883
+ channels: {}
1869
1884
  }
1870
- }
1871
- function getFirstMentionId(options) {
1872
- let mentionId = "";
1873
- if (options.message) {
1874
- switch (options.type) {
1875
- case "user":
1876
- mentionId = options.message.mentions.users.first()?.id || "";
1877
- break;
1878
- case "channel":
1879
- mentionId = options.message.mentions.channels.first()?.id || "";
1880
- break;
1881
- case "role":
1882
- mentionId = options.message.mentions.roles.first()?.id || "";
1883
- break;
1884
- }
1885
+ };
1886
+ var createStaffConfig = createConfigFactory(defaultConfig5);
1887
+
1888
+ // src/client/vimcord.utils.ts
1889
+ var DEFAULT_MODULE_SUFFIXES = {
1890
+ slashCommands: ".slash",
1891
+ contextCommands: ".ctx",
1892
+ prefixCommands: ".prefix",
1893
+ events: ".event"
1894
+ };
1895
+ var configSetters = {
1896
+ app: createAppConfig,
1897
+ staff: createStaffConfig,
1898
+ slashCommands: createSlashCommandConfig,
1899
+ prefixCommands: createPrefixCommandConfig,
1900
+ contextCommands: createContextCommandConfig
1901
+ };
1902
+ var moduleImporters = {
1903
+ slashCommands: (client, options, set) => {
1904
+ const opt = options;
1905
+ const dir = Array.isArray(options) ? options : opt?.dir ?? [];
1906
+ const suffix = Array.isArray(options) ? DEFAULT_MODULE_SUFFIXES.slashCommands : opt?.suffix ?? DEFAULT_MODULE_SUFFIXES.slashCommands;
1907
+ return client.commands.slash.importFrom(dir, set, suffix);
1908
+ },
1909
+ contextCommands: (client, options, set) => {
1910
+ const opt = options;
1911
+ const dir = Array.isArray(options) ? options : opt?.dir ?? [];
1912
+ const suffix = Array.isArray(options) ? DEFAULT_MODULE_SUFFIXES.contextCommands : opt?.suffix ?? DEFAULT_MODULE_SUFFIXES.contextCommands;
1913
+ return client.commands.context.importFrom(dir, set, suffix);
1914
+ },
1915
+ prefixCommands: (client, options, set) => {
1916
+ const opt = options;
1917
+ const dir = Array.isArray(options) ? options : opt?.dir ?? [];
1918
+ const suffix = Array.isArray(options) ? DEFAULT_MODULE_SUFFIXES.prefixCommands : opt?.suffix ?? DEFAULT_MODULE_SUFFIXES.prefixCommands;
1919
+ return client.commands.prefix.importFrom(dir, set, suffix);
1920
+ },
1921
+ events: (client, options, set) => {
1922
+ const opt = options;
1923
+ const dir = Array.isArray(options) ? options : opt?.dir ?? [];
1924
+ const suffix = Array.isArray(options) ? DEFAULT_MODULE_SUFFIXES.events : opt?.suffix ?? DEFAULT_MODULE_SUFFIXES.events;
1925
+ return client.events.importFrom(dir, set, suffix);
1885
1926
  }
1886
- const firstArg = options.content?.split(" ")[0] || "";
1887
- return mentionId || isMentionOrSnowflake(firstArg) ? cleanMention(firstArg) : "";
1888
- }
1889
- async function fetchUser(client, userId) {
1890
- if (!userId) return null;
1891
- return client.users.cache.get(__zero(userId)) || await client.users.fetch(__zero(userId)).catch(() => null);
1892
- }
1893
- async function fetchGuild(client, guildId) {
1894
- if (!guildId) return null;
1895
- return client.guilds.cache.get(__zero(guildId)) || await client.guilds.fetch(__zero(guildId)).catch(() => null);
1896
- }
1897
- async function fetchMember(guild, memberId) {
1898
- if (!memberId) return null;
1899
- return guild.members.cache.get(__zero(memberId)) || await guild.members.fetch(__zero(memberId)).catch(() => null);
1900
- }
1901
- async function fetchChannel(guild, channelId, type) {
1902
- if (!channelId) return null;
1903
- const channel = guild.channels.cache.get(__zero(channelId)) || await guild.channels.fetch(__zero(channelId)).catch(() => null);
1904
- if (type && channel?.type !== type) return null;
1905
- return channel;
1927
+ };
1928
+ function defineClientOptions(options) {
1929
+ return options;
1906
1930
  }
1907
- async function fetchRole(guild, roleId) {
1908
- if (!roleId) return null;
1909
- return guild.roles.cache.get(__zero(roleId)) || await guild.roles.fetch(__zero(roleId)).catch(() => null) || null;
1931
+ function defineVimcordFeatures(features) {
1932
+ return features;
1910
1933
  }
1911
- async function fetchMessage(channel, messageId) {
1912
- if (!messageId) return null;
1913
- return channel.messages.cache.get(__zero(messageId)) || await channel.messages.fetch(__zero(messageId)).catch(() => null) || null;
1934
+ function defineVimcordConfig(config) {
1935
+ return {
1936
+ app: createAppConfig(config.app),
1937
+ staff: createStaffConfig(config.staff),
1938
+ slashCommands: createSlashCommandConfig(config.slashCommands),
1939
+ prefixCommands: createPrefixCommandConfig(config.prefixCommands),
1940
+ contextCommands: createContextCommandConfig(config.contextCommands)
1941
+ };
1914
1942
  }
1943
+ var useClient = Vimcord.getInstance;
1944
+ var useReadyClient = Vimcord.getReadyInstance;
1945
+ var createClient = Vimcord.create;
1915
1946
 
1916
- // src/types/status.ts
1947
+ // src/modules/command.manager.ts
1917
1948
  var import_discord8 = require("discord.js");
1918
- var import_lodash11 = __toESM(require("lodash"));
1919
- var StatusType = /* @__PURE__ */ ((StatusType2) => {
1920
- StatusType2["DND"] = "dnd";
1921
- StatusType2["Idle"] = "idle";
1922
- StatusType2["Online"] = "online";
1923
- StatusType2["Invisible"] = "invisible";
1924
- return StatusType2;
1925
- })(StatusType || {});
1926
- var defaultPresence = {
1927
- production: {
1928
- interval: 6e4,
1929
- randomize: false,
1930
- activity: [
1931
- { status: "online" /* Online */, type: import_discord8.ActivityType.Custom, name: "Need help? Use /help or !help" },
1932
- { status: "online" /* Online */, type: import_discord8.ActivityType.Custom, name: "Join our community!" },
1933
- { status: "online" /* Online */, type: import_discord8.ActivityType.Watching, name: "\u2728 $GUILD_COUNT servers" }
1934
- ]
1935
- },
1936
- development: {
1937
- activity: { status: "dnd" /* DND */, type: import_discord8.ActivityType.Custom, name: "In development!" }
1938
- }
1939
- };
1940
- function createVimcordStatusConfig(options = {}) {
1941
- return import_lodash11.default.merge(defaultPresence, options);
1942
- }
1943
1949
 
1944
- // src/modules/status.manager.ts
1945
- var import_node_events = __toESM(require("events"));
1946
- var import_qznt2 = require("qznt");
1947
- var StatusManager = class {
1948
- client;
1949
- logger;
1950
- emitter = new import_node_events.default();
1951
- lastActivity = null;
1952
- lastActivityIndex = 0;
1953
- task = null;
1954
- constructor(client) {
1955
- this.client = client;
1956
- this.logger = new Logger({ prefixEmoji: "\u{1F4AC}", prefix: `StatusManager (i${this.client.clientId})` });
1957
- this.emitter.on("changed", (activity) => {
1958
- if (this.client.config.app.verbose) {
1959
- this.logger.debug(`Status changed to '${activity.name}'`);
1960
- }
1961
- });
1962
- this.emitter.on("cleared", () => {
1963
- if (this.client.config.app.verbose) {
1964
- this.logger.debug("Status cleared");
1965
- }
1966
- });
1967
- }
1968
- clearData() {
1969
- this.task?.stop();
1970
- this.task = null;
1971
- this.lastActivity = null;
1972
- this.lastActivityIndex = 0;
1973
- return this;
1974
- }
1975
- async getReadyClient() {
1976
- const client = await this.client.waitForReady();
1977
- if (!client.user) throw new Error("Cannot manage the client's activity when its user is not hydrated");
1978
- return client;
1979
- }
1980
- async formatActivityName(name) {
1981
- name = name.replace("$USER_COUNT", import_qznt2.$.format.number(this.client.users.cache.size)).replace("$GUILD_COUNT", import_qznt2.$.format.number(this.client.guilds.cache.size)).replace(
1982
- "$INVITE",
1983
- this.client.config.staff.guild.inviteUrl ? this.client.config.staff.guild.inviteUrl : "<STAFF_INVITE_URL_NOT_SET>"
1984
- );
1985
- if (name.includes("$STAFF_GUILD_MEMBER_COUNT")) {
1986
- await fetchGuild(this.client, this.client.config.staff.guild.id).then((guild) => {
1987
- if (!guild) return name = name.replace("$STAFF_GUILD_MEMBER_COUNT", "<STAFF_GUILD_NOT_FOUND>");
1988
- name = name.replace("$STAFF_GUILD_MEMBER_COUNT", import_qznt2.$.format.number(guild.members.cache.size));
1989
- }).catch((err) => this.logger.error("Failed to fetch the staff guild", err));
1990
- }
1991
- return name;
1992
- }
1993
- async setActivity(activity) {
1994
- const client = await this.getReadyClient();
1995
- activity.name = await this.formatActivityName(activity.name);
1996
- client.user.setStatus(activity.status);
1997
- client.user.setActivity({ name: activity.name, type: activity.type, url: activity.streamUrl });
1998
- this.emitter.emit("changed", activity);
1999
- }
2000
- async statusRotationTask(clientStatus) {
2001
- let activity;
2002
- if (clientStatus.randomize && Array.isArray(clientStatus.activity)) {
2003
- activity = import_qznt2.$.rnd.choice(clientStatus.activity, { not: this.lastActivity });
2004
- this.lastActivity = activity;
2005
- } else {
2006
- const activityIndex = (this.lastActivityIndex + 1) % clientStatus.activity.length;
2007
- this.lastActivityIndex = activityIndex;
2008
- activity = clientStatus.activity[activityIndex];
2009
- }
2010
- await this.setActivity(activity);
2011
- this.emitter.emit("rotation", activity);
2012
- }
2013
- async scheduleStatusRotation(clientStatus) {
2014
- if (!clientStatus.interval) throw new Error("Cannot create client activity interval without interval time");
2015
- this.task?.stop();
2016
- this.task = null;
2017
- this.task = new import_qznt2.$.Loop(() => this.statusRotationTask(clientStatus), import_qznt2.$.math.ms(clientStatus.interval), true);
2018
- this.start();
1950
+ // src/utils/import.utils.ts
1951
+ var import_node_path2 = __toESM(require("path"));
1952
+ var import_qznt = require("qznt");
1953
+ function getProcessDir() {
1954
+ const mainPath = process.argv[1];
1955
+ if (!mainPath) return "";
1956
+ return import_node_path2.default.dirname(mainPath);
1957
+ }
1958
+ async function importModulesFromDir(dir, suffix) {
1959
+ const cwd = getProcessDir();
1960
+ const MODULE_RELATIVE_PATH = import_node_path2.default.join(cwd, dir);
1961
+ const MODULE_LOG_PATH = dir;
1962
+ const files = import_qznt.$.fs.readDir(MODULE_RELATIVE_PATH).filter((fn) => fn.endsWith(`${suffix ? `.${suffix}` : ""}.js`) || fn.endsWith(`${suffix ? `.${suffix}` : ""}.ts`));
1963
+ if (!files.length) {
1964
+ return [];
2019
1965
  }
2020
- start() {
2021
- if (this.task) {
2022
- this.task.start();
2023
- this.emitter.emit("started", this.task);
2024
- }
2025
- return this;
1966
+ const modules = await Promise.all(
1967
+ files.map(async (fn) => {
1968
+ let _path = import_node_path2.default.join(MODULE_RELATIVE_PATH, fn);
1969
+ let _logPath = `./${import_node_path2.default.join(MODULE_LOG_PATH, fn)}`;
1970
+ let _module;
1971
+ try {
1972
+ delete require.cache[require.resolve(_path)];
1973
+ _module = require(_path);
1974
+ } catch (err) {
1975
+ console.warn(`Failed to import module at '${_logPath}'`, err);
1976
+ _module = null;
1977
+ }
1978
+ return { module: _module, path: _logPath };
1979
+ })
1980
+ );
1981
+ const filteredModules = modules.filter((m) => m.module);
1982
+ if (!filteredModules.length) {
1983
+ console.warn(`No valid modules were found in directory '${dir}'`);
2026
1984
  }
2027
- pause() {
2028
- if (this.task) {
2029
- this.task.stop();
2030
- this.emitter.emit("paused", this.task);
2031
- }
2032
- return this;
1985
+ return filteredModules;
1986
+ }
1987
+
1988
+ // src/modules/base-module.importer.ts
1989
+ var ModuleImporter = class {
1990
+ client;
1991
+ constructor(client) {
1992
+ this.client = client;
2033
1993
  }
2034
- async set(status) {
2035
- const statusConfig = createVimcordStatusConfig(status);
2036
- let clientStatus;
2037
- if (this.client.config.app.devMode) {
2038
- clientStatus = statusConfig.development;
2039
- } else {
2040
- clientStatus = statusConfig.production;
2041
- }
2042
- if (!clientStatus.interval) {
2043
- await this.setActivity(Array.isArray(clientStatus.activity) ? clientStatus.activity[0] : clientStatus.activity);
2044
- } else {
2045
- await this.scheduleStatusRotation(clientStatus);
1994
+ async importFrom(dir, set = false, suffix) {
1995
+ if (set) this.items.clear();
1996
+ const dirs = Array.isArray(dir) ? dir : [dir];
1997
+ const modules = [];
1998
+ const effectiveSuffix = Array.isArray(suffix) ? suffix[0] : suffix ?? this.itemSuffix;
1999
+ for (const _dir of dirs) {
2000
+ const results = await importModulesFromDir(_dir, effectiveSuffix ?? void 0);
2001
+ modules.push(...results.map(({ module: module2 }) => module2.default));
2046
2002
  }
2047
- return this;
2048
- }
2049
- async destroy() {
2050
- if (this.task) {
2051
- this.task.stop();
2052
- this.task = null;
2053
- this.emitter.emit("destroyed");
2054
- await this.clear();
2003
+ for (const module2 of modules) {
2004
+ const name = this.getName(module2);
2005
+ this.items.set(name, module2);
2055
2006
  }
2056
- return this;
2057
- }
2058
- async clear() {
2059
- const client = await this.getReadyClient();
2060
- this.clearData();
2061
- client.user.setActivity({ name: "" });
2062
- this.emitter.emit("cleared");
2063
- return this;
2007
+ this.client.logger.moduleLoaded(this.itemName, modules.length);
2008
+ return this.items;
2064
2009
  }
2065
2010
  };
2066
2011
 
2067
2012
  // src/modules/command.manager.ts
2068
- var import_discord9 = require("discord.js");
2069
- var BaseCommandManager = class {
2013
+ var BaseCommandManager = class extends ModuleImporter {
2070
2014
  type;
2071
- client;
2072
- commands = /* @__PURE__ */ new Map();
2073
- moduleSuffix;
2074
- constructor(client, type, moduleSuffix) {
2015
+ items = /* @__PURE__ */ new Map();
2016
+ itemSuffix;
2017
+ constructor(client, type, itemSuffix) {
2018
+ super(client);
2075
2019
  this.type = type;
2076
- this.client = client;
2077
- this.moduleSuffix = moduleSuffix;
2020
+ switch (type) {
2021
+ case 0 /* Slash */:
2022
+ this.itemSuffix = itemSuffix ?? DEFAULT_MODULE_SUFFIXES.slashCommands;
2023
+ break;
2024
+ case 2 /* Context */:
2025
+ this.itemSuffix = itemSuffix ?? DEFAULT_MODULE_SUFFIXES.contextCommands;
2026
+ break;
2027
+ case 1 /* Prefix */:
2028
+ this.itemSuffix = itemSuffix ?? DEFAULT_MODULE_SUFFIXES.prefixCommands;
2029
+ break;
2030
+ }
2031
+ }
2032
+ get commands() {
2033
+ return this.items;
2034
+ }
2035
+ get itemName() {
2036
+ switch (this.type) {
2037
+ case 0 /* Slash */:
2038
+ return "Slash Commands";
2039
+ case 2 /* Context */:
2040
+ return "Context Commands";
2041
+ case 1 /* Prefix */:
2042
+ return "Prefix Commands";
2043
+ }
2044
+ }
2045
+ getName(module2) {
2046
+ return "builder" in module2 ? module2.builder.name : module2.options.name;
2078
2047
  }
2079
2048
  /**
2080
2049
  * Gets a command by name.
@@ -2083,7 +2052,7 @@ var BaseCommandManager = class {
2083
2052
  if (this.type === 1 /* Prefix */) {
2084
2053
  const config = this.client.config.prefixCommands;
2085
2054
  const search = config.allowCaseInsensitiveCommandNames ? name.toLowerCase() : name;
2086
- return Array.from(this.commands.values()).find((cmd) => {
2055
+ return Array.from(this.items.values()).find((cmd) => {
2087
2056
  const commandName = "builder" in cmd ? cmd.builder.name : cmd.options.name;
2088
2057
  const trigger = config.allowCaseInsensitiveCommandNames ? commandName.toLowerCase() : commandName;
2089
2058
  if (trigger === search) return true;
@@ -2094,7 +2063,7 @@ var BaseCommandManager = class {
2094
2063
  }
2095
2064
  });
2096
2065
  } else {
2097
- return this.commands.get(name);
2066
+ return this.items.get(name);
2098
2067
  }
2099
2068
  }
2100
2069
  /**
@@ -2103,7 +2072,7 @@ var BaseCommandManager = class {
2103
2072
  getAll(options = {}) {
2104
2073
  const matchedCommands = /* @__PURE__ */ new Map();
2105
2074
  const isDev = this.client.config.app.devMode;
2106
- for (const cmd of this.commands.values()) {
2075
+ for (const cmd of this.items.values()) {
2107
2076
  const commandName = "builder" in cmd ? cmd.builder.name : cmd.options.name;
2108
2077
  if (options.names || options.fuzzyNames) {
2109
2078
  const nameMatched = options.names?.includes(commandName) || options.fuzzyNames?.some((fuzzy) => commandName.includes(fuzzy));
@@ -2130,7 +2099,7 @@ var BaseCommandManager = class {
2130
2099
  */
2131
2100
  sortByCategory() {
2132
2101
  const categories = /* @__PURE__ */ new Map();
2133
- for (const cmd of this.commands.values()) {
2102
+ for (const cmd of this.items.values()) {
2134
2103
  const metadata = cmd.options.metadata;
2135
2104
  if (!metadata?.category) continue;
2136
2105
  let entry = categories.get(metadata.category);
@@ -2153,52 +2122,20 @@ var BaseCommandManager = class {
2153
2122
  return cat;
2154
2123
  });
2155
2124
  }
2156
- /**
2157
- * Imports command modules from a directory.
2158
- * @param dir Path of one or more folders.
2159
- * @param set Replaces imported command modules with the ones found.
2160
- */
2161
- async importFrom(dir, set = false) {
2162
- if (set) this.commands.clear();
2163
- const dirs = Array.isArray(dir) ? dir : [dir];
2164
- const modules = [];
2165
- for (const _dir of dirs) {
2166
- const results = await importModulesFromDir(_dir, this.moduleSuffix);
2167
- modules.push(...results.map(({ module: module2 }) => module2.default));
2168
- }
2169
- for (const module2 of modules) {
2170
- const commandName = "builder" in module2 ? module2.builder.name : module2.options.name;
2171
- this.commands.set(commandName, module2);
2172
- }
2173
- let moduleType;
2174
- switch (this.type) {
2175
- case 0 /* Slash */:
2176
- moduleType = "Slash Commands";
2177
- break;
2178
- case 2 /* Context */:
2179
- moduleType = "Context Commands";
2180
- break;
2181
- case 1 /* Prefix */:
2182
- moduleType = "Prefix Commands";
2183
- break;
2184
- }
2185
- this.client.logger.moduleLoaded(moduleType, modules.length);
2186
- return this.commands;
2187
- }
2188
2125
  };
2189
2126
  var SlashCommandManager = class extends BaseCommandManager {
2190
2127
  constructor(client) {
2191
- super(client, 0 /* Slash */, client.config.app.moduleSuffixes.slashCommand);
2128
+ super(client, 0 /* Slash */);
2192
2129
  }
2193
2130
  };
2194
2131
  var ContextCommandManager = class extends BaseCommandManager {
2195
2132
  constructor(client) {
2196
- super(client, 2 /* Context */, client.config.app.moduleSuffixes.contextCommand);
2133
+ super(client, 2 /* Context */);
2197
2134
  }
2198
2135
  };
2199
2136
  var PrefixCommandManager = class extends BaseCommandManager {
2200
2137
  constructor(client) {
2201
- super(client, 1 /* Prefix */, client.config.app.moduleSuffixes.prefixCommand);
2138
+ super(client, 1 /* Prefix */);
2202
2139
  }
2203
2140
  };
2204
2141
  var CommandManager = class {
@@ -2216,7 +2153,7 @@ var CommandManager = class {
2216
2153
  return [...this.slash.getAll(options), ...this.context.getAll(options)];
2217
2154
  }
2218
2155
  async registerGlobal(options = {}) {
2219
- const client = await this.client.waitForReady();
2156
+ const client = await Vimcord.getReadyInstance(this.client.clientId);
2220
2157
  if (!client.rest) {
2221
2158
  console.error(`[CommandManager] \u2716 Failed to register app commands globally: REST is not initialized`);
2222
2159
  return;
@@ -2230,7 +2167,7 @@ var CommandManager = class {
2230
2167
  `[CommandManager] Registering (${commands.length}) ${commands.length === 1 ? "command" : "commands"} globally...`
2231
2168
  );
2232
2169
  try {
2233
- await client.rest.put(import_discord9.Routes.applicationCommands(client.user.id), { body: commands });
2170
+ await client.rest.put(import_discord8.Routes.applicationCommands(client.user.id), { body: commands });
2234
2171
  console.log(`[CommandManager] \u2714 Registered app ${commands.length === 1 ? "command" : "commands"} globally`);
2235
2172
  } catch (err) {
2236
2173
  console.error(
@@ -2240,20 +2177,20 @@ var CommandManager = class {
2240
2177
  }
2241
2178
  }
2242
2179
  async unregisterGlobal() {
2243
- const client = await this.client.waitForReady();
2180
+ const client = await Vimcord.getReadyInstance(this.client.clientId);
2244
2181
  if (!client.rest) {
2245
2182
  console.error(`[CommandManager] \u2716 Failed to remove app commands globally: REST is not initialized`);
2246
2183
  return;
2247
2184
  }
2248
2185
  try {
2249
- await client.rest.put(import_discord9.Routes.applicationCommands(client.user.id), { body: [] });
2186
+ await client.rest.put(import_discord8.Routes.applicationCommands(client.user.id), { body: [] });
2250
2187
  console.log(`[CommandManager] \u2714 Removed app commands globally`);
2251
2188
  } catch (err) {
2252
2189
  console.error(`[CommandManager] \u2716 Failed to remove app commands globally`, err);
2253
2190
  }
2254
2191
  }
2255
2192
  async registerGuild(options = {}) {
2256
- const client = await this.client.waitForReady();
2193
+ const client = await Vimcord.getReadyInstance(this.client.clientId);
2257
2194
  if (!client.rest) {
2258
2195
  console.error(`[CommandManager] \u2716 Failed to register app commands by guild: REST is not initialized`);
2259
2196
  return;
@@ -2269,7 +2206,7 @@ var CommandManager = class {
2269
2206
  );
2270
2207
  await Promise.all(
2271
2208
  guildIds.map(
2272
- (guildId) => client.rest.put(import_discord9.Routes.applicationGuildCommands(client.user.id, guildId), { body: commands }).then(() => {
2209
+ (guildId) => client.rest.put(import_discord8.Routes.applicationGuildCommands(client.user.id, guildId), { body: commands }).then(() => {
2273
2210
  const gName = client.guilds.cache.get(guildId)?.name || "n/a";
2274
2211
  console.log(
2275
2212
  `[CommandManager] \u2714 Set app ${commands.length === 1 ? "command" : "commands"} in guild: ${guildId} (${gName})`
@@ -2285,7 +2222,7 @@ var CommandManager = class {
2285
2222
  );
2286
2223
  }
2287
2224
  async unregisterGuild(options = {}) {
2288
- const client = await this.client.waitForReady();
2225
+ const client = await Vimcord.getReadyInstance(this.client.clientId);
2289
2226
  if (!client.rest) {
2290
2227
  console.error(`[CommandManager] \u2716 Failed to register app commands by guild: REST is not initialized`);
2291
2228
  return;
@@ -2294,127 +2231,384 @@ var CommandManager = class {
2294
2231
  console.log(`[CommandManager] Unregistering commands from ${guildIds.length} guilds...`);
2295
2232
  await Promise.all(
2296
2233
  guildIds.map(
2297
- (guildId) => client.rest.put(import_discord9.Routes.applicationGuildCommands(client.user.id, guildId), { body: [] }).then(() => console.log(`[CommandManager] \u2714 Removed app commands in guild: ${guildId}`)).catch((err) => console.log(`[CommandManager] \u2716 Failed to remove app commands in guild: ${guildId}`, err))
2234
+ (guildId) => client.rest.put(import_discord8.Routes.applicationGuildCommands(client.user.id, guildId), { body: [] }).then(() => console.log(`[CommandManager] \u2714 Removed app commands in guild: ${guildId}`)).catch((err) => console.log(`[CommandManager] \u2716 Failed to remove app commands in guild: ${guildId}`, err))
2298
2235
  )
2299
2236
  );
2300
2237
  }
2301
2238
  };
2302
2239
 
2303
2240
  // src/modules/event.manager.ts
2304
- var import_discord10 = require("discord.js");
2305
- var EventManager = class {
2306
- client;
2307
- events = /* @__PURE__ */ new Map();
2241
+ var import_discord9 = require("discord.js");
2242
+ var EventManager = class extends ModuleImporter {
2243
+ items = /* @__PURE__ */ new Map();
2244
+ itemSuffix = "event";
2245
+ itemName = "Event Handlers";
2308
2246
  logger;
2309
2247
  constructor(client) {
2310
- this.client = client;
2248
+ super(client);
2311
2249
  this.logger = new Logger({ prefixEmoji: "\u{1F4CB}", prefix: `EventManager (i${this.client.clientId})` });
2312
- for (const event of Object.values(import_discord10.Events)) {
2250
+ for (const event of Object.values(import_discord9.Events)) {
2313
2251
  client.on(
2314
2252
  event.toString(),
2315
2253
  async (...args) => this.executeEvents.apply(this, [event, ...args])
2316
2254
  );
2317
2255
  }
2318
2256
  }
2257
+ getName(module2) {
2258
+ return module2.name;
2259
+ }
2319
2260
  register(...events) {
2320
2261
  for (const event of events) {
2321
- this.events.set(event.name, event);
2262
+ this.items.set(event.name, event);
2322
2263
  if (this.client.config.app.verbose) {
2323
2264
  this.logger.debug(`'${event.name}' registered for EventType '${event.event}'`);
2324
2265
  }
2325
2266
  }
2326
2267
  }
2327
- unregister(...names) {
2328
- for (const name of names) {
2329
- const event = this.events.get(name);
2330
- if (!event) continue;
2331
- this.events.delete(name);
2332
- if (this.client.config.app.verbose) {
2333
- this.logger.debug(`'${event.name}' unregistered for EventType '${event.event}'`);
2334
- }
2268
+ unregister(...names) {
2269
+ for (const name of names) {
2270
+ const event = this.items.get(name);
2271
+ if (!event) continue;
2272
+ this.items.delete(name);
2273
+ if (this.client.config.app.verbose) {
2274
+ this.logger.debug(`'${event.name}' unregistered for EventType '${event.event}'`);
2275
+ }
2276
+ }
2277
+ }
2278
+ clear() {
2279
+ this.items.forEach((e) => this.unregister(e.name));
2280
+ this.items.clear();
2281
+ }
2282
+ get(name) {
2283
+ return this.items.get(name);
2284
+ }
2285
+ getByTag(tag) {
2286
+ return Array.from(this.items.values()).filter((event) => event.metadata?.tags?.includes(tag));
2287
+ }
2288
+ getByCategory(category) {
2289
+ return Array.from(this.items.values()).filter((event) => event.metadata?.category?.includes(category));
2290
+ }
2291
+ getByEvent(eventType) {
2292
+ return Array.from(this.items.values()).filter((event) => event.event === eventType);
2293
+ }
2294
+ async executeEvents(eventType, ...args) {
2295
+ const events = this.getByEvent(eventType);
2296
+ if (!events.length) return;
2297
+ const sortedEvents = events.sort((a, b) => b.priority - a.priority);
2298
+ await Promise.all(
2299
+ sortedEvents.map(async (event) => {
2300
+ try {
2301
+ await event.execute?.(this.client, ...args);
2302
+ if (event.once) {
2303
+ this.unregister(event.name);
2304
+ }
2305
+ } catch (err) {
2306
+ this.logger.error(`'${event.name}' failed to execute`, err);
2307
+ }
2308
+ })
2309
+ );
2310
+ }
2311
+ };
2312
+
2313
+ // src/tools/utils.ts
2314
+ var MENTION_OR_SNOWFLAKE_REGEX = /<@[#&]?[\d]{6,}>|[\d]{6,}/;
2315
+ var fetchUserPromises = /* @__PURE__ */ new Map();
2316
+ var fetchGuildPromises = /* @__PURE__ */ new Map();
2317
+ var fetchMemberPromises = /* @__PURE__ */ new Map();
2318
+ var fetchChannelPromises = /* @__PURE__ */ new Map();
2319
+ var fetchMessagePromises = /* @__PURE__ */ new Map();
2320
+ var fetchRolePromises = /* @__PURE__ */ new Map();
2321
+ function createCachedFetch(cache, fetchFn, cacheKey) {
2322
+ const cached = cache.get(cacheKey);
2323
+ if (cached) return cached;
2324
+ const promise = fetchFn().finally(() => cache.delete(cacheKey));
2325
+ cache.set(cacheKey, promise);
2326
+ return promise;
2327
+ }
2328
+ function __zero(str) {
2329
+ return str?.length ? str : "0";
2330
+ }
2331
+ function isMentionOrSnowflake(str) {
2332
+ return str ? MENTION_OR_SNOWFLAKE_REGEX.test(str) : false;
2333
+ }
2334
+ function cleanMention(str) {
2335
+ return str ? str.replaceAll(/[<@#&>]/g, "").trim() : void 0;
2336
+ }
2337
+ async function getMessageMention(message, content, type, index = 0, idOnly) {
2338
+ const args = content?.split(" ");
2339
+ const arg = isMentionOrSnowflake(args?.[index]) ? cleanMention(args?.[index]) : void 0;
2340
+ switch (type) {
2341
+ case "user": {
2342
+ const userMention2 = message.mentions.users.at(index) || null;
2343
+ if (!userMention2 && arg) {
2344
+ return idOnly ? arg : await fetchUser(message.client, arg);
2345
+ }
2346
+ return idOnly ? userMention2?.id || null : userMention2;
2347
+ }
2348
+ case "member": {
2349
+ if (!message.guild) return null;
2350
+ const member = await fetchMember(message.guild, message.mentions.users.at(index)?.id ?? arg);
2351
+ return idOnly ? member?.id || null : member;
2352
+ }
2353
+ case "channel": {
2354
+ const channelMention = message.mentions.channels.at(index) || null;
2355
+ if (!channelMention && arg) {
2356
+ if (idOnly) return arg;
2357
+ const channel = message.guild ? await fetchChannel(message.guild, arg) : message.client.channels.cache.get(__zero(arg)) ?? message.client.channels.fetch(__zero(arg));
2358
+ return channel;
2359
+ }
2360
+ return idOnly ? channelMention?.id || null : channelMention;
2361
+ }
2362
+ case "role": {
2363
+ const roleMention = message.mentions.roles.at(index) || null;
2364
+ if (!roleMention && arg) {
2365
+ if (idOnly) return arg;
2366
+ return message.guild ? await fetchRole(message.guild, arg) : null;
2367
+ }
2368
+ return idOnly ? roleMention?.id || null : roleMention;
2369
+ }
2370
+ default:
2371
+ return null;
2372
+ }
2373
+ }
2374
+ function getFirstMentionId(options) {
2375
+ let mentionId = "";
2376
+ if (options.message) {
2377
+ switch (options.type) {
2378
+ case "user":
2379
+ mentionId = options.message.mentions.users.first()?.id || "";
2380
+ break;
2381
+ case "member":
2382
+ mentionId = options.message.mentions.members?.first()?.id || "";
2383
+ break;
2384
+ case "channel":
2385
+ mentionId = options.message.mentions.channels.first()?.id || "";
2386
+ break;
2387
+ case "role":
2388
+ mentionId = options.message.mentions.roles.first()?.id || "";
2389
+ break;
2390
+ }
2391
+ }
2392
+ const firstArg = options.content?.split(" ")[0] || "";
2393
+ return mentionId || isMentionOrSnowflake(firstArg) ? cleanMention(firstArg) : "";
2394
+ }
2395
+ async function fetchUser(client, userId) {
2396
+ if (!userId) return null;
2397
+ const key = `${client.user.id}-${userId}`;
2398
+ const cached = client.users.cache.get(__zero(userId));
2399
+ if (cached) return cached;
2400
+ return createCachedFetch(fetchUserPromises, () => client.users.fetch(__zero(userId)).catch(() => null), key);
2401
+ }
2402
+ async function fetchGuild(client, guildId) {
2403
+ if (!guildId) return null;
2404
+ const key = `${client.user.id}-${guildId}`;
2405
+ const cached = client.guilds.cache.get(__zero(guildId));
2406
+ if (cached) return cached;
2407
+ return createCachedFetch(fetchGuildPromises, () => client.guilds.fetch(__zero(guildId)).catch(() => null), key);
2408
+ }
2409
+ async function fetchMember(guild, memberId) {
2410
+ if (!memberId) return null;
2411
+ const key = `${guild.id}-${memberId}`;
2412
+ const cached = guild.members.cache.get(__zero(memberId));
2413
+ if (cached) return cached;
2414
+ return createCachedFetch(fetchMemberPromises, () => guild.members.fetch(__zero(memberId)).catch(() => null), key);
2415
+ }
2416
+ async function fetchChannel(guild, channelId, type) {
2417
+ if (!channelId) return null;
2418
+ const key = `${guild.id}-${channelId}`;
2419
+ const cached = guild.channels.cache.get(__zero(channelId)) ?? null;
2420
+ if (cached) {
2421
+ if (type && cached.type !== type) return null;
2422
+ return cached;
2423
+ }
2424
+ const channel = await createCachedFetch(
2425
+ fetchChannelPromises,
2426
+ () => guild.channels.fetch(__zero(channelId)).catch(() => null),
2427
+ key
2428
+ );
2429
+ if (type && channel?.type !== type) return null;
2430
+ return channel;
2431
+ }
2432
+ async function fetchMessage(channel, messageId) {
2433
+ if (!messageId) return null;
2434
+ const key = `${channel.guild.id}-${messageId}`;
2435
+ const cached = channel.messages.cache.get(__zero(messageId));
2436
+ if (cached) return cached;
2437
+ return createCachedFetch(fetchMessagePromises, () => channel.messages.fetch(__zero(messageId)).catch(() => null), key);
2438
+ }
2439
+ async function fetchRole(guild, roleId) {
2440
+ if (!roleId) return null;
2441
+ const key = `${guild.id}-${roleId}`;
2442
+ const cached = guild.roles.cache.get(__zero(roleId));
2443
+ if (cached) return cached;
2444
+ return createCachedFetch(fetchRolePromises, () => guild.roles.fetch(__zero(roleId)).catch(() => null), key);
2445
+ }
2446
+
2447
+ // src/types/status.ts
2448
+ var import_discord10 = require("discord.js");
2449
+ var import_lodash6 = __toESM(require("lodash"));
2450
+ var StatusType = /* @__PURE__ */ ((StatusType2) => {
2451
+ StatusType2["DND"] = "dnd";
2452
+ StatusType2["Idle"] = "idle";
2453
+ StatusType2["Online"] = "online";
2454
+ StatusType2["Invisible"] = "invisible";
2455
+ return StatusType2;
2456
+ })(StatusType || {});
2457
+ var defaultPresence = {
2458
+ production: {
2459
+ interval: 6e4,
2460
+ randomize: false,
2461
+ activity: [
2462
+ { status: "online" /* Online */, type: import_discord10.ActivityType.Custom, name: "Need help? Use /help or !help" },
2463
+ { status: "online" /* Online */, type: import_discord10.ActivityType.Custom, name: "Join our community!" },
2464
+ { status: "online" /* Online */, type: import_discord10.ActivityType.Watching, name: "\u2728 $GUILD_COUNT servers" }
2465
+ ]
2466
+ },
2467
+ development: {
2468
+ activity: { status: "dnd" /* DND */, type: import_discord10.ActivityType.Custom, name: "In development!" }
2469
+ }
2470
+ };
2471
+ function createVimcordStatusConfig(options = {}) {
2472
+ return import_lodash6.default.merge(defaultPresence, options);
2473
+ }
2474
+
2475
+ // src/modules/status.manager.ts
2476
+ var import_node_events = __toESM(require("events"));
2477
+ var import_qznt2 = require("qznt");
2478
+ var StatusManager = class {
2479
+ client;
2480
+ logger;
2481
+ emitter = new import_node_events.default();
2482
+ lastActivity = null;
2483
+ lastActivityIndex = 0;
2484
+ task = null;
2485
+ constructor(client) {
2486
+ this.client = client;
2487
+ this.logger = new Logger({ prefixEmoji: "\u{1F4AC}", prefix: `StatusManager (i${this.client.clientId})` });
2488
+ this.emitter.on("changed", (activity) => {
2489
+ if (this.client.config.app.verbose) {
2490
+ this.logger.debug(`Status changed to '${activity.name}'`);
2491
+ }
2492
+ });
2493
+ this.emitter.on("cleared", () => {
2494
+ if (this.client.config.app.verbose) {
2495
+ this.logger.debug("Status cleared");
2496
+ }
2497
+ });
2498
+ }
2499
+ clearData() {
2500
+ this.task?.stop();
2501
+ this.task = null;
2502
+ this.lastActivity = null;
2503
+ this.lastActivityIndex = 0;
2504
+ return this;
2505
+ }
2506
+ async getReadyClient() {
2507
+ const client = await Vimcord.getReadyInstance(this.client.clientId);
2508
+ if (!client.user) throw new Error("Cannot manage the client's activity when its user is not hydrated");
2509
+ return client;
2510
+ }
2511
+ async formatActivityName(name) {
2512
+ name = name.replace("$USER_COUNT", import_qznt2.$.format.number(this.client.users.cache.size)).replace("$GUILD_COUNT", import_qznt2.$.format.number(this.client.guilds.cache.size)).replace(
2513
+ "$INVITE",
2514
+ this.client.config.staff.guild.inviteUrl ? this.client.config.staff.guild.inviteUrl : "<STAFF_INVITE_URL_NOT_SET>"
2515
+ );
2516
+ if (name.includes("$STAFF_GUILD_MEMBER_COUNT")) {
2517
+ await fetchGuild(this.client, this.client.config.staff.guild.id).then((guild) => {
2518
+ if (!guild) return name = name.replace("$STAFF_GUILD_MEMBER_COUNT", "<STAFF_GUILD_NOT_FOUND>");
2519
+ name = name.replace("$STAFF_GUILD_MEMBER_COUNT", import_qznt2.$.format.number(guild.members.cache.size));
2520
+ }).catch((err) => this.logger.error("Failed to fetch the staff guild", err));
2335
2521
  }
2522
+ return name;
2336
2523
  }
2337
- clear() {
2338
- this.events.forEach((e) => this.unregister(e.name));
2339
- this.events.clear();
2524
+ async setActivity(activity) {
2525
+ const client = await this.getReadyClient();
2526
+ activity.name = await this.formatActivityName(activity.name);
2527
+ client.user.setStatus(activity.status);
2528
+ client.user.setActivity({ name: activity.name, type: activity.type, url: activity.streamUrl });
2529
+ this.emitter.emit("changed", activity);
2340
2530
  }
2341
- get(name) {
2342
- return this.events.get(name);
2531
+ async statusRotationTask(clientStatus) {
2532
+ let activity;
2533
+ if (clientStatus.randomize && Array.isArray(clientStatus.activity)) {
2534
+ activity = import_qznt2.$.rnd.choice(clientStatus.activity, { not: this.lastActivity });
2535
+ this.lastActivity = activity;
2536
+ } else {
2537
+ const activityIndex = (this.lastActivityIndex + 1) % clientStatus.activity.length;
2538
+ this.lastActivityIndex = activityIndex;
2539
+ activity = clientStatus.activity[activityIndex];
2540
+ }
2541
+ await this.setActivity(activity);
2542
+ this.emitter.emit("rotation", activity);
2343
2543
  }
2344
- getByTag(tag) {
2345
- return Array.from(this.events.values()).filter((event) => event.metadata?.tags?.includes(tag));
2544
+ async scheduleStatusRotation(clientStatus) {
2545
+ if (!clientStatus.interval) throw new Error("Cannot create client activity interval without interval time");
2546
+ this.task?.stop();
2547
+ this.task = null;
2548
+ this.task = new import_qznt2.$.Loop(() => this.statusRotationTask(clientStatus), import_qznt2.$.math.ms(clientStatus.interval), true);
2549
+ this.start();
2346
2550
  }
2347
- getByCategory(category) {
2348
- return Array.from(this.events.values()).filter((event) => event.metadata?.category?.includes(category));
2551
+ start() {
2552
+ if (this.task) {
2553
+ this.task.start();
2554
+ this.emitter.emit("started", this.task);
2555
+ }
2556
+ return this;
2349
2557
  }
2350
- getByEvent(eventType) {
2351
- return Array.from(this.events.values()).filter((event) => event.event === eventType);
2558
+ pause() {
2559
+ if (this.task) {
2560
+ this.task.stop();
2561
+ this.emitter.emit("paused", this.task);
2562
+ }
2563
+ return this;
2352
2564
  }
2353
- async executeEvents(eventType, ...args) {
2354
- const events = this.getByEvent(eventType);
2355
- if (!events.length) return;
2356
- const sortedEvents = events.sort((a, b) => b.priority - a.priority);
2357
- await Promise.all(
2358
- sortedEvents.map(async (event) => {
2359
- try {
2360
- await event.execute?.(this.client, ...args);
2361
- if (event.once) {
2362
- this.unregister(event.name);
2363
- }
2364
- } catch (err) {
2365
- this.logger.error(`'${event.name}' failed to execute`, err);
2366
- }
2367
- })
2368
- );
2565
+ async set(status) {
2566
+ const statusConfig = createVimcordStatusConfig(status);
2567
+ let clientStatus;
2568
+ if (this.client.config.app.devMode) {
2569
+ clientStatus = statusConfig.development;
2570
+ } else {
2571
+ clientStatus = statusConfig.production;
2572
+ }
2573
+ if (!clientStatus.interval) {
2574
+ await this.setActivity(Array.isArray(clientStatus.activity) ? clientStatus.activity[0] : clientStatus.activity);
2575
+ } else {
2576
+ await this.scheduleStatusRotation(clientStatus);
2577
+ }
2578
+ return this;
2369
2579
  }
2370
- /** Import event modules that end with `.event` */
2371
- async importFrom(dir, replaceAll) {
2372
- dir = Array.isArray(dir) ? dir : [dir];
2373
- const eventModules = await Promise.all(
2374
- dir.map((dir2) => importModulesFromDir(dir2, "event"))
2375
- );
2376
- if (replaceAll) {
2377
- this.clear();
2378
- }
2379
- let importedEvents = 0;
2380
- let ignoredEvents = 0;
2381
- for (const event of eventModules.flat()) {
2382
- if (!event.module.default.enabled) {
2383
- ignoredEvents++;
2384
- } else {
2385
- importedEvents++;
2386
- }
2387
- this.register(event.module.default);
2580
+ async destroy() {
2581
+ if (this.task) {
2582
+ this.task.stop();
2583
+ this.task = null;
2584
+ this.emitter.emit("destroyed");
2585
+ await this.clear();
2388
2586
  }
2389
- this.client.logger.moduleLoaded("Event Handlers", importedEvents, ignoredEvents);
2390
- return this.events;
2587
+ return this;
2588
+ }
2589
+ async clear() {
2590
+ const client = await this.getReadyClient();
2591
+ this.clearData();
2592
+ client.user.setActivity({ name: "" });
2593
+ this.emitter.emit("cleared");
2594
+ return this;
2391
2595
  }
2392
2596
  };
2393
2597
 
2394
- // package.json
2395
- var version = "1.0.36";
2396
-
2397
- // src/client.ts
2398
- var import_node_crypto3 = require("crypto");
2399
- var import_qznt4 = require("qznt");
2400
-
2401
- // src/utils/VimcordCLI.ts
2598
+ // src/utils/vimcord.cli.ts
2402
2599
  var import_node_readline = require("readline");
2403
2600
  var import_qznt3 = require("qznt");
2404
-
2405
- // src/utils/clientUtils.ts
2406
- function useClient(clientId = 0) {
2407
- return Vimcord.instances.get(clientId);
2408
- }
2409
- async function useReadyClient(clientId = 0) {
2410
- return useClient(clientId)?.waitForReady();
2411
- }
2412
- function createClient(options, features = {}, config = {}) {
2413
- return new Vimcord(options, features, config);
2414
- }
2415
-
2416
- // src/utils/VimcordCLI.ts
2417
- var VimcordCLI = class {
2601
+ var VimcordCLI = class _VimcordCLI {
2602
+ static mode = "off";
2603
+ static setMode(mode) {
2604
+ if (_VimcordCLI.mode === mode) return;
2605
+ _VimcordCLI.mode = mode;
2606
+ if (mode === "on") {
2607
+ CLI.logger.log(`~ Type ${CLI.options.prefix}help to view available commands`);
2608
+ } else {
2609
+ CLI.logger.log(`~ [MODE] Now set to "${mode}"`);
2610
+ }
2611
+ }
2418
2612
  rl;
2419
2613
  options;
2420
2614
  commands = /* @__PURE__ */ new Map();
@@ -2427,6 +2621,7 @@ var VimcordCLI = class {
2427
2621
  terminal: false
2428
2622
  });
2429
2623
  this.rl.on("line", (line) => {
2624
+ if (_VimcordCLI.mode !== "on") return;
2430
2625
  const { isCommand, commandName, content, args } = this.parseLine(line);
2431
2626
  if (!isCommand) return;
2432
2627
  const command = this.commands.get(commandName);
@@ -2483,7 +2678,6 @@ var VimcordCLI = class {
2483
2678
  return true;
2484
2679
  }
2485
2680
  };
2486
- var initCalled = false;
2487
2681
  var CLI = new VimcordCLI({ prefix: "/" });
2488
2682
  CLI.addCommand("help", "View information about a command, or the available CLI options", (args) => {
2489
2683
  const prefix = CLI.options.prefix;
@@ -2590,16 +2784,81 @@ CLI.addCommand("cmds", "List the loaded commands", async (args, content) => {
2590
2784
  return CLI.logger.error(`'${mode}' is not a valid option. Valid options: [slash|prefix|ctx]`);
2591
2785
  }
2592
2786
  });
2593
- function initCLI() {
2594
- if (initCalled) return;
2595
- CLI.logger.log(`~ Type ${CLI.options.prefix}help to view available commands`);
2596
- initCalled = true;
2597
- }
2598
2787
 
2599
- // src/client.ts
2600
- var import_chalk2 = __toESM(require("chalk"));
2788
+ // src/client/Vimcord.ts
2789
+ var import_discord11 = require("discord.js");
2790
+ var import_dotenv = require("dotenv");
2791
+ var import_node_crypto3 = require("crypto");
2792
+ var import_node_events2 = __toESM(require("events"));
2793
+ var import_qznt4 = require("qznt");
2601
2794
  var Vimcord = class _Vimcord extends import_discord11.Client {
2602
2795
  static instances = /* @__PURE__ */ new Map();
2796
+ static emitter = new import_node_events2.default();
2797
+ clientStartingPromise = null;
2798
+ static create(optionsOrConfig, features, config) {
2799
+ if ("options" in optionsOrConfig) {
2800
+ const { options, features: features2, config: config2 } = optionsOrConfig;
2801
+ return new _Vimcord(options, features2, config2);
2802
+ } else {
2803
+ return new _Vimcord(optionsOrConfig, features, config);
2804
+ }
2805
+ }
2806
+ /**
2807
+ * Returns an instance of Vimcord.
2808
+ * @param clientId [default: 0]
2809
+ */
2810
+ static getInstance(clientId) {
2811
+ if (clientId === void 0) {
2812
+ return _Vimcord.instances.values().next().value;
2813
+ }
2814
+ return _Vimcord.instances.get(clientId);
2815
+ }
2816
+ /**
2817
+ * Waits for a Vimcord instance to be ready.
2818
+ * @param clientId [default: 0]
2819
+ * @param timeoutMs [default: 60000]
2820
+ */
2821
+ static async getReadyInstance(clientId, timeoutMs = 6e4) {
2822
+ const client = _Vimcord.getInstance(clientId);
2823
+ if (client?.isReady()) {
2824
+ _Vimcord.emitter.emit("ready", client);
2825
+ return client;
2826
+ }
2827
+ if (client) {
2828
+ return new Promise((resolve, reject) => {
2829
+ const timeout = setTimeout(() => {
2830
+ _Vimcord.emitter.off("ready", listener);
2831
+ reject(new Error(`Client (i${clientId ?? 0}) timed out waiting for ready`));
2832
+ }, timeoutMs);
2833
+ const listener = (c) => {
2834
+ if (c.clientId === (clientId ?? 0)) {
2835
+ clearTimeout(timeout);
2836
+ _Vimcord.emitter.off("ready", listener);
2837
+ resolve(c);
2838
+ }
2839
+ };
2840
+ client.once("clientReady", () => {
2841
+ clearTimeout(timeout);
2842
+ _Vimcord.emitter.emit("ready", client);
2843
+ resolve(client);
2844
+ });
2845
+ });
2846
+ }
2847
+ return new Promise((resolve, reject) => {
2848
+ const timeout = setTimeout(() => {
2849
+ _Vimcord.emitter.off("ready", listener);
2850
+ reject(new Error(`Vimcord instance (i${clientId ?? 0}) failed to initialize within ${timeoutMs / 1e3}s.`));
2851
+ }, timeoutMs);
2852
+ const listener = (c) => {
2853
+ if (c.clientId === (clientId ?? 0)) {
2854
+ clearTimeout(timeout);
2855
+ _Vimcord.emitter.off("ready", listener);
2856
+ resolve(c);
2857
+ }
2858
+ };
2859
+ _Vimcord.emitter.on("ready", listener);
2860
+ });
2861
+ }
2603
2862
  uuid = (0, import_node_crypto3.randomUUID)();
2604
2863
  clientId = _Vimcord.instances.size;
2605
2864
  clientOptions;
@@ -2609,229 +2868,181 @@ var Vimcord = class _Vimcord extends import_discord11.Client {
2609
2868
  events;
2610
2869
  commands;
2611
2870
  db;
2612
- // Configure custom logger
2613
- logger = new Logger({ prefixEmoji: "\u26A1", prefix: `vimcord (i${this.clientId})` }).extend({
2614
- clientBanner(client) {
2615
- if (client.config.app.disableBanner) return;
2616
- const border = "\u2550".repeat(50);
2617
- console.log(import_chalk2.default.hex(this.colors.primary)(`
2618
- \u2554${border}\u2557`));
2619
- console.log(
2620
- import_chalk2.default.hex(this.colors.primary)("\u2551") + import_chalk2.default.bold.hex(this.colors.text)(
2621
- ` \u{1F680} ${client.config.app.name} v${client.config.app.appVersion}`.padEnd(
2622
- 50 - (client.config.app.devMode ? 12 : 0)
2623
- )
2624
- ) + import_chalk2.default.hex(this.colors.primary)(
2625
- `${client.config.app.devMode ? import_chalk2.default.hex(this.colors.warn)("devMode \u26A0\uFE0F ") : ""}\u2551`
2626
- )
2627
- );
2628
- console.log(import_chalk2.default.hex(this.colors.primary)(`\u2551${"".padEnd(50)}\u2551`));
2629
- console.log(
2630
- import_chalk2.default.hex(this.colors.primary)("\u2551") + import_chalk2.default.hex(this.colors.muted)(
2631
- ` # Powered by Vimcord v${version}`.padEnd(50 - 3 - `${client.clientId}`.length)
2632
- ) + import_chalk2.default.hex(this.colors.primary)(`${import_chalk2.default.hex(this.colors.muted)(`i${client.clientId}`)} \u2551`)
2633
- );
2634
- console.log(import_chalk2.default.hex(this.colors.primary)(`\u255A${border}\u255D
2635
- `));
2636
- },
2637
- clientReady(clientTag, guildCount) {
2638
- console.log(
2639
- this.formatTimestamp(),
2640
- this.formatPrefix(),
2641
- import_chalk2.default.hex(this.colors.success)("\u{1F916} READY"),
2642
- import_chalk2.default.white(`Connected as ${import_chalk2.default.bold.hex(this.colors.primary)(clientTag)}`),
2643
- import_chalk2.default.hex(this.colors.muted)(`\u2022 ${guildCount} guilds`)
2644
- );
2645
- },
2646
- moduleLoaded(moduleName, count, ignoredCount) {
2647
- const countText = count ? import_chalk2.default.hex(this.colors.muted)(`(${count} items)`) : "";
2648
- console.log(
2649
- this.formatTimestamp(),
2650
- this.formatPrefix(),
2651
- import_chalk2.default.hex("#9B59B6")("\u{1F4E6} MODULE"),
2652
- import_chalk2.default.hex(this.colors.warn)(`${moduleName} loaded`),
2653
- ignoredCount ? import_chalk2.default.hex(this.colors.muted)(`(${ignoredCount} ignored)`) : "",
2654
- countText
2655
- );
2656
- },
2657
- commandExecuted(commandName, username, guildName) {
2658
- const location = guildName ? `in ${import_chalk2.default.hex(this.colors.muted)(guildName)}` : "in DMs";
2659
- console.log(
2660
- this.formatTimestamp(),
2661
- this.formatPrefix(),
2662
- import_chalk2.default.hex("#87CEEB")("\u{1F4DD} COMMAND"),
2663
- import_chalk2.default.hex(this.colors.warn)(`/${commandName}`),
2664
- import_chalk2.default.white(`used by ${import_chalk2.default.bold(username)}`),
2665
- import_chalk2.default.hex(this.colors.muted)(location)
2666
- );
2667
- },
2668
- database(action, details) {
2669
- console.log(
2670
- this.formatTimestamp(),
2671
- this.formatPrefix(),
2672
- import_chalk2.default.hex("#FF6B9D")("\u{1F5C4}\uFE0F DATABASE"),
2673
- import_chalk2.default.white(action),
2674
- details ? import_chalk2.default.hex(this.colors.muted)(details) : ""
2675
- );
2676
- }
2677
- });
2678
- clientStartingPromise = null;
2871
+ logger = clientLoggerFactory(this);
2872
+ error;
2679
2873
  constructor(options, features = {}, config = {}) {
2680
2874
  super(options);
2681
2875
  this.clientOptions = options;
2682
2876
  this.features = features;
2683
- if (this.features.useEnv) {
2684
- if (typeof this.features.useEnv === "object") {
2685
- import_dotenv.default.config({ quiet: true, ...this.features.useEnv });
2686
- } else {
2687
- import_dotenv.default.config({ quiet: true });
2688
- }
2689
- }
2877
+ this.config = defineVimcordConfig(config);
2878
+ this.error = new ErrorHandler(this);
2690
2879
  if (this.features.useGlobalErrorHandlers) {
2691
- process.on("uncaughtException", (err) => this.logger.error("Uncaught Exception", err));
2692
- process.on("unhandledRejection", (err) => this.logger.error("Unhandled Rejection", err));
2693
- process.on("exit", (code) => this.logger.debug(`Process exited with code ${code}`));
2694
- this.on("error", (err) => this.logger.error("Client Error", err));
2695
- this.on("shardError", (err) => this.logger.error("Client Shard Error", err));
2696
- }
2697
- this.config = {
2698
- app: createAppConfig(config.app),
2699
- staff: createStaffConfig(config.staff),
2700
- slashCommands: createSlashCommandConfig(config.slashCommands),
2701
- prefixCommands: createPrefixCommandConfig(config.prefixCommands),
2702
- contextCommands: createContextCommandConfig(config.contextCommands)
2703
- };
2880
+ this.error.setupGlobalHandlers();
2881
+ }
2704
2882
  this.status = new StatusManager(this);
2705
2883
  this.events = new EventManager(this);
2706
2884
  this.commands = new CommandManager(this);
2707
2885
  this.logger.clientBanner(this);
2708
- this.once("clientReady", (client) => {
2709
- this.logger.clientReady(client.user.tag, client.guilds.cache.size);
2710
- });
2886
+ this.once("clientReady", (client) => this.logger.clientReady(client.user.tag, client.guilds.cache.size));
2711
2887
  _Vimcord.instances.set(this.clientId, this);
2712
- initCLI();
2888
+ if (this.config.app.enableCLI) {
2889
+ VimcordCLI.setMode("on");
2890
+ }
2713
2891
  }
2714
- /** Returns the options, features, and config of this client. */
2715
- toJSON() {
2716
- return {
2717
- options: this.clientOptions,
2718
- features: this.features,
2719
- config: this.config
2720
- };
2892
+ /** Current app name */
2893
+ // prettier-ignore
2894
+ get $name() {
2895
+ return this.config.app.name;
2721
2896
  }
2722
- /** Makes a clone of this client. */
2723
- clone() {
2724
- const { options, features, config } = this.toJSON();
2725
- return new _Vimcord(options, features, config);
2897
+ // prettier-ignore
2898
+ set $name(name) {
2899
+ this.config.app.name = name;
2726
2900
  }
2727
- configureApp(options = {}) {
2728
- this.config.app = createAppConfig(options);
2729
- if (this.features.hookToolsDevMode) {
2730
- globalToolsConfig.devMode = this.config.app.devMode;
2731
- }
2732
- return this;
2901
+ /** Current app version */
2902
+ // prettier-ignore
2903
+ get $version() {
2904
+ return this.config.app.version;
2733
2905
  }
2734
- configureStaff(options = {}) {
2735
- this.config.staff = createStaffConfig(options);
2736
- return this;
2906
+ // prettier-ignore
2907
+ set $version(version2) {
2908
+ this.config.app.version = version2;
2737
2909
  }
2738
- configureSlashCommands(options = {}) {
2739
- this.config.slashCommands = createSlashCommandConfig(options);
2740
- return this;
2910
+ /** Current dev mode state */
2911
+ // prettier-ignore
2912
+ get $devMode() {
2913
+ return this.config.app.devMode;
2741
2914
  }
2742
- configurePrefixCommands(options = {}) {
2743
- this.config.prefixCommands = createPrefixCommandConfig(options);
2744
- return this;
2915
+ // prettier-ignore
2916
+ set $devMode(mode) {
2917
+ this.config.app.devMode = mode;
2745
2918
  }
2746
- configureContextCommands(options = {}) {
2747
- this.config.contextCommands = createContextCommandConfig(options);
2748
- return this;
2919
+ /** Current verbose mode state */
2920
+ // prettier-ignore
2921
+ get $verboseMode() {
2922
+ return this.config.app.verbose;
2749
2923
  }
2750
- async importEventModules(dir, replaceAll) {
2751
- await this.events.importFrom(dir, replaceAll);
2752
- return this;
2924
+ // prettier-ignore
2925
+ set $verboseMode(mode) {
2926
+ this.config.app.verbose = mode;
2753
2927
  }
2754
- async importSlashCommandModules(dir, replaceAll) {
2755
- await this.commands.slash.importFrom(dir, replaceAll);
2756
- return this;
2928
+ /** Returns the options, features, and config of this client. */
2929
+ toJSON() {
2930
+ return { options: this.clientOptions, features: this.features, config: this.config };
2757
2931
  }
2758
- async importPrefixCommandModules(dir, replaceAll) {
2759
- await this.commands.prefix.importFrom(dir, replaceAll);
2760
- return this;
2932
+ /** Makes a clone of this client. */
2933
+ clone() {
2934
+ const { options, features, config } = this.toJSON();
2935
+ return new _Vimcord(options, features, config);
2761
2936
  }
2762
- async importContextCommandModules(dir, replaceAll) {
2763
- await this.commands.context.importFrom(dir, replaceAll);
2937
+ /**
2938
+ * Modifies a client config.
2939
+ * @param type The type of config to modify.
2940
+ * @param options The options to set for the config.
2941
+ */
2942
+ configure(type, options = {}) {
2943
+ this.config[type] = configSetters[type](options, this.config[type]);
2764
2944
  return this;
2765
2945
  }
2766
- async useDatabase(db) {
2767
- this.db = db;
2768
- this.logger.database("Using", db.moduleName);
2769
- return this.db.connect();
2770
- }
2771
- async waitForReady() {
2772
- if (this.isReady()) return this;
2773
- return new Promise((resolve, reject) => {
2774
- const timeout = setTimeout(
2775
- () => reject(new Error(`Client (i${this.clientId}) timed out waiting for ready`)),
2776
- 6e4
2777
- );
2778
- this.once("clientReady", () => {
2779
- clearTimeout(timeout);
2780
- resolve(this);
2781
- });
2782
- });
2783
- }
2946
+ /** Builds the client by importing modules and registering builtin handlers. */
2784
2947
  async build() {
2785
- this.configureApp(this.config.app);
2786
- this.configureStaff(this.config.staff);
2787
- this.configureSlashCommands(this.config.slashCommands);
2788
- this.configurePrefixCommands(this.config.prefixCommands);
2789
- this.configureContextCommands(this.config.contextCommands);
2948
+ this.configure("app", this.config.app);
2949
+ this.configure("staff", this.config.staff);
2950
+ this.configure("slashCommands", this.config.slashCommands);
2951
+ this.configure("prefixCommands", this.config.prefixCommands);
2952
+ this.configure("contextCommands", this.config.contextCommands);
2790
2953
  if (this.features.importModules) {
2791
2954
  const importModules = this.features.importModules;
2792
2955
  await Promise.all([
2793
- importModules.events && this.importEventModules(importModules.events),
2794
- importModules.slashCommands && this.importSlashCommandModules(importModules.slashCommands),
2795
- importModules.prefixCommands && this.importPrefixCommandModules(importModules.prefixCommands),
2796
- importModules.contextCommands && this.importContextCommandModules(importModules.contextCommands)
2956
+ importModules.events && this.importModules("events", importModules.events),
2957
+ importModules.slashCommands && this.importModules("slashCommands", importModules.slashCommands),
2958
+ importModules.prefixCommands && this.importModules("prefixCommands", importModules.prefixCommands),
2959
+ importModules.contextCommands && this.importModules("contextCommands", importModules.contextCommands)
2797
2960
  ]);
2798
2961
  }
2799
2962
  if (this.features.useDefaultSlashCommandHandler) {
2800
- this.events.register(BUILTIN_SlashCommandHandler);
2963
+ this.events.register(slashCommandHandler);
2801
2964
  }
2802
2965
  if (this.features.useDefaultContextCommandHandler) {
2803
- this.events.register(BUILTIN_ContextCommandHandler);
2966
+ this.events.register(contextCommandHandler);
2804
2967
  }
2805
2968
  if (this.features.useDefaultPrefixCommandHandler) {
2806
- this.events.register(BUILTIN_PrefixCommandHandler);
2969
+ this.events.register(prefixCommandHandler);
2807
2970
  }
2808
2971
  return this;
2809
2972
  }
2810
- async start(tokenOrPreHook, preHook) {
2973
+ /**
2974
+ * Imports modules into the client.
2975
+ * @param type The type of modules to import.
2976
+ * @param options The options to import the module with.
2977
+ * @param set Replaces already imported modules with the ones found.
2978
+ */
2979
+ async importModules(type, options, set) {
2980
+ await moduleImporters[type](this, options, set);
2981
+ return this;
2982
+ }
2983
+ /**
2984
+ * Allows Vimcord to handle environment variables using [dotenv](https://www.npmjs.com/package/dotenv).
2985
+ * @param options Options for dotenv
2986
+ * @see https://www.npmjs.com/package/dotenv
2987
+ */
2988
+ useEnv(options) {
2989
+ this.logger.database("Using", "dotenv");
2990
+ (0, import_dotenv.configDotenv)({ quiet: true, ...options });
2991
+ return this;
2992
+ }
2993
+ /**
2994
+ * Connects to a database.
2995
+ * @param db The database manager to use.
2996
+ */
2997
+ async useDatabase(db) {
2998
+ this.db = db;
2999
+ this.logger.database("Using", db.moduleName);
3000
+ return this.db.connect();
3001
+ }
3002
+ /**
3003
+ * Fetches a user from the client, checking the cache first.
3004
+ * @param userId The ID of the user to fetch.
3005
+ */
3006
+ async fetchUser(userId) {
3007
+ const client = await _Vimcord.getReadyInstance(this.clientId);
3008
+ return fetchUser(client, userId);
3009
+ }
3010
+ /**
3011
+ * Fetches a guild from the client, checking the cache first.
3012
+ * @param guildId The ID of the guild to fetch.
3013
+ */
3014
+ async fetchGuild(guildId) {
3015
+ const client = await _Vimcord.getReadyInstance(this.clientId);
3016
+ return fetchGuild(client, guildId);
3017
+ }
3018
+ async start(tokenOrPreHook, callback) {
2811
3019
  if (this.clientStartingPromise) return this.clientStartingPromise;
2812
- const main = async () => {
3020
+ const execute = async () => {
2813
3021
  let token = typeof tokenOrPreHook === "string" ? tokenOrPreHook : void 0;
2814
- token ??= this.config.app.devMode ? process.env.TOKEN_DEV : process.env.TOKEN;
3022
+ token ??= this.$devMode ? process.env.TOKEN_DEV : process.env.TOKEN;
2815
3023
  if (!token) {
2816
3024
  throw new Error(
2817
- `TOKEN Missing: ${this.config.app.devMode ? "devMode is enabled, but TOKEN_DEV is not set" : "TOKEN not set"}`
3025
+ `TOKEN Missing: ${this.$devMode ? "devMode is enabled, but TOKEN_DEV is not set" : "TOKEN not set"}`
2818
3026
  );
2819
3027
  }
2820
3028
  await this.build();
2821
3029
  try {
3030
+ const stopLoader = this.logger.loader("Connecting to Discord...");
3031
+ const loginResult = await import_qznt4.$.async.retry(() => super.login(token), {
3032
+ retries: this.features.maxLoginAttempts ?? 3,
3033
+ delay: 1e3
3034
+ });
3035
+ stopLoader("Connected to Discord ");
3036
+ this.$verboseMode && this.logger.debug("Waiting for ready...");
2822
3037
  if (typeof tokenOrPreHook === "function") {
2823
3038
  await tokenOrPreHook(this);
2824
3039
  } else {
2825
- await preHook?.(this);
3040
+ await callback?.(this);
2826
3041
  }
2827
- const stopLoader = this.logger.loader("Connecting to Discord...");
2828
- const loginResult = await import_qznt4.$.async.retry(() => super.login(token), this.features.loginAttempts ?? 3, 1e3);
2829
- stopLoader("Connected to Discord ");
2830
- this.config.app.verbose && this.logger.debug("\u23F3 Waiting for ready...");
2831
3042
  return loginResult;
2832
3043
  } catch (err) {
2833
3044
  this.logger.error(
2834
- `Failed to log into Discord after ${this.features.loginAttempts} attempt(s))`,
3045
+ `Failed to log into Discord after ${this.features.maxLoginAttempts} attempt(s)`,
2835
3046
  err
2836
3047
  );
2837
3048
  return null;
@@ -2839,29 +3050,25 @@ var Vimcord = class _Vimcord extends import_discord11.Client {
2839
3050
  this.clientStartingPromise = null;
2840
3051
  }
2841
3052
  };
2842
- this.clientStartingPromise = main();
3053
+ this.clientStartingPromise = execute();
2843
3054
  return this.clientStartingPromise;
2844
3055
  }
3056
+ /** Destroys the client and disconnects from Discord. */
2845
3057
  async kill() {
2846
3058
  await super.destroy();
2847
3059
  _Vimcord.instances.delete(this.clientId);
2848
- this.logger.debug("\u{1F6AA} Logged out of Discord");
2849
- }
2850
- /** Shortcut for {@link fetchUser tools.fetchUser} */
2851
- async fetchUser(id) {
2852
- const client = await this.waitForReady();
2853
- return fetchUser(client, id);
2854
- }
2855
- /** Shortcut for {@link fetchGuild tools.fetchGuild} */
2856
- async fetchGuild(id) {
2857
- const client = await this.waitForReady();
2858
- return fetchGuild(client, id);
3060
+ this.logger.debug("Logged out of Discord");
2859
3061
  }
2860
3062
  };
2861
3063
 
2862
- // src/modules/db/mongo/mongo.ts
3064
+ // src/db/mongo/mongo-schema.builder.ts
3065
+ var import_mongoose2 = require("mongoose");
3066
+ var import_node_crypto4 = require("crypto");
3067
+ var import_qznt6 = require("qznt");
3068
+
3069
+ // src/db/mongo/mongo.database.ts
2863
3070
  var import_mongoose = __toESM(require("mongoose"));
2864
- var import_node_events2 = __toESM(require("events"));
3071
+ var import_node_events3 = __toESM(require("events"));
2865
3072
  var import_qznt5 = require("qznt");
2866
3073
  try {
2867
3074
  import("mongoose");
@@ -2870,7 +3077,7 @@ try {
2870
3077
  }
2871
3078
  var MongoDatabase = class _MongoDatabase {
2872
3079
  static instances = /* @__PURE__ */ new Map();
2873
- static emitter = new import_node_events2.default();
3080
+ static emitter = new import_node_events3.default();
2874
3081
  moduleName = "MongoDatabase";
2875
3082
  clientId;
2876
3083
  client;
@@ -2955,10 +3162,9 @@ var MongoDatabase = class _MongoDatabase {
2955
3162
  this.isConnecting = true;
2956
3163
  try {
2957
3164
  const stopLoader = this.client.logger.loader("Connecting to MongoDB...");
2958
- await import_qznt5.$.async.retry(
2959
- () => this.mongoose.connect(connectionUri, { autoIndex: true, ...connectionOptions }),
2960
- maxRetries
2961
- );
3165
+ await import_qznt5.$.async.retry(() => this.mongoose.connect(connectionUri, { autoIndex: true, ...connectionOptions }), {
3166
+ retries: maxRetries
3167
+ });
2962
3168
  _MongoDatabase.emitter.emit("ready", this);
2963
3169
  stopLoader("Connected to MongoDB ");
2964
3170
  } catch (err) {
@@ -2989,10 +3195,7 @@ var MongoDatabase = class _MongoDatabase {
2989
3195
  }
2990
3196
  };
2991
3197
 
2992
- // src/modules/db/mongo/mongoSchema.builder.ts
2993
- var import_mongoose2 = require("mongoose");
2994
- var import_node_crypto4 = require("crypto");
2995
- var import_qznt6 = require("qznt");
3198
+ // src/db/mongo/mongo-schema.builder.ts
2996
3199
  try {
2997
3200
  import("mongoose");
2998
3201
  } catch {
@@ -3078,10 +3281,13 @@ var MongoSchemaBuilder = class _MongoSchemaBuilder {
3078
3281
  * @param maxRetries [default: 3]
3079
3282
  */
3080
3283
  async execute(fn, maxRetries = 3) {
3081
- return await import_qznt6.$.async.retry(async () => {
3082
- const model = await this.getModel();
3083
- return await fn(model);
3084
- }, maxRetries);
3284
+ return await import_qznt6.$.async.retry(
3285
+ async () => {
3286
+ const model = await this.getModel();
3287
+ return await fn(model);
3288
+ },
3289
+ { retries: maxRetries }
3290
+ );
3085
3291
  }
3086
3292
  async startSession(options) {
3087
3293
  return await this.execute(async () => {
@@ -3687,7 +3893,7 @@ var BetterModal = class {
3687
3893
 
3688
3894
  // src/tools/Paginator.ts
3689
3895
  var import_discord14 = require("discord.js");
3690
- var import_node_events3 = __toESM(require("events"));
3896
+ var import_node_events4 = __toESM(require("events"));
3691
3897
  var PaginationType = /* @__PURE__ */ ((PaginationType2) => {
3692
3898
  PaginationType2[PaginationType2["Short"] = 0] = "Short";
3693
3899
  PaginationType2[PaginationType2["ShortJump"] = 1] = "ShortJump";
@@ -3732,7 +3938,7 @@ var Paginator = class {
3732
3938
  config;
3733
3939
  data;
3734
3940
  events;
3735
- eventEmitter = new import_node_events3.default();
3941
+ eventEmitter = new import_node_events4.default();
3736
3942
  constructor(options = {}) {
3737
3943
  this.config = options.config ? createToolsConfig(options.config) : globalToolsConfig;
3738
3944
  this.options = {
@@ -4344,9 +4550,6 @@ async function prompt(handler, options, sendOptions) {
4344
4550
  }
4345
4551
  // Annotate the CommonJS export names for ESM import in node:
4346
4552
  0 && (module.exports = {
4347
- BUILTIN_ContextCommandHandler,
4348
- BUILTIN_PrefixCommandHandler,
4349
- BUILTIN_SlashCommandHandler,
4350
4553
  BaseCommandBuilder,
4351
4554
  BaseCommandManager,
4352
4555
  BetterCollector,
@@ -4359,13 +4562,16 @@ async function prompt(handler, options, sendOptions) {
4359
4562
  CommandType,
4360
4563
  ContextCommandBuilder,
4361
4564
  ContextCommandManager,
4565
+ DEFAULT_MODULE_SUFFIXES,
4362
4566
  DynaSend,
4567
+ ErrorHandler,
4363
4568
  EventBuilder,
4364
4569
  EventManager,
4365
4570
  LOGGER_COLORS,
4366
4571
  LogLevel,
4367
4572
  Logger,
4368
4573
  MissingPermissionReason,
4574
+ ModuleImporter,
4369
4575
  MongoDatabase,
4370
4576
  MongoSchemaBuilder,
4371
4577
  PaginationTimeoutType,
@@ -4385,8 +4591,12 @@ async function prompt(handler, options, sendOptions) {
4385
4591
  VimcordCLI,
4386
4592
  __zero,
4387
4593
  cleanMention,
4594
+ clientLoggerFactory,
4595
+ configSetters,
4596
+ contextCommandHandler,
4388
4597
  createAppConfig,
4389
4598
  createClient,
4599
+ createConfigFactory,
4390
4600
  createContextCommandConfig,
4391
4601
  createMongoPlugin,
4392
4602
  createMongoSchema,
@@ -4395,7 +4605,10 @@ async function prompt(handler, options, sendOptions) {
4395
4605
  createStaffConfig,
4396
4606
  createToolsConfig,
4397
4607
  createVimcordStatusConfig,
4608
+ defineClientOptions,
4398
4609
  defineGlobalToolsConfig,
4610
+ defineVimcordConfig,
4611
+ defineVimcordFeatures,
4399
4612
  dynaSend,
4400
4613
  fetchChannel,
4401
4614
  fetchGuild,
@@ -4403,17 +4616,20 @@ async function prompt(handler, options, sendOptions) {
4403
4616
  fetchMessage,
4404
4617
  fetchRole,
4405
4618
  fetchUser,
4406
- getCallerFileName,
4619
+ getDevMode,
4407
4620
  getFirstMentionId,
4408
4621
  getMessageMention,
4622
+ getPackageJson,
4409
4623
  getProcessDir,
4410
4624
  globalToolsConfig,
4411
4625
  importModulesFromDir,
4412
- initCLI,
4413
4626
  isMentionOrSnowflake,
4414
4627
  logger,
4628
+ moduleImporters,
4629
+ prefixCommandHandler,
4415
4630
  prompt,
4416
4631
  sendCommandErrorEmbed,
4632
+ slashCommandHandler,
4417
4633
  useClient,
4418
4634
  useReadyClient,
4419
4635
  validateCommandPermissions