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