vimcord 1.0.35 → 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,17 +71,24 @@ __export(index_exports, {
71
71
  VimcordCLI: () => VimcordCLI,
72
72
  __zero: () => __zero,
73
73
  cleanMention: () => cleanMention,
74
+ clientLoggerFactory: () => clientLoggerFactory,
75
+ configSetters: () => configSetters,
76
+ contextCommandHandler: () => contextCommandHandler,
77
+ createAppConfig: () => createAppConfig,
74
78
  createClient: () => createClient,
79
+ createConfigFactory: () => createConfigFactory,
80
+ createContextCommandConfig: () => createContextCommandConfig,
75
81
  createMongoPlugin: () => createMongoPlugin,
76
82
  createMongoSchema: () => createMongoSchema,
83
+ createPrefixCommandConfig: () => createPrefixCommandConfig,
84
+ createSlashCommandConfig: () => createSlashCommandConfig,
85
+ createStaffConfig: () => createStaffConfig,
77
86
  createToolsConfig: () => createToolsConfig,
78
- createVimcordAppConfig: () => createVimcordAppConfig,
79
- createVimcordContextCommandConfig: () => createVimcordContextCommandConfig,
80
- createVimcordPrefixCommandConfig: () => createVimcordPrefixCommandConfig,
81
- createVimcordSlashCommandConfig: () => createVimcordSlashCommandConfig,
82
- createVimcordStaffConfig: () => createVimcordStaffConfig,
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
- globalVimcordToolsConfig: () => globalVimcordToolsConfig,
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
  }
@@ -860,7 +1017,11 @@ var SlashCommandBuilder = class extends BaseCommandBuilder {
860
1017
  if (subCommand) {
861
1018
  const handler = this.routes.get(subCommand.toLowerCase());
862
1019
  if (handler) return await handler(client, interaction);
863
- if (config.onUnknownRouteHandler) return await config.onUnknownRouteHandler(client, interaction);
1020
+ if (config.onUnknownRouteHandler) {
1021
+ return await config.onUnknownRouteHandler(client, interaction);
1022
+ } else {
1023
+ return await interaction.reply({ content: `Unknown subcommand: ${subCommand}`, flags: "Ephemeral" });
1024
+ }
864
1025
  }
865
1026
  return await originalExecute?.(client, interaction);
866
1027
  }
@@ -919,26 +1080,120 @@ var SlashCommandBuilder = class extends BaseCommandBuilder {
919
1080
  }
920
1081
  };
921
1082
 
922
- // src/client.ts
923
- var import_discord11 = require("discord.js");
924
- 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
+ });
925
1104
 
926
- // src/configs/tools.config.ts
927
- var import_lodash5 = __toESM(require("lodash"));
928
- var globalVimcordToolsConfig = {
929
- devMode: false,
930
- embedColor: [],
931
- embedColorDev: [],
932
- timeouts: {
933
- collectorTimeout: 6e4,
934
- collectorIdle: 6e4,
935
- pagination: 6e4,
936
- prompt: 3e4,
937
- modalSubmit: 6e4
938
- },
939
- collector: {
940
- notAParticipantMessage: "You are not allowed to use this.",
941
- userLockMessage: "Please wait until your current action is finished.",
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.",
942
1197
  notAParticipantWarningCooldown: 5e3
943
1198
  },
944
1199
  paginator: {
@@ -960,95 +1215,13 @@ var globalVimcordToolsConfig = {
960
1215
  rejectLabel: "Cancel"
961
1216
  }
962
1217
  };
963
- function defineGlobalToolsConfig(options) {
964
- Object.assign(globalVimcordToolsConfig, import_lodash5.default.merge(globalVimcordToolsConfig, options));
965
- }
966
- function createToolsConfig(options) {
967
- return import_lodash5.default.merge(globalVimcordToolsConfig, options);
968
- }
969
-
970
- // src/configs/app.config.ts
971
- var import_lodash6 = __toESM(require("lodash"));
972
- var defaultConfig = {
973
- devMode: process.argv.includes("--dev"),
974
- name: "Discord Bot",
975
- appVersion: "1.0.0",
976
- verbose: false,
977
- disableBanner: false,
978
- moduleSuffixes: {
979
- slashCommand: "slash",
980
- contextCommand: "ctx",
981
- prefixCommand: "prefix",
982
- event: "event"
983
- }
984
- };
985
- function createVimcordAppConfig(options = {}) {
986
- return import_lodash6.default.merge(defaultConfig, options);
987
- }
988
-
989
- // src/configs/staff.config.ts
990
- var import_lodash7 = __toESM(require("lodash"));
991
- var defaultConfig2 = {
992
- ownerId: null,
993
- superUsers: [],
994
- superUserRoles: [],
995
- bypassers: [],
996
- bypassesGuildAdmin: {
997
- allBotStaff: false,
998
- botOwner: false,
999
- superUsers: false,
1000
- bypassers: false
1001
- },
1002
- guild: {
1003
- id: null,
1004
- inviteUrl: null,
1005
- channels: {}
1006
- }
1007
- };
1008
- function createVimcordStaffConfig(options = {}) {
1009
- return import_lodash7.default.merge(defaultConfig2, options);
1010
- }
1011
-
1012
- // src/configs/slashCommand.config.ts
1013
- var import_lodash8 = __toESM(require("lodash"));
1014
- var defaultConfig3 = {
1015
- logExecution: true
1016
- };
1017
- function createVimcordSlashCommandConfig(options = {}) {
1018
- return import_lodash8.default.merge(defaultConfig3, options);
1019
- }
1020
-
1021
- // src/configs/prefixCommand.config.ts
1022
- var import_lodash9 = __toESM(require("lodash"));
1023
- var defaultConfig4 = {
1024
- enabled: true,
1025
- defaultPrefix: "!",
1026
- allowMentionAsPrefix: true,
1027
- allowCaseInsensitiveCommandNames: true,
1028
- logExecution: true
1029
- };
1030
- function createVimcordPrefixCommandConfig(options = {}) {
1031
- return import_lodash9.default.merge(defaultConfig4, options);
1032
- }
1033
-
1034
- // src/configs/contextCommand.config.ts
1035
- var import_lodash10 = __toESM(require("lodash"));
1036
- var defaultConfig5 = {
1037
- enabled: true,
1038
- logExecution: true
1218
+ var createToolsConfig = createConfigFactory(globalToolsConfig);
1219
+ var defineGlobalToolsConfig = (options) => {
1220
+ Object.assign(globalToolsConfig, createToolsConfig(options, globalToolsConfig));
1039
1221
  };
1040
- function createVimcordContextCommandConfig(options = {}) {
1041
- return import_lodash10.default.merge(defaultConfig5, options);
1042
- }
1043
-
1044
- // src/utils/sendCommandErrorEmbed.ts
1045
- var import_discord6 = require("discord.js");
1046
-
1047
- // src/tools/BetterEmbed.ts
1048
- var import_discord5 = require("discord.js");
1049
1222
 
1050
1223
  // src/tools/dynaSend.ts
1051
- var import_discord4 = require("discord.js");
1224
+ var import_discord5 = require("discord.js");
1052
1225
 
1053
1226
  // src/tools/types.ts
1054
1227
  var SendMethod = /* @__PURE__ */ ((SendMethod2) => {
@@ -1068,7 +1241,7 @@ var DynaSend = class {
1068
1241
  return Array.isArray(value) ? value : [value];
1069
1242
  }
1070
1243
  static isInteractionCallback(obj) {
1071
- return obj instanceof import_discord4.InteractionCallbackResponse;
1244
+ return obj instanceof import_discord5.InteractionCallbackResponse;
1072
1245
  }
1073
1246
  static filterFlags(flags, excludeFlags) {
1074
1247
  if (!flags) return void 0;
@@ -1076,26 +1249,26 @@ var DynaSend = class {
1076
1249
  return flagArray.filter((flag) => !excludeFlags.includes(flag));
1077
1250
  }
1078
1251
  static detectSendMethod(handler) {
1079
- if (handler instanceof import_discord4.BaseInteraction) {
1252
+ if (handler instanceof import_discord5.BaseInteraction) {
1080
1253
  return handler.replied || handler.deferred ? 1 /* EditReply */ : 0 /* Reply */;
1081
1254
  }
1082
- if (handler instanceof import_discord4.BaseChannel) return 3 /* Channel */;
1083
- if (handler instanceof import_discord4.Message) return 4 /* MessageReply */;
1084
- 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 */;
1085
1258
  throw new Error("[DynaSend] Unable to determine send method for handler type");
1086
1259
  }
1087
1260
  static validateSendMethod(handler, method) {
1088
1261
  const interactionMethods = [0 /* Reply */, 1 /* EditReply */, 2 /* FollowUp */];
1089
- if (interactionMethods.includes(method) && !(handler instanceof import_discord4.BaseInteraction)) {
1262
+ if (interactionMethods.includes(method) && !(handler instanceof import_discord5.BaseInteraction)) {
1090
1263
  throw new TypeError(`[DynaSend] SendMethod '${SendMethod[method]}' requires BaseInteraction handler`);
1091
1264
  }
1092
- if (method === 3 /* Channel */ && !(handler instanceof import_discord4.BaseChannel)) {
1265
+ if (method === 3 /* Channel */ && !(handler instanceof import_discord5.BaseChannel)) {
1093
1266
  throw new TypeError(`[DynaSend] SendMethod '${SendMethod[method]}' requires BaseChannel handler`);
1094
1267
  }
1095
- 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)) {
1096
1269
  throw new TypeError(`[DynaSend] SendMethod '${SendMethod[method]}' requires Message handler`);
1097
1270
  }
1098
- 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)) {
1099
1272
  throw new TypeError(`[DynaSend] SendMethod '${SendMethod[method]}' requires User or GuildMember handler`);
1100
1273
  }
1101
1274
  }
@@ -1226,7 +1399,7 @@ async function dynaSend(handler, options) {
1226
1399
 
1227
1400
  // src/tools/BetterEmbed.ts
1228
1401
  var BetterEmbed = class _BetterEmbed {
1229
- embed = new import_discord5.EmbedBuilder();
1402
+ embed = new import_discord6.EmbedBuilder();
1230
1403
  data;
1231
1404
  config;
1232
1405
  /** A powerful wrapper for `EmbedBuilder` that introduces useful features
@@ -1255,7 +1428,7 @@ var BetterEmbed = class _BetterEmbed {
1255
1428
  * - __`$month`__: _M or MM_
1256
1429
  * - __`$day`__: _D or DD_ */
1257
1430
  constructor(data = {}) {
1258
- this.config = data.config || globalVimcordToolsConfig;
1431
+ this.config = data.config ? createToolsConfig(data.config) : globalToolsConfig;
1259
1432
  this.data = {
1260
1433
  context: data.context || null,
1261
1434
  author: data.author || null,
@@ -1303,8 +1476,8 @@ var BetterEmbed = class _BetterEmbed {
1303
1476
  applyContextFormatting(str) {
1304
1477
  if (!this.data.acf) return;
1305
1478
  const user = this.getContextUser();
1306
- const guildMember = user instanceof import_discord5.GuildMember ? user : null;
1307
- 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);
1308
1481
  const client = this.getContextClient();
1309
1482
  const formatString = (str2) => {
1310
1483
  if (!str2 || !str2.includes("$")) return str2;
@@ -1494,24 +1667,25 @@ var BetterEmbed = class _BetterEmbed {
1494
1667
  }
1495
1668
  };
1496
1669
 
1497
- // src/utils/sendCommandErrorEmbed.ts
1670
+ // src/utils/command-error.utils.ts
1671
+ var import_discord7 = require("discord.js");
1498
1672
  async function sendCommandErrorEmbed(client, error, guild, messageOrInteraction) {
1499
1673
  if (!client.features.enableCommandErrorMessage) return null;
1500
1674
  const config = typeof client.features.enableCommandErrorMessage !== "boolean" ? client.features.enableCommandErrorMessage : void 0;
1501
1675
  const buttons = {
1502
- supportServer: new import_discord6.ButtonBuilder({
1676
+ supportServer: new import_discord7.ButtonBuilder({
1503
1677
  url: config?.inviteUrl || client.config.staff.guild.inviteUrl || "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
1504
1678
  // may or may not be a rickroll
1505
1679
  label: config?.inviteButtonLabel || "Support Support",
1506
- style: import_discord6.ButtonStyle.Link
1680
+ style: import_discord7.ButtonStyle.Link
1507
1681
  }),
1508
- details: new import_discord6.ButtonBuilder({
1682
+ details: new import_discord7.ButtonBuilder({
1509
1683
  customId: "btn_details",
1510
1684
  label: config?.detailButtonLabel || "Details",
1511
- style: import_discord6.ButtonStyle.Secondary
1685
+ style: import_discord7.ButtonStyle.Secondary
1512
1686
  })
1513
1687
  };
1514
- const actionRow = new import_discord6.ActionRowBuilder({
1688
+ const actionRow = new import_discord7.ActionRowBuilder({
1515
1689
  components: config?.inviteUrl && guild?.id !== (config.inviteUrl || client.config.staff.guild.id) ? [buttons.supportServer, buttons.details] : [buttons.details]
1516
1690
  });
1517
1691
  const embed_error = config?.embed?.(new BetterEmbed(), error, guild) || new BetterEmbed({
@@ -1519,19 +1693,20 @@ async function sendCommandErrorEmbed(client, error, guild, messageOrInteraction)
1519
1693
  title: "Something went wrong",
1520
1694
  description: "If you keep encountering this error, please report it."
1521
1695
  });
1522
- const msg = await embed_error.send(messageOrInteraction, {
1696
+ const msg = await dynaSend(messageOrInteraction, {
1697
+ embeds: [embed_error],
1523
1698
  components: [actionRow],
1524
1699
  flags: config?.ephemeral ? "Ephemeral" : void 0,
1525
1700
  deleteAfter: config?.deleteAfter
1526
1701
  });
1527
1702
  if (!msg) return null;
1528
1703
  const collector = msg.createMessageComponentCollector({
1529
- componentType: import_discord6.ComponentType.Button,
1704
+ componentType: import_discord7.ComponentType.Button,
1530
1705
  idle: config?.detailButtonIdleTimeout ?? 3e4,
1531
1706
  filter: (i) => i.customId === "btn_details"
1532
1707
  });
1533
1708
  collector.on("collect", (i) => {
1534
- const attachment = new import_discord6.AttachmentBuilder(Buffer.from(`${error.message}
1709
+ const attachment = new import_discord7.AttachmentBuilder(Buffer.from(`${error.message}
1535
1710
 
1536
1711
  ${error.stack}`), {
1537
1712
  name: "error.txt"
@@ -1540,537 +1715,335 @@ ${error.stack}`), {
1540
1715
  });
1541
1716
  collector.on("end", () => {
1542
1717
  buttons.details.setDisabled(true);
1543
- embed_error.send(messageOrInteraction, {
1544
- 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,
1545
1721
  components: [actionRow]
1546
1722
  });
1547
1723
  });
1548
1724
  return msg;
1549
1725
  }
1550
1726
 
1551
- // src/modules/builtins/builtin.slashCommandHandler.ts
1552
- var BUILTIN_SlashCommandHandler = new EventBuilder({
1553
- event: "interactionCreate",
1554
- name: "SlashCommandHandler",
1555
- async execute(client, interaction) {
1556
- if (!interaction.isChatInputCommand()) return;
1557
- const command = client.commands.slash.get(interaction.commandName);
1558
- if (!command) {
1559
- const content = `**/\`${interaction.commandName}\`** is not a registered command.`;
1560
- if (interaction.replied || interaction.deferred) {
1561
- return interaction.followUp({ content, flags: "Ephemeral" });
1562
- }
1563
- return interaction.reply({ content, flags: "Ephemeral" });
1564
- }
1565
- try {
1566
- return await command.run(client, client, interaction);
1567
- } catch (err) {
1568
- await sendCommandErrorEmbed(client, err, interaction.guild, interaction);
1569
- throw err;
1570
- }
1727
+ // src/client/error-handler.ts
1728
+ var ErrorHandler = class {
1729
+ client;
1730
+ constructor(client) {
1731
+ this.client = client;
1571
1732
  }
1572
- });
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;
1737
+ }
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
+ };
1573
1751
 
1574
- // src/modules/builtins/builtin.prefixCommandHandler.ts
1575
- var import_discord7 = require("discord.js");
1576
- var BUILTIN_PrefixCommandHandler = new EventBuilder({
1577
- event: "messageCreate",
1578
- name: "PrefixCommandHandler",
1579
- async execute(client, message) {
1580
- if (message.author.bot || !message.guild) return;
1581
- const config = client.config.prefixCommands;
1582
- let activePrefix = config.defaultPrefix;
1583
- if (config.guildPrefixResolver) {
1584
- try {
1585
- const customPrefix = await config.guildPrefixResolver(client, message.guild.id);
1586
- if (customPrefix) activePrefix = customPrefix;
1587
- } catch (err) {
1588
- client.logger.error(`Error in guildPrefixResolver for guild ${message.guild.id}:`, err);
1589
- }
1590
- }
1591
- let prefixUsed;
1592
- if (message.content.startsWith(activePrefix)) {
1593
- prefixUsed = activePrefix;
1594
- } else if (config.allowMentionAsPrefix) {
1595
- const mention = (0, import_discord7.userMention)(client.user.id);
1596
- if (message.content.startsWith(mention)) {
1597
- prefixUsed = message.content.startsWith(`${mention} `) ? `${mention} ` : mention;
1598
- }
1599
- }
1600
- if (!prefixUsed) return;
1601
- const contentWithoutPrefix = message.content.slice(prefixUsed.length).trim();
1602
- const args = contentWithoutPrefix.split(/\s+/);
1603
- const trigger = args.shift();
1604
- if (!trigger) return;
1605
- const command = client.commands.prefix.get(trigger);
1606
- if (!command) return;
1607
- message.content = args.join(" ");
1608
- try {
1609
- return await command.run(client, client, message);
1610
- } catch (err) {
1611
- await sendCommandErrorEmbed(client, err, message.guild, message);
1612
- throw err;
1613
- }
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
+ );
1614
1818
  }
1615
1819
  });
1616
1820
 
1617
- // src/modules/builtins/builtin.contextCommandHandler.ts
1618
- var BUILTIN_ContextCommandHandler = new EventBuilder({
1619
- event: "interactionCreate",
1620
- name: "ContextCommandHandler",
1621
- async execute(client, interaction) {
1622
- if (!interaction.isContextMenuCommand()) return;
1623
- const command = client.commands.context.get(interaction.commandName);
1624
- if (!command) {
1625
- const content = `**${interaction.commandName}** is not a registered context command.`;
1626
- if (interaction.replied || interaction.deferred) {
1627
- return interaction.followUp({ content, flags: "Ephemeral" });
1628
- }
1629
- return interaction.reply({ content, flags: "Ephemeral" });
1630
- }
1631
- try {
1632
- return await command.run(client, client, interaction);
1633
- } catch (err) {
1634
- await sendCommandErrorEmbed(client, err, interaction.guild, interaction);
1635
- throw err;
1636
- }
1637
- }
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
1839
+ };
1840
+ var createAppConfig = createConfigFactory(defaultConfig, (config) => {
1841
+ if (!config.name) throw new Error("App name is required");
1842
+ defineGlobalToolsConfig({ devMode: config.devMode });
1638
1843
  });
1639
1844
 
1640
- // src/tools/Logger.ts
1641
- var import_chalk = __toESM(require("chalk"));
1642
- var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
1643
- LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
1644
- LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
1645
- LogLevel2[LogLevel2["SUCCESS"] = 2] = "SUCCESS";
1646
- LogLevel2[LogLevel2["WARN"] = 3] = "WARN";
1647
- LogLevel2[LogLevel2["ERROR"] = 4] = "ERROR";
1648
- return LogLevel2;
1649
- })(LogLevel || {});
1650
- var LOGGER_COLORS = {
1651
- primary: "#5865F2",
1652
- success: "#57F287",
1653
- warn: "#FEE75C",
1654
- danger: "#ED4245",
1655
- muted: "#747F8D",
1656
- text: "#FFFFFF"
1845
+ // src/configs/contextCommand.config.ts
1846
+ var defaultConfig2 = {
1847
+ enabled: true,
1848
+ logExecution: true
1657
1849
  };
1658
- var Logger = class {
1659
- logPrefixEmoji;
1660
- logPrefix;
1661
- minLevel;
1662
- showTimestamp;
1663
- colorScheme;
1664
- constructor(options) {
1665
- const { prefixEmoji = null, prefix = null, minLevel = 0 /* DEBUG */, showTimestamp = true } = options || {};
1666
- this.logPrefixEmoji = prefixEmoji;
1667
- this.logPrefix = prefix;
1668
- this.minLevel = minLevel;
1669
- this.showTimestamp = showTimestamp;
1670
- this.colorScheme = {
1671
- ...LOGGER_COLORS,
1672
- ...options?.colors
1673
- };
1674
- }
1675
- formatTimestamp() {
1676
- if (!this.showTimestamp) return "";
1677
- const now = /* @__PURE__ */ new Date();
1678
- const time = now.toLocaleTimeString("en-US", {
1679
- hour12: false,
1680
- hour: "2-digit",
1681
- minute: "2-digit",
1682
- second: "2-digit"
1683
- });
1684
- return import_chalk.default.hex(this.colorScheme.muted)(`[${time}]`);
1685
- }
1686
- formatPrefix() {
1687
- if (!this.logPrefix) return "";
1688
- return import_chalk.default.bold.hex(this.colorScheme.primary)(
1689
- `${this.logPrefixEmoji ? `${this.logPrefixEmoji} ` : ""}${this.logPrefix}`
1690
- );
1691
- }
1692
- shouldLog(level) {
1693
- return level >= this.minLevel;
1694
- }
1695
- get prefixEmoji() {
1696
- return this.logPrefixEmoji;
1697
- }
1698
- get prefix() {
1699
- return this.logPrefix;
1700
- }
1701
- get colors() {
1702
- return this.colorScheme;
1703
- }
1704
- extend(extras) {
1705
- for (const [key, fn] of Object.entries(extras)) {
1706
- if (typeof fn === "function") {
1707
- this[key] = function(...args) {
1708
- return fn.call(this, ...args);
1709
- };
1710
- }
1711
- }
1712
- return this;
1713
- }
1714
- setPrefix(prefix) {
1715
- this.logPrefix = prefix;
1716
- return this;
1717
- }
1718
- setPrefixEmoji(prefixEmoji) {
1719
- this.logPrefixEmoji = prefixEmoji;
1720
- return this;
1721
- }
1722
- setMinLevel(minLevel) {
1723
- this.minLevel = minLevel;
1724
- return this;
1725
- }
1726
- setShowTimestamp(show) {
1727
- this.showTimestamp = show;
1728
- return this;
1729
- }
1730
- setColors(colors) {
1731
- this.colorScheme = {
1732
- ...LOGGER_COLORS,
1733
- ...colors
1734
- };
1735
- return this;
1736
- }
1737
- log(message, ...args) {
1738
- console.log(this.formatTimestamp(), this.formatPrefix(), message, ...args);
1739
- }
1740
- debug(message, ...args) {
1741
- if (!this.shouldLog(0 /* DEBUG */)) return;
1742
- console.log(
1743
- this.formatTimestamp(),
1744
- this.formatPrefix(),
1745
- import_chalk.default.hex(this.colorScheme.muted)("DEBUG"),
1746
- import_chalk.default.dim(message),
1747
- ...args
1748
- );
1749
- }
1750
- info(message, ...args) {
1751
- if (!this.shouldLog(1 /* INFO */)) return;
1752
- console.log(this.formatTimestamp(), this.formatPrefix(), import_chalk.default.hex("#87CEEB")("INFO"), message, ...args);
1753
- }
1754
- success(message, ...args) {
1755
- if (!this.shouldLog(2 /* SUCCESS */)) return;
1756
- console.log(
1757
- this.formatTimestamp(),
1758
- this.formatPrefix(),
1759
- import_chalk.default.bold.hex(this.colorScheme.success)("\u2713 SUCCESS"),
1760
- import_chalk.default.hex(this.colorScheme.success)(message),
1761
- ...args
1762
- );
1763
- }
1764
- warn(message, ...args) {
1765
- if (!this.shouldLog(3 /* WARN */)) return;
1766
- console.warn(
1767
- this.formatTimestamp(),
1768
- this.formatPrefix(),
1769
- import_chalk.default.bold.hex(this.colorScheme.warn)("\u26A0 WARN"),
1770
- import_chalk.default.hex(this.colorScheme.warn)(message),
1771
- ...args
1772
- );
1773
- }
1774
- error(message, error, ...args) {
1775
- if (!this.shouldLog(4 /* ERROR */)) return;
1776
- console.error(
1777
- this.formatTimestamp(),
1778
- this.formatPrefix(),
1779
- import_chalk.default.bold.hex(this.colorScheme.danger)("\u2715 ERROR"),
1780
- import_chalk.default.hex(this.colorScheme.danger)(message),
1781
- ...args
1782
- );
1783
- if (error && error.stack) {
1784
- console.error(import_chalk.default.dim(error.stack));
1785
- }
1786
- }
1787
- loader(message) {
1788
- const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
1789
- let i = 0;
1790
- const interval = setInterval(() => {
1791
- process.stdout.write(
1792
- `\r${this.formatTimestamp()} ${this.formatPrefix()} ${import_chalk.default.hex(this.colorScheme.warn)(frames[i])} ${message}`
1793
- );
1794
- i = (i + 1) % frames.length;
1795
- }, 100);
1796
- return (newMessage) => {
1797
- clearInterval(interval);
1798
- process.stdout.write(
1799
- `\r${this.formatTimestamp()} ${this.formatPrefix()} ${import_chalk.default.hex(this.colorScheme.success)("\u2713")} ${newMessage || message}
1800
- `
1801
- );
1802
- };
1803
- }
1804
- table(title, data) {
1805
- console.log(this.formatTimestamp(), this.formatPrefix(), import_chalk.default.bold(title));
1806
- Object.entries(data).forEach(([key, value]) => {
1807
- const formattedKey = import_chalk.default.hex(this.colorScheme.warn)(` ${key}`);
1808
- const formattedValue = import_chalk.default.hex(this.colorScheme.muted)(value);
1809
- console.log(`${formattedKey.padEnd(25)} ${formattedValue}`);
1810
- });
1811
- }
1812
- section(title) {
1813
- const line = "\u2500".repeat(Math.max(30, title.length + 4));
1814
- console.log(import_chalk.default.hex(this.colorScheme.muted)(`
1815
- \u250C\u2500${line}\u2500\u2510`));
1816
- console.log(
1817
- 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")
1818
- );
1819
- console.log(import_chalk.default.hex(this.colorScheme.muted)(`\u2514\u2500${line}\u2500\u2518`));
1820
- }
1850
+ var createContextCommandConfig = createConfigFactory(defaultConfig2);
1851
+
1852
+ // src/configs/prefixCommand.config.ts
1853
+ var defaultConfig3 = {
1854
+ enabled: true,
1855
+ defaultPrefix: "!",
1856
+ allowMentionAsPrefix: true,
1857
+ allowCaseInsensitiveCommandNames: true,
1858
+ logExecution: true
1821
1859
  };
1822
- var logger = new Logger();
1860
+ var createPrefixCommandConfig = createConfigFactory(defaultConfig3);
1823
1861
 
1824
- // src/tools/utils.ts
1825
- function __zero(str) {
1826
- return str?.length ? str : "0";
1827
- }
1828
- function isMentionOrSnowflake(str) {
1829
- return str ? str.match(/<@[#&]?[\d]{6,}>/) || str.match(/\d{6,}/) ? true : false : false;
1830
- }
1831
- function cleanMention(str) {
1832
- return str ? str.replaceAll(/[<@#&>]/g, "").trim() : void 0;
1833
- }
1834
- async function getMessageMention(message, content, type, index = 0, idOnly) {
1835
- const args = content?.split(" ");
1836
- const arg = isMentionOrSnowflake(args?.[index]) ? cleanMention(args?.[index]) : void 0;
1837
- switch (type) {
1838
- case "user":
1839
- const userMention2 = message.mentions.users.at(index) || null;
1840
- if (!userMention2 && arg) {
1841
- return idOnly ? arg : await fetchUser(message.client, arg);
1842
- } else {
1843
- return idOnly ? userMention2?.id || null : userMention2;
1844
- }
1845
- case "member":
1846
- if (!message.guild) return null;
1847
- const member = await fetchMember(message.guild, message.mentions.users.at(index)?.id ?? arg);
1848
- return idOnly ? member?.id || null : member;
1849
- case "channel":
1850
- const channelMention = message.mentions.channels.at(index) || null;
1851
- if (!channelMention && arg) {
1852
- return idOnly ? arg : message.guild ? await fetchChannel(message.guild, arg) : message.client.channels.cache.get(__zero(arg)) ?? message.client.channels.fetch(__zero(arg));
1853
- } else {
1854
- return idOnly ? channelMention?.id || null : channelMention;
1855
- }
1856
- case "role":
1857
- const roleMention = message.mentions.roles.at(index) || null;
1858
- if (!roleMention && arg) {
1859
- return idOnly ? arg : message.guild ? await fetchRole(message.guild, arg) : null;
1860
- } else {
1861
- return idOnly ? roleMention?.id || null : roleMention;
1862
- }
1863
- default:
1864
- return null;
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: {}
1865
1884
  }
1866
- }
1867
- function getFirstMentionId(options) {
1868
- let mentionId = "";
1869
- if (options.message) {
1870
- switch (options.type) {
1871
- case "user":
1872
- mentionId = options.message.mentions.users.first()?.id || "";
1873
- break;
1874
- case "channel":
1875
- mentionId = options.message.mentions.channels.first()?.id || "";
1876
- break;
1877
- case "role":
1878
- mentionId = options.message.mentions.roles.first()?.id || "";
1879
- break;
1880
- }
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);
1881
1926
  }
1882
- const firstArg = options.content?.split(" ")[0] || "";
1883
- return mentionId || isMentionOrSnowflake(firstArg) ? cleanMention(firstArg) : "";
1884
- }
1885
- async function fetchUser(client, userId) {
1886
- if (!userId) return null;
1887
- return client.users.cache.get(__zero(userId)) || await client.users.fetch(__zero(userId)).catch(() => null);
1888
- }
1889
- async function fetchGuild(client, guildId) {
1890
- if (!guildId) return null;
1891
- return client.guilds.cache.get(__zero(guildId)) || await client.guilds.fetch(__zero(guildId)).catch(() => null);
1892
- }
1893
- async function fetchMember(guild, memberId) {
1894
- if (!memberId) return null;
1895
- return guild.members.cache.get(__zero(memberId)) || await guild.members.fetch(__zero(memberId)).catch(() => null);
1896
- }
1897
- async function fetchChannel(guild, channelId, type) {
1898
- if (!channelId) return null;
1899
- const channel = guild.channels.cache.get(__zero(channelId)) || await guild.channels.fetch(__zero(channelId)).catch(() => null);
1900
- if (type && channel?.type !== type) return null;
1901
- return channel;
1927
+ };
1928
+ function defineClientOptions(options) {
1929
+ return options;
1902
1930
  }
1903
- async function fetchRole(guild, roleId) {
1904
- if (!roleId) return null;
1905
- return guild.roles.cache.get(__zero(roleId)) || await guild.roles.fetch(__zero(roleId)).catch(() => null) || null;
1931
+ function defineVimcordFeatures(features) {
1932
+ return features;
1906
1933
  }
1907
- async function fetchMessage(channel, messageId) {
1908
- if (!messageId) return null;
1909
- 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
+ };
1910
1942
  }
1943
+ var useClient = Vimcord.getInstance;
1944
+ var useReadyClient = Vimcord.getReadyInstance;
1945
+ var createClient = Vimcord.create;
1911
1946
 
1912
- // src/types/status.ts
1947
+ // src/modules/command.manager.ts
1913
1948
  var import_discord8 = require("discord.js");
1914
- var import_lodash11 = __toESM(require("lodash"));
1915
- var StatusType = /* @__PURE__ */ ((StatusType2) => {
1916
- StatusType2["DND"] = "dnd";
1917
- StatusType2["Idle"] = "idle";
1918
- StatusType2["Online"] = "online";
1919
- StatusType2["Invisible"] = "invisible";
1920
- return StatusType2;
1921
- })(StatusType || {});
1922
- var defaultPresence = {
1923
- production: {
1924
- interval: 6e4,
1925
- randomize: false,
1926
- activity: [
1927
- { status: "online" /* Online */, type: import_discord8.ActivityType.Custom, name: "Need help? Use /help or !help" },
1928
- { status: "online" /* Online */, type: import_discord8.ActivityType.Custom, name: "Join our community!" },
1929
- { status: "online" /* Online */, type: import_discord8.ActivityType.Watching, name: "\u2728 $GUILD_COUNT servers" }
1930
- ]
1931
- },
1932
- development: {
1933
- activity: { status: "dnd" /* DND */, type: import_discord8.ActivityType.Custom, name: "In development!" }
1934
- }
1935
- };
1936
- function createVimcordStatusConfig(options = {}) {
1937
- return import_lodash11.default.merge(defaultPresence, options);
1938
- }
1939
1949
 
1940
- // src/modules/status.manager.ts
1941
- var import_node_events = __toESM(require("events"));
1942
- var import_qznt2 = require("qznt");
1943
- var StatusManager = class {
1944
- client;
1945
- logger;
1946
- emitter = new import_node_events.default();
1947
- lastActivity = null;
1948
- lastActivityIndex = 0;
1949
- task = null;
1950
- constructor(client) {
1951
- this.client = client;
1952
- this.logger = new Logger({ prefixEmoji: "\u{1F4AC}", prefix: `StatusManager (i${this.client.clientId})` });
1953
- this.emitter.on("changed", (activity) => {
1954
- if (this.client.config.app.verbose) {
1955
- this.logger.debug(`Status changed to '${activity.name}'`);
1956
- }
1957
- });
1958
- this.emitter.on("cleared", () => {
1959
- if (this.client.config.app.verbose) {
1960
- this.logger.debug("Status cleared");
1961
- }
1962
- });
1963
- }
1964
- clearData() {
1965
- this.task?.stop();
1966
- this.task = null;
1967
- this.lastActivity = null;
1968
- this.lastActivityIndex = 0;
1969
- return this;
1970
- }
1971
- async getReadyClient() {
1972
- const client = await this.client.waitForReady();
1973
- if (!client.user) throw new Error("Cannot manage the client's activity when its user is not hydrated");
1974
- return client;
1975
- }
1976
- async formatActivityName(name) {
1977
- 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(
1978
- "$INVITE",
1979
- this.client.config.staff.guild.inviteUrl ? this.client.config.staff.guild.inviteUrl : "<STAFF_INVITE_URL_NOT_SET>"
1980
- );
1981
- if (name.includes("$STAFF_GUILD_MEMBER_COUNT")) {
1982
- await fetchGuild(this.client, this.client.config.staff.guild.id).then((guild) => {
1983
- if (!guild) return name = name.replace("$STAFF_GUILD_MEMBER_COUNT", "<STAFF_GUILD_NOT_FOUND>");
1984
- name = name.replace("$STAFF_GUILD_MEMBER_COUNT", import_qznt2.$.format.number(guild.members.cache.size));
1985
- }).catch((err) => this.logger.error("Failed to fetch the staff guild", err));
1986
- }
1987
- return name;
1988
- }
1989
- async setActivity(activity) {
1990
- const client = await this.getReadyClient();
1991
- activity.name = await this.formatActivityName(activity.name);
1992
- client.user.setStatus(activity.status);
1993
- client.user.setActivity({ name: activity.name, type: activity.type, url: activity.streamUrl });
1994
- this.emitter.emit("changed", activity);
1995
- }
1996
- async statusRotationTask(clientStatus) {
1997
- let activity;
1998
- if (clientStatus.randomize && Array.isArray(clientStatus.activity)) {
1999
- activity = import_qznt2.$.rnd.choice(clientStatus.activity, { not: this.lastActivity });
2000
- this.lastActivity = activity;
2001
- } else {
2002
- const activityIndex = (this.lastActivityIndex + 1) % clientStatus.activity.length;
2003
- this.lastActivityIndex = activityIndex;
2004
- activity = clientStatus.activity[activityIndex];
2005
- }
2006
- await this.setActivity(activity);
2007
- this.emitter.emit("rotation", activity);
2008
- }
2009
- async scheduleStatusRotation(clientStatus) {
2010
- if (!clientStatus.interval) throw new Error("Cannot create client activity interval without interval time");
2011
- this.task?.stop();
2012
- this.task = null;
2013
- this.task = new import_qznt2.$.Loop(() => this.statusRotationTask(clientStatus), import_qznt2.$.math.ms(clientStatus.interval), true);
2014
- 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 [];
2015
1965
  }
2016
- start() {
2017
- if (this.task) {
2018
- this.task.start();
2019
- this.emitter.emit("started", this.task);
2020
- }
2021
- 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}'`);
2022
1984
  }
2023
- pause() {
2024
- if (this.task) {
2025
- this.task.stop();
2026
- this.emitter.emit("paused", this.task);
2027
- }
2028
- 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;
2029
1993
  }
2030
- async set(status) {
2031
- const statusConfig = createVimcordStatusConfig(status);
2032
- let clientStatus;
2033
- if (this.client.config.app.devMode) {
2034
- clientStatus = statusConfig.development;
2035
- } else {
2036
- clientStatus = statusConfig.production;
2037
- }
2038
- if (!clientStatus.interval) {
2039
- await this.setActivity(Array.isArray(clientStatus.activity) ? clientStatus.activity[0] : clientStatus.activity);
2040
- } else {
2041
- 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));
2042
2002
  }
2043
- return this;
2044
- }
2045
- async destroy() {
2046
- if (this.task) {
2047
- this.task.stop();
2048
- this.task = null;
2049
- this.emitter.emit("destroyed");
2050
- await this.clear();
2003
+ for (const module2 of modules) {
2004
+ const name = this.getName(module2);
2005
+ this.items.set(name, module2);
2051
2006
  }
2052
- return this;
2053
- }
2054
- async clear() {
2055
- const client = await this.getReadyClient();
2056
- this.clearData();
2057
- client.user.setActivity({ name: "" });
2058
- this.emitter.emit("cleared");
2059
- return this;
2007
+ this.client.logger.moduleLoaded(this.itemName, modules.length);
2008
+ return this.items;
2060
2009
  }
2061
2010
  };
2062
2011
 
2063
2012
  // src/modules/command.manager.ts
2064
- var import_discord9 = require("discord.js");
2065
- var BaseCommandManager = class {
2013
+ var BaseCommandManager = class extends ModuleImporter {
2066
2014
  type;
2067
- client;
2068
- commands = /* @__PURE__ */ new Map();
2069
- moduleSuffix;
2070
- constructor(client, type, moduleSuffix) {
2015
+ items = /* @__PURE__ */ new Map();
2016
+ itemSuffix;
2017
+ constructor(client, type, itemSuffix) {
2018
+ super(client);
2071
2019
  this.type = type;
2072
- this.client = client;
2073
- 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;
2074
2047
  }
2075
2048
  /**
2076
2049
  * Gets a command by name.
@@ -2079,7 +2052,7 @@ var BaseCommandManager = class {
2079
2052
  if (this.type === 1 /* Prefix */) {
2080
2053
  const config = this.client.config.prefixCommands;
2081
2054
  const search = config.allowCaseInsensitiveCommandNames ? name.toLowerCase() : name;
2082
- return Array.from(this.commands.values()).find((cmd) => {
2055
+ return Array.from(this.items.values()).find((cmd) => {
2083
2056
  const commandName = "builder" in cmd ? cmd.builder.name : cmd.options.name;
2084
2057
  const trigger = config.allowCaseInsensitiveCommandNames ? commandName.toLowerCase() : commandName;
2085
2058
  if (trigger === search) return true;
@@ -2090,7 +2063,7 @@ var BaseCommandManager = class {
2090
2063
  }
2091
2064
  });
2092
2065
  } else {
2093
- return this.commands.get(name);
2066
+ return this.items.get(name);
2094
2067
  }
2095
2068
  }
2096
2069
  /**
@@ -2099,7 +2072,7 @@ var BaseCommandManager = class {
2099
2072
  getAll(options = {}) {
2100
2073
  const matchedCommands = /* @__PURE__ */ new Map();
2101
2074
  const isDev = this.client.config.app.devMode;
2102
- for (const cmd of this.commands.values()) {
2075
+ for (const cmd of this.items.values()) {
2103
2076
  const commandName = "builder" in cmd ? cmd.builder.name : cmd.options.name;
2104
2077
  if (options.names || options.fuzzyNames) {
2105
2078
  const nameMatched = options.names?.includes(commandName) || options.fuzzyNames?.some((fuzzy) => commandName.includes(fuzzy));
@@ -2126,7 +2099,7 @@ var BaseCommandManager = class {
2126
2099
  */
2127
2100
  sortByCategory() {
2128
2101
  const categories = /* @__PURE__ */ new Map();
2129
- for (const cmd of this.commands.values()) {
2102
+ for (const cmd of this.items.values()) {
2130
2103
  const metadata = cmd.options.metadata;
2131
2104
  if (!metadata?.category) continue;
2132
2105
  let entry = categories.get(metadata.category);
@@ -2149,52 +2122,20 @@ var BaseCommandManager = class {
2149
2122
  return cat;
2150
2123
  });
2151
2124
  }
2152
- /**
2153
- * Imports command modules from a directory.
2154
- * @param dir Path of one or more folders.
2155
- * @param set Replaces imported command modules with the ones found.
2156
- */
2157
- async importFrom(dir, set = false) {
2158
- if (set) this.commands.clear();
2159
- const dirs = Array.isArray(dir) ? dir : [dir];
2160
- const modules = [];
2161
- for (const _dir of dirs) {
2162
- const results = await importModulesFromDir(_dir, this.moduleSuffix);
2163
- modules.push(...results.map(({ module: module2 }) => module2.default));
2164
- }
2165
- for (const module2 of modules) {
2166
- const commandName = "builder" in module2 ? module2.builder.name : module2.options.name;
2167
- this.commands.set(commandName, module2);
2168
- }
2169
- let moduleType;
2170
- switch (this.type) {
2171
- case 0 /* Slash */:
2172
- moduleType = "Slash Commands";
2173
- break;
2174
- case 2 /* Context */:
2175
- moduleType = "Context Commands";
2176
- break;
2177
- case 1 /* Prefix */:
2178
- moduleType = "Prefix Commands";
2179
- break;
2180
- }
2181
- this.client.logger.moduleLoaded(moduleType, modules.length);
2182
- return this.commands;
2183
- }
2184
2125
  };
2185
2126
  var SlashCommandManager = class extends BaseCommandManager {
2186
2127
  constructor(client) {
2187
- super(client, 0 /* Slash */, client.config.app.moduleSuffixes.slashCommand);
2128
+ super(client, 0 /* Slash */);
2188
2129
  }
2189
2130
  };
2190
2131
  var ContextCommandManager = class extends BaseCommandManager {
2191
2132
  constructor(client) {
2192
- super(client, 2 /* Context */, client.config.app.moduleSuffixes.contextCommand);
2133
+ super(client, 2 /* Context */);
2193
2134
  }
2194
2135
  };
2195
2136
  var PrefixCommandManager = class extends BaseCommandManager {
2196
2137
  constructor(client) {
2197
- super(client, 1 /* Prefix */, client.config.app.moduleSuffixes.prefixCommand);
2138
+ super(client, 1 /* Prefix */);
2198
2139
  }
2199
2140
  };
2200
2141
  var CommandManager = class {
@@ -2212,7 +2153,7 @@ var CommandManager = class {
2212
2153
  return [...this.slash.getAll(options), ...this.context.getAll(options)];
2213
2154
  }
2214
2155
  async registerGlobal(options = {}) {
2215
- const client = await this.client.waitForReady();
2156
+ const client = await Vimcord.getReadyInstance(this.client.clientId);
2216
2157
  if (!client.rest) {
2217
2158
  console.error(`[CommandManager] \u2716 Failed to register app commands globally: REST is not initialized`);
2218
2159
  return;
@@ -2222,29 +2163,34 @@ var CommandManager = class {
2222
2163
  console.log("[CommandManager] No commands to register globally");
2223
2164
  return;
2224
2165
  }
2225
- console.log(`[CommandManager] Registering (${commands.length}) commands globally...`);
2166
+ console.log(
2167
+ `[CommandManager] Registering (${commands.length}) ${commands.length === 1 ? "command" : "commands"} globally...`
2168
+ );
2226
2169
  try {
2227
- await client.rest.put(import_discord9.Routes.applicationCommands(client.user.id), { body: commands });
2228
- console.log(`[CommandManager] \u2714 Registered app commands globally`);
2170
+ await client.rest.put(import_discord8.Routes.applicationCommands(client.user.id), { body: commands });
2171
+ console.log(`[CommandManager] \u2714 Registered app ${commands.length === 1 ? "command" : "commands"} globally`);
2229
2172
  } catch (err) {
2230
- console.error(`[CommandManager] \u2716 Failed to register app commands globally`, err);
2173
+ console.error(
2174
+ `[CommandManager] \u2716 Failed to register app ${commands.length === 1 ? "command" : "commands"} globally`,
2175
+ err
2176
+ );
2231
2177
  }
2232
2178
  }
2233
2179
  async unregisterGlobal() {
2234
- const client = await this.client.waitForReady();
2180
+ const client = await Vimcord.getReadyInstance(this.client.clientId);
2235
2181
  if (!client.rest) {
2236
2182
  console.error(`[CommandManager] \u2716 Failed to remove app commands globally: REST is not initialized`);
2237
2183
  return;
2238
2184
  }
2239
2185
  try {
2240
- 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: [] });
2241
2187
  console.log(`[CommandManager] \u2714 Removed app commands globally`);
2242
2188
  } catch (err) {
2243
2189
  console.error(`[CommandManager] \u2716 Failed to remove app commands globally`, err);
2244
2190
  }
2245
2191
  }
2246
2192
  async registerGuild(options = {}) {
2247
- const client = await this.client.waitForReady();
2193
+ const client = await Vimcord.getReadyInstance(this.client.clientId);
2248
2194
  if (!client.rest) {
2249
2195
  console.error(`[CommandManager] \u2716 Failed to register app commands by guild: REST is not initialized`);
2250
2196
  return;
@@ -2255,21 +2201,28 @@ var CommandManager = class {
2255
2201
  return;
2256
2202
  }
2257
2203
  const guildIds = options.guilds || client.guilds.cache.map((g) => g.id);
2258
- console.log(`[CommandManager] Registering (${commands.length}) commands for ${guildIds.length} guilds...`);
2204
+ console.log(
2205
+ `[CommandManager] Registering (${commands.length}) ${commands.length === 1 ? "command" : "commands"} for ${guildIds.length} guilds...`
2206
+ );
2259
2207
  await Promise.all(
2260
2208
  guildIds.map(
2261
- (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(() => {
2262
2210
  const gName = client.guilds.cache.get(guildId)?.name || "n/a";
2263
- console.log(`[CommandManager] \u2714 Set app commands in guild: ${guildId} (${gName})`);
2211
+ console.log(
2212
+ `[CommandManager] \u2714 Set app ${commands.length === 1 ? "command" : "commands"} in guild: ${guildId} (${gName})`
2213
+ );
2264
2214
  }).catch((err) => {
2265
2215
  const gName = client.guilds.cache.get(guildId)?.name || "n/a";
2266
- console.log(`[CommandManager] \u2716 Failed to set app commands in guild: ${guildId} (${gName})`, err);
2216
+ console.log(
2217
+ `[CommandManager] \u2716 Failed to set app ${commands.length === 1 ? "command" : "commands"} in guild: ${guildId} (${gName})`,
2218
+ err
2219
+ );
2267
2220
  })
2268
2221
  )
2269
2222
  );
2270
2223
  }
2271
2224
  async unregisterGuild(options = {}) {
2272
- const client = await this.client.waitForReady();
2225
+ const client = await Vimcord.getReadyInstance(this.client.clientId);
2273
2226
  if (!client.rest) {
2274
2227
  console.error(`[CommandManager] \u2716 Failed to register app commands by guild: REST is not initialized`);
2275
2228
  return;
@@ -2278,31 +2231,35 @@ var CommandManager = class {
2278
2231
  console.log(`[CommandManager] Unregistering commands from ${guildIds.length} guilds...`);
2279
2232
  await Promise.all(
2280
2233
  guildIds.map(
2281
- (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))
2282
2235
  )
2283
2236
  );
2284
2237
  }
2285
2238
  };
2286
2239
 
2287
2240
  // src/modules/event.manager.ts
2288
- var import_discord10 = require("discord.js");
2289
- var EventManager = class {
2290
- client;
2291
- 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";
2292
2246
  logger;
2293
2247
  constructor(client) {
2294
- this.client = client;
2248
+ super(client);
2295
2249
  this.logger = new Logger({ prefixEmoji: "\u{1F4CB}", prefix: `EventManager (i${this.client.clientId})` });
2296
- for (const event of Object.values(import_discord10.Events)) {
2250
+ for (const event of Object.values(import_discord9.Events)) {
2297
2251
  client.on(
2298
2252
  event.toString(),
2299
2253
  async (...args) => this.executeEvents.apply(this, [event, ...args])
2300
2254
  );
2301
2255
  }
2302
2256
  }
2257
+ getName(module2) {
2258
+ return module2.name;
2259
+ }
2303
2260
  register(...events) {
2304
2261
  for (const event of events) {
2305
- this.events.set(event.name, event);
2262
+ this.items.set(event.name, event);
2306
2263
  if (this.client.config.app.verbose) {
2307
2264
  this.logger.debug(`'${event.name}' registered for EventType '${event.event}'`);
2308
2265
  }
@@ -2310,95 +2267,348 @@ var EventManager = class {
2310
2267
  }
2311
2268
  unregister(...names) {
2312
2269
  for (const name of names) {
2313
- const event = this.events.get(name);
2270
+ const event = this.items.get(name);
2314
2271
  if (!event) continue;
2315
- this.events.delete(name);
2272
+ this.items.delete(name);
2316
2273
  if (this.client.config.app.verbose) {
2317
2274
  this.logger.debug(`'${event.name}' unregistered for EventType '${event.event}'`);
2318
2275
  }
2319
2276
  }
2320
2277
  }
2321
- clear() {
2322
- this.events.forEach((e) => this.unregister(e.name));
2323
- this.events.clear();
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));
2521
+ }
2522
+ return name;
2523
+ }
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);
2324
2530
  }
2325
- get(name) {
2326
- 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);
2327
2543
  }
2328
- getByTag(tag) {
2329
- 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();
2330
2550
  }
2331
- getByCategory(category) {
2332
- 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;
2333
2557
  }
2334
- getByEvent(eventType) {
2335
- 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;
2336
2564
  }
2337
- async executeEvents(eventType, ...args) {
2338
- const events = this.getByEvent(eventType);
2339
- if (!events.length) return;
2340
- const sortedEvents = events.sort((a, b) => b.priority - a.priority);
2341
- await Promise.all(
2342
- sortedEvents.map(async (event) => {
2343
- try {
2344
- await event.execute?.(this.client, ...args);
2345
- if (event.once) {
2346
- this.unregister(event.name);
2347
- }
2348
- } catch (err) {
2349
- this.logger.error(`'${event.name}' failed to execute`, err);
2350
- }
2351
- })
2352
- );
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;
2353
2579
  }
2354
- /** Import event modules that end with `.event` */
2355
- async importFrom(dir, replaceAll) {
2356
- dir = Array.isArray(dir) ? dir : [dir];
2357
- const eventModules = await Promise.all(
2358
- dir.map((dir2) => importModulesFromDir(dir2, "event"))
2359
- );
2360
- if (replaceAll) {
2361
- this.clear();
2362
- }
2363
- let importedEvents = 0;
2364
- let ignoredEvents = 0;
2365
- for (const event of eventModules.flat()) {
2366
- if (!event.module.default.enabled) {
2367
- ignoredEvents++;
2368
- } else {
2369
- importedEvents++;
2370
- }
2371
- 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();
2372
2586
  }
2373
- this.client.logger.moduleLoaded("Event Handlers", importedEvents, ignoredEvents);
2374
- 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;
2375
2595
  }
2376
2596
  };
2377
2597
 
2378
- // package.json
2379
- var version = "1.0.35";
2380
-
2381
- // src/client.ts
2382
- var import_node_crypto3 = require("crypto");
2383
- var import_qznt4 = require("qznt");
2384
-
2385
- // src/utils/VimcordCLI.ts
2598
+ // src/utils/vimcord.cli.ts
2386
2599
  var import_node_readline = require("readline");
2387
2600
  var import_qznt3 = require("qznt");
2388
-
2389
- // src/utils/clientUtils.ts
2390
- function useClient(clientId = 0) {
2391
- return Vimcord.instances.get(clientId);
2392
- }
2393
- async function useReadyClient(clientId = 0) {
2394
- return useClient(clientId)?.waitForReady();
2395
- }
2396
- function createClient(options, features = {}, config = {}) {
2397
- return new Vimcord(options, features, config);
2398
- }
2399
-
2400
- // src/utils/VimcordCLI.ts
2401
- 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
+ }
2402
2612
  rl;
2403
2613
  options;
2404
2614
  commands = /* @__PURE__ */ new Map();
@@ -2411,6 +2621,7 @@ var VimcordCLI = class {
2411
2621
  terminal: false
2412
2622
  });
2413
2623
  this.rl.on("line", (line) => {
2624
+ if (_VimcordCLI.mode !== "on") return;
2414
2625
  const { isCommand, commandName, content, args } = this.parseLine(line);
2415
2626
  if (!isCommand) return;
2416
2627
  const command = this.commands.get(commandName);
@@ -2467,7 +2678,6 @@ var VimcordCLI = class {
2467
2678
  return true;
2468
2679
  }
2469
2680
  };
2470
- var initCalled = false;
2471
2681
  var CLI = new VimcordCLI({ prefix: "/" });
2472
2682
  CLI.addCommand("help", "View information about a command, or the available CLI options", (args) => {
2473
2683
  const prefix = CLI.options.prefix;
@@ -2574,16 +2784,81 @@ CLI.addCommand("cmds", "List the loaded commands", async (args, content) => {
2574
2784
  return CLI.logger.error(`'${mode}' is not a valid option. Valid options: [slash|prefix|ctx]`);
2575
2785
  }
2576
2786
  });
2577
- function initCLI() {
2578
- if (initCalled) return;
2579
- CLI.logger.log(`~ Type ${CLI.options.prefix}help to view available commands`);
2580
- initCalled = true;
2581
- }
2582
2787
 
2583
- // src/client.ts
2584
- 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");
2585
2794
  var Vimcord = class _Vimcord extends import_discord11.Client {
2586
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
+ }
2587
2862
  uuid = (0, import_node_crypto3.randomUUID)();
2588
2863
  clientId = _Vimcord.instances.size;
2589
2864
  clientOptions;
@@ -2593,229 +2868,181 @@ var Vimcord = class _Vimcord extends import_discord11.Client {
2593
2868
  events;
2594
2869
  commands;
2595
2870
  db;
2596
- // Configure custom logger
2597
- logger = new Logger({ prefixEmoji: "\u26A1", prefix: `vimcord (i${this.clientId})` }).extend({
2598
- clientBanner(client) {
2599
- if (client.config.app.disableBanner) return;
2600
- const border = "\u2550".repeat(50);
2601
- console.log(import_chalk2.default.hex(this.colors.primary)(`
2602
- \u2554${border}\u2557`));
2603
- console.log(
2604
- import_chalk2.default.hex(this.colors.primary)("\u2551") + import_chalk2.default.bold.hex(this.colors.text)(
2605
- ` \u{1F680} ${client.config.app.name} v${client.config.app.appVersion}`.padEnd(
2606
- 50 - (client.config.app.devMode ? 12 : 0)
2607
- )
2608
- ) + import_chalk2.default.hex(this.colors.primary)(
2609
- `${client.config.app.devMode ? import_chalk2.default.hex(this.colors.warn)("devMode \u26A0\uFE0F ") : ""}\u2551`
2610
- )
2611
- );
2612
- console.log(import_chalk2.default.hex(this.colors.primary)(`\u2551${"".padEnd(50)}\u2551`));
2613
- console.log(
2614
- import_chalk2.default.hex(this.colors.primary)("\u2551") + import_chalk2.default.hex(this.colors.muted)(
2615
- ` # Powered by Vimcord v${version}`.padEnd(50 - 3 - `${client.clientId}`.length)
2616
- ) + import_chalk2.default.hex(this.colors.primary)(`${import_chalk2.default.hex(this.colors.muted)(`i${client.clientId}`)} \u2551`)
2617
- );
2618
- console.log(import_chalk2.default.hex(this.colors.primary)(`\u255A${border}\u255D
2619
- `));
2620
- },
2621
- clientReady(clientTag, guildCount) {
2622
- console.log(
2623
- this.formatTimestamp(),
2624
- this.formatPrefix(),
2625
- import_chalk2.default.hex(this.colors.success)("\u{1F916} READY"),
2626
- import_chalk2.default.white(`Connected as ${import_chalk2.default.bold.hex(this.colors.primary)(clientTag)}`),
2627
- import_chalk2.default.hex(this.colors.muted)(`\u2022 ${guildCount} guilds`)
2628
- );
2629
- },
2630
- moduleLoaded(moduleName, count, ignoredCount) {
2631
- const countText = count ? import_chalk2.default.hex(this.colors.muted)(`(${count} items)`) : "";
2632
- console.log(
2633
- this.formatTimestamp(),
2634
- this.formatPrefix(),
2635
- import_chalk2.default.hex("#9B59B6")("\u{1F4E6} MODULE"),
2636
- import_chalk2.default.hex(this.colors.warn)(`${moduleName} loaded`),
2637
- ignoredCount ? import_chalk2.default.hex(this.colors.muted)(`(${ignoredCount} ignored)`) : "",
2638
- countText
2639
- );
2640
- },
2641
- commandExecuted(commandName, username, guildName) {
2642
- const location = guildName ? `in ${import_chalk2.default.hex(this.colors.muted)(guildName)}` : "in DMs";
2643
- console.log(
2644
- this.formatTimestamp(),
2645
- this.formatPrefix(),
2646
- import_chalk2.default.hex("#87CEEB")("\u{1F4DD} COMMAND"),
2647
- import_chalk2.default.hex(this.colors.warn)(`/${commandName}`),
2648
- import_chalk2.default.white(`used by ${import_chalk2.default.bold(username)}`),
2649
- import_chalk2.default.hex(this.colors.muted)(location)
2650
- );
2651
- },
2652
- database(action, details) {
2653
- console.log(
2654
- this.formatTimestamp(),
2655
- this.formatPrefix(),
2656
- import_chalk2.default.hex("#FF6B9D")("\u{1F5C4}\uFE0F DATABASE"),
2657
- import_chalk2.default.white(action),
2658
- details ? import_chalk2.default.hex(this.colors.muted)(details) : ""
2659
- );
2660
- }
2661
- });
2662
- clientStartingPromise = null;
2871
+ logger = clientLoggerFactory(this);
2872
+ error;
2663
2873
  constructor(options, features = {}, config = {}) {
2664
2874
  super(options);
2665
2875
  this.clientOptions = options;
2666
2876
  this.features = features;
2667
- this.config = {
2668
- app: createVimcordAppConfig(config.app),
2669
- staff: createVimcordStaffConfig(config.staff),
2670
- slashCommands: createVimcordSlashCommandConfig(config.slashCommands),
2671
- prefixCommands: createVimcordPrefixCommandConfig(config.prefixCommands),
2672
- contextCommands: createVimcordContextCommandConfig(config.contextCommands)
2673
- };
2877
+ this.config = defineVimcordConfig(config);
2878
+ this.error = new ErrorHandler(this);
2879
+ if (this.features.useGlobalErrorHandlers) {
2880
+ this.error.setupGlobalHandlers();
2881
+ }
2674
2882
  this.status = new StatusManager(this);
2675
2883
  this.events = new EventManager(this);
2676
2884
  this.commands = new CommandManager(this);
2677
- if (this.features.useEnv) {
2678
- if (typeof this.features.useEnv === "object") {
2679
- import_dotenv.default.config({ quiet: true, ...this.features.useEnv });
2680
- } else {
2681
- import_dotenv.default.config({ quiet: true });
2682
- }
2683
- }
2684
- if (this.features.useGlobalErrorHandlers) {
2685
- process.on("uncaughtException", (err) => this.logger.error("Uncaught Exception", err));
2686
- process.on("unhandledRejection", (err) => this.logger.error("Unhandled Rejection", err));
2687
- process.on("exit", (code) => this.logger.debug(`Process exited with code ${code}`));
2688
- this.on("error", (err) => this.logger.error("Client Error", err));
2689
- this.on("shardError", (err) => this.logger.error("Client Shard Error", err));
2690
- }
2691
2885
  this.logger.clientBanner(this);
2692
- this.once("clientReady", (client) => {
2693
- this.logger.clientReady(client.user.tag, client.guilds.cache.size);
2694
- });
2886
+ this.once("clientReady", (client) => this.logger.clientReady(client.user.tag, client.guilds.cache.size));
2695
2887
  _Vimcord.instances.set(this.clientId, this);
2696
- initCLI();
2888
+ if (this.config.app.enableCLI) {
2889
+ VimcordCLI.setMode("on");
2890
+ }
2697
2891
  }
2698
- /** Returns the options, features, and config of this client. */
2699
- toJSON() {
2700
- return {
2701
- options: this.clientOptions,
2702
- features: this.features,
2703
- config: this.config
2704
- };
2892
+ /** Current app name */
2893
+ // prettier-ignore
2894
+ get $name() {
2895
+ return this.config.app.name;
2705
2896
  }
2706
- /** Makes a clone of this client. */
2707
- clone() {
2708
- const { options, features, config } = this.toJSON();
2709
- return new _Vimcord(options, features, config);
2897
+ // prettier-ignore
2898
+ set $name(name) {
2899
+ this.config.app.name = name;
2710
2900
  }
2711
- configureApp(options = {}) {
2712
- this.config.app = createVimcordAppConfig(options);
2713
- if (this.features.hookToolsDevMode) {
2714
- globalVimcordToolsConfig.devMode = this.config.app.devMode;
2715
- }
2716
- return this;
2901
+ /** Current app version */
2902
+ // prettier-ignore
2903
+ get $version() {
2904
+ return this.config.app.version;
2717
2905
  }
2718
- configureStaff(options = {}) {
2719
- this.config.staff = createVimcordStaffConfig(options);
2720
- return this;
2906
+ // prettier-ignore
2907
+ set $version(version2) {
2908
+ this.config.app.version = version2;
2721
2909
  }
2722
- configureSlashCommands(options = {}) {
2723
- this.config.slashCommands = createVimcordSlashCommandConfig(options);
2724
- return this;
2910
+ /** Current dev mode state */
2911
+ // prettier-ignore
2912
+ get $devMode() {
2913
+ return this.config.app.devMode;
2725
2914
  }
2726
- configurePrefixCommands(options = {}) {
2727
- this.config.prefixCommands = createVimcordPrefixCommandConfig(options);
2728
- return this;
2915
+ // prettier-ignore
2916
+ set $devMode(mode) {
2917
+ this.config.app.devMode = mode;
2729
2918
  }
2730
- configureContextCommands(options = {}) {
2731
- this.config.contextCommands = createVimcordContextCommandConfig(options);
2732
- return this;
2919
+ /** Current verbose mode state */
2920
+ // prettier-ignore
2921
+ get $verboseMode() {
2922
+ return this.config.app.verbose;
2733
2923
  }
2734
- async importEventModules(dir, replaceAll) {
2735
- await this.events.importFrom(dir, replaceAll);
2736
- return this;
2924
+ // prettier-ignore
2925
+ set $verboseMode(mode) {
2926
+ this.config.app.verbose = mode;
2737
2927
  }
2738
- async importSlashCommandModules(dir, replaceAll) {
2739
- await this.commands.slash.importFrom(dir, replaceAll);
2740
- 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 };
2741
2931
  }
2742
- async importPrefixCommandModules(dir, replaceAll) {
2743
- await this.commands.prefix.importFrom(dir, replaceAll);
2744
- 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);
2745
2936
  }
2746
- async importContextCommandModules(dir, replaceAll) {
2747
- 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]);
2748
2944
  return this;
2749
2945
  }
2750
- async useDatabase(db) {
2751
- this.db = db;
2752
- this.logger.database("Using", db.moduleName);
2753
- return this.db.connect();
2754
- }
2755
- async waitForReady() {
2756
- if (this.isReady()) return this;
2757
- return new Promise((resolve, reject) => {
2758
- const timeout = setTimeout(
2759
- () => reject(new Error(`Client (i${this.clientId}) timed out waiting for ready`)),
2760
- 6e4
2761
- );
2762
- this.once("clientReady", () => {
2763
- clearTimeout(timeout);
2764
- resolve(this);
2765
- });
2766
- });
2767
- }
2946
+ /** Builds the client by importing modules and registering builtin handlers. */
2768
2947
  async build() {
2769
- this.configureApp(this.config.app);
2770
- this.configureStaff(this.config.staff);
2771
- this.configureSlashCommands(this.config.slashCommands);
2772
- this.configurePrefixCommands(this.config.prefixCommands);
2773
- 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);
2774
2953
  if (this.features.importModules) {
2775
2954
  const importModules = this.features.importModules;
2776
2955
  await Promise.all([
2777
- importModules.events && this.importEventModules(importModules.events),
2778
- importModules.slashCommands && this.importSlashCommandModules(importModules.slashCommands),
2779
- importModules.prefixCommands && this.importPrefixCommandModules(importModules.prefixCommands),
2780
- 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)
2781
2960
  ]);
2782
2961
  }
2783
2962
  if (this.features.useDefaultSlashCommandHandler) {
2784
- this.events.register(BUILTIN_SlashCommandHandler);
2963
+ this.events.register(slashCommandHandler);
2785
2964
  }
2786
2965
  if (this.features.useDefaultContextCommandHandler) {
2787
- this.events.register(BUILTIN_ContextCommandHandler);
2966
+ this.events.register(contextCommandHandler);
2788
2967
  }
2789
2968
  if (this.features.useDefaultPrefixCommandHandler) {
2790
- this.events.register(BUILTIN_PrefixCommandHandler);
2969
+ this.events.register(prefixCommandHandler);
2791
2970
  }
2792
2971
  return this;
2793
2972
  }
2794
- 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) {
2795
3019
  if (this.clientStartingPromise) return this.clientStartingPromise;
2796
- const main = async () => {
3020
+ const execute = async () => {
2797
3021
  let token = typeof tokenOrPreHook === "string" ? tokenOrPreHook : void 0;
2798
- token ??= this.config.app.devMode ? process.env.TOKEN_DEV : process.env.TOKEN;
3022
+ token ??= this.$devMode ? process.env.TOKEN_DEV : process.env.TOKEN;
2799
3023
  if (!token) {
2800
3024
  throw new Error(
2801
- `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"}`
2802
3026
  );
2803
3027
  }
2804
3028
  await this.build();
2805
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...");
2806
3037
  if (typeof tokenOrPreHook === "function") {
2807
3038
  await tokenOrPreHook(this);
2808
3039
  } else {
2809
- await preHook?.(this);
3040
+ await callback?.(this);
2810
3041
  }
2811
- const stopLoader = this.logger.loader("Connecting to Discord...");
2812
- const loginResult = await import_qznt4.$.async.retry(() => super.login(token), this.features.loginAttempts ?? 3, 1e3);
2813
- stopLoader("Connected to Discord ");
2814
- this.config.app.verbose && this.logger.debug("\u23F3 Waiting for ready...");
2815
3042
  return loginResult;
2816
3043
  } catch (err) {
2817
3044
  this.logger.error(
2818
- `Failed to log into Discord after ${this.features.loginAttempts} attempt(s))`,
3045
+ `Failed to log into Discord after ${this.features.maxLoginAttempts} attempt(s)`,
2819
3046
  err
2820
3047
  );
2821
3048
  return null;
@@ -2823,29 +3050,25 @@ var Vimcord = class _Vimcord extends import_discord11.Client {
2823
3050
  this.clientStartingPromise = null;
2824
3051
  }
2825
3052
  };
2826
- this.clientStartingPromise = main();
3053
+ this.clientStartingPromise = execute();
2827
3054
  return this.clientStartingPromise;
2828
3055
  }
3056
+ /** Destroys the client and disconnects from Discord. */
2829
3057
  async kill() {
2830
3058
  await super.destroy();
2831
3059
  _Vimcord.instances.delete(this.clientId);
2832
- this.logger.debug("\u{1F6AA} Logged out of Discord");
2833
- }
2834
- /** Shortcut for {@link fetchUser tools.fetchUser} */
2835
- async fetchUser(id) {
2836
- const client = await this.waitForReady();
2837
- return fetchUser(client, id);
2838
- }
2839
- /** Shortcut for {@link fetchGuild tools.fetchGuild} */
2840
- async fetchGuild(id) {
2841
- const client = await this.waitForReady();
2842
- return fetchGuild(client, id);
3060
+ this.logger.debug("Logged out of Discord");
2843
3061
  }
2844
3062
  };
2845
3063
 
2846
- // 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
2847
3070
  var import_mongoose = __toESM(require("mongoose"));
2848
- var import_node_events2 = __toESM(require("events"));
3071
+ var import_node_events3 = __toESM(require("events"));
2849
3072
  var import_qznt5 = require("qznt");
2850
3073
  try {
2851
3074
  import("mongoose");
@@ -2854,7 +3077,7 @@ try {
2854
3077
  }
2855
3078
  var MongoDatabase = class _MongoDatabase {
2856
3079
  static instances = /* @__PURE__ */ new Map();
2857
- static emitter = new import_node_events2.default();
3080
+ static emitter = new import_node_events3.default();
2858
3081
  moduleName = "MongoDatabase";
2859
3082
  clientId;
2860
3083
  client;
@@ -2939,10 +3162,9 @@ var MongoDatabase = class _MongoDatabase {
2939
3162
  this.isConnecting = true;
2940
3163
  try {
2941
3164
  const stopLoader = this.client.logger.loader("Connecting to MongoDB...");
2942
- await import_qznt5.$.async.retry(
2943
- () => this.mongoose.connect(connectionUri, { autoIndex: true, ...connectionOptions }),
2944
- maxRetries
2945
- );
3165
+ await import_qznt5.$.async.retry(() => this.mongoose.connect(connectionUri, { autoIndex: true, ...connectionOptions }), {
3166
+ retries: maxRetries
3167
+ });
2946
3168
  _MongoDatabase.emitter.emit("ready", this);
2947
3169
  stopLoader("Connected to MongoDB ");
2948
3170
  } catch (err) {
@@ -2973,10 +3195,7 @@ var MongoDatabase = class _MongoDatabase {
2973
3195
  }
2974
3196
  };
2975
3197
 
2976
- // src/modules/db/mongo/mongoSchema.builder.ts
2977
- var import_mongoose2 = require("mongoose");
2978
- var import_node_crypto4 = require("crypto");
2979
- var import_qznt6 = require("qznt");
3198
+ // src/db/mongo/mongo-schema.builder.ts
2980
3199
  try {
2981
3200
  import("mongoose");
2982
3201
  } catch {
@@ -3062,10 +3281,13 @@ var MongoSchemaBuilder = class _MongoSchemaBuilder {
3062
3281
  * @param maxRetries [default: 3]
3063
3282
  */
3064
3283
  async execute(fn, maxRetries = 3) {
3065
- return await import_qznt6.$.async.retry(async () => {
3066
- const model = await this.getModel();
3067
- return await fn(model);
3068
- }, 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
+ );
3069
3291
  }
3070
3292
  async startSession(options) {
3071
3293
  return await this.execute(async () => {
@@ -3139,6 +3361,12 @@ var MongoSchemaBuilder = class _MongoSchemaBuilder {
3139
3361
  return result?.length ? result : [];
3140
3362
  });
3141
3363
  }
3364
+ async bulkWrite(ops, options) {
3365
+ return await this.execute(async (model) => model.bulkWrite(ops, options));
3366
+ }
3367
+ async bulkSave(docs, options) {
3368
+ return await this.execute(async (model) => model.bulkSave(docs, options));
3369
+ }
3142
3370
  };
3143
3371
 
3144
3372
  // src/tools/BetterCollector.ts
@@ -3310,8 +3538,8 @@ var BetterCollector = class _BetterCollector {
3310
3538
  handleListenerError(err) {
3311
3539
  console.error("[BetterCollector] Listener Error:", err);
3312
3540
  }
3313
- constructor(message, options) {
3314
- this.config = options?.config || globalVimcordToolsConfig;
3541
+ constructor(message, options = {}) {
3542
+ this.config = options.config ? createToolsConfig(options.config) : globalToolsConfig;
3315
3543
  this.message = message || void 0;
3316
3544
  this.options = options;
3317
3545
  this.build();
@@ -3357,7 +3585,7 @@ var BetterContainer = class {
3357
3585
  data;
3358
3586
  config;
3359
3587
  constructor(data = {}) {
3360
- this.config = data.config || globalVimcordToolsConfig;
3588
+ this.config = data.config ? createToolsConfig(data.config) : globalToolsConfig;
3361
3589
  this.data = {
3362
3590
  color: data.color ?? (this.config.devMode ? this.config.embedColorDev : this.config.embedColor),
3363
3591
  ...data
@@ -3365,7 +3593,7 @@ var BetterContainer = class {
3365
3593
  this.build();
3366
3594
  }
3367
3595
  configure() {
3368
- if (this.data.color) {
3596
+ if (this.data.color !== void 0) {
3369
3597
  try {
3370
3598
  const color = Array.isArray(this.data.color) ? this.data.color[Math.floor(Math.random() * this.data.color.length)] ?? null : this.data.color;
3371
3599
  if (color) {
@@ -3381,6 +3609,14 @@ var BetterContainer = class {
3381
3609
  build() {
3382
3610
  this.configure();
3383
3611
  }
3612
+ setColor(color) {
3613
+ this.data.color = color;
3614
+ return this;
3615
+ }
3616
+ clearColor() {
3617
+ this.data.color = null;
3618
+ return this;
3619
+ }
3384
3620
  addSeparator(options) {
3385
3621
  this.container.addSeparatorComponents((sb) => {
3386
3622
  if (options?.divider !== void 0) sb.setDivider(options.divider);
@@ -3458,7 +3694,7 @@ var BetterModal = class {
3458
3694
  this.id = options.id || this.createModalId();
3459
3695
  this.options = options;
3460
3696
  this.modal = new import_discord13.ModalBuilder().setCustomId(this.id);
3461
- this.config = options.config || globalVimcordToolsConfig;
3697
+ this.config = options.config || globalToolsConfig;
3462
3698
  if (options.title) {
3463
3699
  this.setTitle(options.title);
3464
3700
  }
@@ -3638,7 +3874,8 @@ var BetterModal = class {
3638
3874
  values,
3639
3875
  interaction: modalSubmit,
3640
3876
  reply: (options2) => dynaSend(modalSubmit, options2),
3641
- deferUpdate: async (options2) => await modalSubmit.deferUpdate(options2)
3877
+ deferUpdate: async (options2) => await modalSubmit.deferUpdate(options2),
3878
+ followUp: async (options2) => await modalSubmit.followUp(options2)
3642
3879
  };
3643
3880
  } catch (error) {
3644
3881
  return null;
@@ -3656,7 +3893,7 @@ var BetterModal = class {
3656
3893
 
3657
3894
  // src/tools/Paginator.ts
3658
3895
  var import_discord14 = require("discord.js");
3659
- var import_node_events3 = __toESM(require("events"));
3896
+ var import_node_events4 = __toESM(require("events"));
3660
3897
  var PaginationType = /* @__PURE__ */ ((PaginationType2) => {
3661
3898
  PaginationType2[PaginationType2["Short"] = 0] = "Short";
3662
3899
  PaginationType2[PaginationType2["ShortJump"] = 1] = "ShortJump";
@@ -3701,9 +3938,9 @@ var Paginator = class {
3701
3938
  config;
3702
3939
  data;
3703
3940
  events;
3704
- eventEmitter = new import_node_events3.default();
3941
+ eventEmitter = new import_node_events4.default();
3705
3942
  constructor(options = {}) {
3706
- this.config = options.config || globalVimcordToolsConfig;
3943
+ this.config = options.config ? createToolsConfig(options.config) : globalToolsConfig;
3707
3944
  this.options = {
3708
3945
  type: options.type ?? 0 /* Short */,
3709
3946
  participants: options.participants ?? [],
@@ -4131,7 +4368,7 @@ var Prompt = class {
4131
4368
  config;
4132
4369
  message = null;
4133
4370
  constructor(options = {}) {
4134
- this.config = options.config ?? globalVimcordToolsConfig;
4371
+ this.config = options.config ? createToolsConfig(options.config) : globalToolsConfig;
4135
4372
  this.participants = options.participants ?? [];
4136
4373
  this.timeout = options.timeout ?? this.config.timeouts.prompt;
4137
4374
  this.content = options.content;
@@ -4313,9 +4550,6 @@ async function prompt(handler, options, sendOptions) {
4313
4550
  }
4314
4551
  // Annotate the CommonJS export names for ESM import in node:
4315
4552
  0 && (module.exports = {
4316
- BUILTIN_ContextCommandHandler,
4317
- BUILTIN_PrefixCommandHandler,
4318
- BUILTIN_SlashCommandHandler,
4319
4553
  BaseCommandBuilder,
4320
4554
  BaseCommandManager,
4321
4555
  BetterCollector,
@@ -4328,13 +4562,16 @@ async function prompt(handler, options, sendOptions) {
4328
4562
  CommandType,
4329
4563
  ContextCommandBuilder,
4330
4564
  ContextCommandManager,
4565
+ DEFAULT_MODULE_SUFFIXES,
4331
4566
  DynaSend,
4567
+ ErrorHandler,
4332
4568
  EventBuilder,
4333
4569
  EventManager,
4334
4570
  LOGGER_COLORS,
4335
4571
  LogLevel,
4336
4572
  Logger,
4337
4573
  MissingPermissionReason,
4574
+ ModuleImporter,
4338
4575
  MongoDatabase,
4339
4576
  MongoSchemaBuilder,
4340
4577
  PaginationTimeoutType,
@@ -4354,17 +4591,24 @@ async function prompt(handler, options, sendOptions) {
4354
4591
  VimcordCLI,
4355
4592
  __zero,
4356
4593
  cleanMention,
4594
+ clientLoggerFactory,
4595
+ configSetters,
4596
+ contextCommandHandler,
4597
+ createAppConfig,
4357
4598
  createClient,
4599
+ createConfigFactory,
4600
+ createContextCommandConfig,
4358
4601
  createMongoPlugin,
4359
4602
  createMongoSchema,
4603
+ createPrefixCommandConfig,
4604
+ createSlashCommandConfig,
4605
+ createStaffConfig,
4360
4606
  createToolsConfig,
4361
- createVimcordAppConfig,
4362
- createVimcordContextCommandConfig,
4363
- createVimcordPrefixCommandConfig,
4364
- createVimcordSlashCommandConfig,
4365
- createVimcordStaffConfig,
4366
4607
  createVimcordStatusConfig,
4608
+ defineClientOptions,
4367
4609
  defineGlobalToolsConfig,
4610
+ defineVimcordConfig,
4611
+ defineVimcordFeatures,
4368
4612
  dynaSend,
4369
4613
  fetchChannel,
4370
4614
  fetchGuild,
@@ -4372,17 +4616,20 @@ async function prompt(handler, options, sendOptions) {
4372
4616
  fetchMessage,
4373
4617
  fetchRole,
4374
4618
  fetchUser,
4375
- getCallerFileName,
4619
+ getDevMode,
4376
4620
  getFirstMentionId,
4377
4621
  getMessageMention,
4622
+ getPackageJson,
4378
4623
  getProcessDir,
4379
- globalVimcordToolsConfig,
4624
+ globalToolsConfig,
4380
4625
  importModulesFromDir,
4381
- initCLI,
4382
4626
  isMentionOrSnowflake,
4383
4627
  logger,
4628
+ moduleImporters,
4629
+ prefixCommandHandler,
4384
4630
  prompt,
4385
4631
  sendCommandErrorEmbed,
4632
+ slashCommandHandler,
4386
4633
  useClient,
4387
4634
  useReadyClient,
4388
4635
  validateCommandPermissions