vimcord 1.0.36 → 1.0.38

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