vimcord 1.0.28 → 1.0.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -30,6 +30,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ BUILTIN_ContextCommandHandler: () => BUILTIN_ContextCommandHandler,
34
+ BUILTIN_PrefixCommandHandler: () => BUILTIN_PrefixCommandHandler,
35
+ BUILTIN_SlashCommandHandler: () => BUILTIN_SlashCommandHandler,
33
36
  BaseCommandBuilder: () => BaseCommandBuilder,
34
37
  BaseCommandManager: () => BaseCommandManager,
35
38
  BetterCollector: () => BetterCollector,
@@ -68,7 +71,6 @@ __export(index_exports, {
68
71
  VimcordCLI: () => VimcordCLI,
69
72
  __zero: () => __zero,
70
73
  cleanMention: () => cleanMention,
71
- clientInstances: () => clientInstances,
72
74
  createClient: () => createClient,
73
75
  createMongoPlugin: () => createMongoPlugin,
74
76
  createMongoSchema: () => createMongoSchema,
@@ -87,9 +89,7 @@ __export(index_exports, {
87
89
  fetchMessage: () => fetchMessage,
88
90
  fetchRole: () => fetchRole,
89
91
  fetchUser: () => fetchUser,
90
- formatThousands: () => formatThousands,
91
92
  getCallerFileName: () => getCallerFileName,
92
- getClientInstances: () => getClientInstances,
93
93
  getFirstMentionId: () => getFirstMentionId,
94
94
  getMessageMention: () => getMessageMention,
95
95
  getProcessDir: () => getProcessDir,
@@ -99,7 +99,6 @@ __export(index_exports, {
99
99
  isMentionOrSnowflake: () => isMentionOrSnowflake,
100
100
  logger: () => logger,
101
101
  prompt: () => prompt,
102
- retryExponentialBackoff: () => retryExponentialBackoff,
103
102
  sendCommandErrorEmbed: () => sendCommandErrorEmbed,
104
103
  useClient: () => useClient,
105
104
  useReadyClient: () => useReadyClient,
@@ -528,6 +527,10 @@ var ContextCommandBuilder = class extends BaseCommandBuilder {
528
527
  // src/utils/dir.ts
529
528
  var import_node_path = __toESM(require("path"));
530
529
  var import_qznt = require("qznt");
530
+ function getCallerFileName() {
531
+ const stack = new Error().stack?.split("\n");
532
+ return stack?.at(4)?.split("at file")?.at(1)?.split("/").at(-1)?.split(":").at(0)?.split(".").at(0);
533
+ }
531
534
  function getProcessDir() {
532
535
  const mainPath = process.argv[1];
533
536
  if (!mainPath) return "";
@@ -537,9 +540,7 @@ async function importModulesFromDir(dir, suffix) {
537
540
  const cwd = getProcessDir();
538
541
  const MODULE_RELATIVE_PATH = import_node_path.default.join(cwd, dir);
539
542
  const MODULE_LOG_PATH = dir;
540
- const files = import_qznt.$.fs.readDir(MODULE_RELATIVE_PATH).filter(
541
- (fn) => fn.endsWith(`${suffix ? `.${suffix}` : ""}.js`) || fn.endsWith(`${suffix ? `.${suffix}` : ""}.ts`)
542
- );
543
+ const files = import_qznt.$.fs.readDir(MODULE_RELATIVE_PATH).filter((fn) => fn.endsWith(`${suffix ? `.${suffix}` : ""}.js`) || fn.endsWith(`${suffix ? `.${suffix}` : ""}.ts`));
543
544
  if (!files.length) {
544
545
  return [];
545
546
  }
@@ -564,10 +565,6 @@ async function importModulesFromDir(dir, suffix) {
564
565
  }
565
566
  return filteredModules;
566
567
  }
567
- function getCallerFileName() {
568
- const stack = new Error().stack?.split("\n");
569
- return stack?.at(4)?.split("at file")?.at(1)?.split("/").at(-1)?.split(":").at(0)?.split(".").at(0);
570
- }
571
568
 
572
569
  // src/builders/event.builder.ts
573
570
  var import_node_crypto2 = require("crypto");
@@ -923,7 +920,7 @@ var SlashCommandBuilder = class extends BaseCommandBuilder {
923
920
  };
924
921
 
925
922
  // src/client.ts
926
- var import_discord10 = require("discord.js");
923
+ var import_discord11 = require("discord.js");
927
924
  var import_dotenv = __toESM(require("dotenv"));
928
925
 
929
926
  // src/configs/tools.config.ts
@@ -1044,1280 +1041,1551 @@ function createVimcordContextCommandConfig(options = {}) {
1044
1041
  return import_lodash10.default.merge(defaultConfig5, options);
1045
1042
  }
1046
1043
 
1047
- // src/tools/Logger.ts
1048
- var import_chalk = __toESM(require("chalk"));
1049
- var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
1050
- LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
1051
- LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
1052
- LogLevel2[LogLevel2["SUCCESS"] = 2] = "SUCCESS";
1053
- LogLevel2[LogLevel2["WARN"] = 3] = "WARN";
1054
- LogLevel2[LogLevel2["ERROR"] = 4] = "ERROR";
1055
- return LogLevel2;
1056
- })(LogLevel || {});
1057
- var LOGGER_COLORS = {
1058
- primary: "#5865F2",
1059
- success: "#57F287",
1060
- warn: "#FEE75C",
1061
- danger: "#ED4245",
1062
- muted: "#747F8D",
1063
- text: "#FFFFFF"
1064
- };
1065
- var Logger = class {
1066
- logPrefixEmoji;
1067
- logPrefix;
1068
- minLevel;
1069
- showTimestamp;
1070
- colorScheme;
1071
- constructor(options) {
1072
- const { prefixEmoji = null, prefix = null, minLevel = 0 /* DEBUG */, showTimestamp = true } = options || {};
1073
- this.logPrefixEmoji = prefixEmoji;
1074
- this.logPrefix = prefix;
1075
- this.minLevel = minLevel;
1076
- this.showTimestamp = showTimestamp;
1077
- this.colorScheme = {
1078
- ...LOGGER_COLORS,
1079
- ...options?.colors
1080
- };
1081
- }
1082
- formatTimestamp() {
1083
- if (!this.showTimestamp) return "";
1084
- const now = /* @__PURE__ */ new Date();
1085
- const time = now.toLocaleTimeString("en-US", {
1086
- hour12: false,
1087
- hour: "2-digit",
1088
- minute: "2-digit",
1089
- second: "2-digit"
1090
- });
1091
- return import_chalk.default.hex(this.colorScheme.muted)(`[${time}]`);
1092
- }
1093
- formatPrefix() {
1094
- if (!this.logPrefix) return "";
1095
- return import_chalk.default.bold.hex(this.colorScheme.primary)(
1096
- `${this.logPrefixEmoji ? `${this.logPrefixEmoji} ` : ""}${this.logPrefix}`
1097
- );
1044
+ // src/utils/sendCommandErrorEmbed.ts
1045
+ var import_discord6 = require("discord.js");
1046
+
1047
+ // src/tools/BetterEmbed.ts
1048
+ var import_discord5 = require("discord.js");
1049
+
1050
+ // src/tools/dynaSend.ts
1051
+ var import_discord4 = require("discord.js");
1052
+
1053
+ // src/tools/types.ts
1054
+ var SendMethod = /* @__PURE__ */ ((SendMethod2) => {
1055
+ SendMethod2[SendMethod2["Reply"] = 0] = "Reply";
1056
+ SendMethod2[SendMethod2["EditReply"] = 1] = "EditReply";
1057
+ SendMethod2[SendMethod2["FollowUp"] = 2] = "FollowUp";
1058
+ SendMethod2[SendMethod2["Channel"] = 3] = "Channel";
1059
+ SendMethod2[SendMethod2["MessageReply"] = 4] = "MessageReply";
1060
+ SendMethod2[SendMethod2["MessageEdit"] = 5] = "MessageEdit";
1061
+ SendMethod2[SendMethod2["User"] = 6] = "User";
1062
+ return SendMethod2;
1063
+ })(SendMethod || {});
1064
+
1065
+ // src/tools/dynaSend.ts
1066
+ var DynaSend = class {
1067
+ static forceArray(value) {
1068
+ return Array.isArray(value) ? value : [value];
1098
1069
  }
1099
- shouldLog(level) {
1100
- return level >= this.minLevel;
1070
+ static isInteractionCallback(obj) {
1071
+ return obj instanceof import_discord4.InteractionCallbackResponse;
1101
1072
  }
1102
- get prefixEmoji() {
1103
- return this.logPrefixEmoji;
1073
+ static filterFlags(flags, excludeFlags) {
1074
+ if (!flags) return void 0;
1075
+ const flagArray = this.forceArray(flags);
1076
+ return flagArray.filter((flag) => !excludeFlags.includes(flag));
1104
1077
  }
1105
- get prefix() {
1106
- return this.logPrefix;
1078
+ static detectSendMethod(handler) {
1079
+ if (handler instanceof import_discord4.BaseInteraction) {
1080
+ return handler.replied || handler.deferred ? 1 /* EditReply */ : 0 /* Reply */;
1081
+ }
1082
+ if (handler instanceof import_discord4.BaseChannel) return 3 /* Channel */;
1083
+ if (handler instanceof import_discord4.Message) return 4 /* MessageReply */;
1084
+ if (handler instanceof import_discord4.GuildMember || handler instanceof import_discord4.User) return 6 /* User */;
1085
+ throw new Error("[DynaSend] Unable to determine send method for handler type");
1107
1086
  }
1108
- get colors() {
1109
- return this.colorScheme;
1087
+ static validateSendMethod(handler, method) {
1088
+ const interactionMethods = [0 /* Reply */, 1 /* EditReply */, 2 /* FollowUp */];
1089
+ if (interactionMethods.includes(method) && !(handler instanceof import_discord4.BaseInteraction)) {
1090
+ throw new TypeError(`[DynaSend] SendMethod '${SendMethod[method]}' requires BaseInteraction handler`);
1091
+ }
1092
+ if (method === 3 /* Channel */ && !(handler instanceof import_discord4.BaseChannel)) {
1093
+ throw new TypeError(`[DynaSend] SendMethod '${SendMethod[method]}' requires BaseChannel handler`);
1094
+ }
1095
+ if ([4 /* MessageReply */, 5 /* MessageEdit */].includes(method) && !(handler instanceof import_discord4.Message)) {
1096
+ throw new TypeError(`[DynaSend] SendMethod '${SendMethod[method]}' requires Message handler`);
1097
+ }
1098
+ if (method === 6 /* User */ && !(handler instanceof import_discord4.GuildMember || handler instanceof import_discord4.User)) {
1099
+ throw new TypeError(`[DynaSend] SendMethod '${SendMethod[method]}' requires User or GuildMember handler`);
1100
+ }
1110
1101
  }
1111
- extend(extras) {
1112
- for (const [key, fn] of Object.entries(extras)) {
1113
- if (typeof fn === "function") {
1114
- this[key] = function(...args) {
1115
- return fn.call(this, ...args);
1102
+ static createMessageData(options, method) {
1103
+ const baseData = {
1104
+ content: options.content,
1105
+ embeds: options.embeds,
1106
+ components: options.components,
1107
+ files: options.files,
1108
+ allowedMentions: options.allowedMentions,
1109
+ tts: options.tts
1110
+ };
1111
+ switch (method) {
1112
+ case 0 /* Reply */:
1113
+ return {
1114
+ ...baseData,
1115
+ flags: options.flags,
1116
+ withResponse: options.withResponse,
1117
+ poll: options.poll
1116
1118
  };
1117
- }
1119
+ case 1 /* EditReply */:
1120
+ return {
1121
+ ...baseData,
1122
+ flags: this.filterFlags(options.flags, ["Ephemeral", "SuppressNotifications"]),
1123
+ withResponse: options.withResponse,
1124
+ poll: options.poll
1125
+ };
1126
+ case 2 /* FollowUp */:
1127
+ return {
1128
+ ...baseData,
1129
+ flags: options.flags,
1130
+ withResponse: options.withResponse,
1131
+ poll: options.poll
1132
+ };
1133
+ case 3 /* Channel */:
1134
+ return {
1135
+ ...baseData,
1136
+ flags: this.filterFlags(options.flags, ["Ephemeral"]),
1137
+ poll: options.poll,
1138
+ stickers: options.stickers,
1139
+ reply: options.reply
1140
+ };
1141
+ case 4 /* MessageReply */:
1142
+ return {
1143
+ ...baseData,
1144
+ flags: this.filterFlags(options.flags, ["Ephemeral"]),
1145
+ poll: options.poll,
1146
+ stickers: options.stickers
1147
+ };
1148
+ case 5 /* MessageEdit */:
1149
+ return {
1150
+ ...baseData,
1151
+ flags: this.filterFlags(options.flags, ["Ephemeral", "SuppressNotifications"])
1152
+ };
1153
+ case 6 /* User */:
1154
+ return {
1155
+ ...baseData,
1156
+ flags: this.filterFlags(options.flags, ["Ephemeral"]),
1157
+ poll: options.poll,
1158
+ forward: options.forward,
1159
+ stickers: options.stickers
1160
+ };
1161
+ default:
1162
+ return baseData;
1118
1163
  }
1119
- return this;
1120
- }
1121
- setPrefix(prefix) {
1122
- this.logPrefix = prefix;
1123
- return this;
1124
- }
1125
- setPrefixEmoji(prefixEmoji) {
1126
- this.logPrefixEmoji = prefixEmoji;
1127
- return this;
1128
1164
  }
1129
- setMinLevel(minLevel) {
1130
- this.minLevel = minLevel;
1131
- return this;
1165
+ static async executeSend(handler, method, data) {
1166
+ try {
1167
+ switch (method) {
1168
+ case 0 /* Reply */: {
1169
+ const response = await handler.reply(data);
1170
+ return this.isInteractionCallback(response) ? response.resource?.message ?? null : null;
1171
+ }
1172
+ case 1 /* EditReply */:
1173
+ return await handler.editReply(data);
1174
+ case 2 /* FollowUp */:
1175
+ return await handler.followUp(data);
1176
+ case 3 /* Channel */:
1177
+ return await handler.send(data);
1178
+ case 4 /* MessageReply */:
1179
+ return await handler.reply(data);
1180
+ case 5 /* MessageEdit */: {
1181
+ const message = handler;
1182
+ if (!message.editable) {
1183
+ console.warn("[DynaSend] Message is not editable");
1184
+ return null;
1185
+ }
1186
+ return await message.edit(data);
1187
+ }
1188
+ case 6 /* User */:
1189
+ return await handler.send(data);
1190
+ default:
1191
+ throw new Error(`[DynaSend] Unknown send method '${method}'`);
1192
+ }
1193
+ } catch (error) {
1194
+ console.error(`[DynaSend] Error with method '${SendMethod[method]}':`, error);
1195
+ return null;
1196
+ }
1132
1197
  }
1133
- setShowTimestamp(show) {
1134
- this.showTimestamp = show;
1135
- return this;
1198
+ static scheduleDelete(message, delay) {
1199
+ if (delay < 1e3) {
1200
+ console.warn(`[DynaSend] Delete delay is less than 1 second (${delay}ms). Is this intentional?`);
1201
+ }
1202
+ setTimeout(async () => {
1203
+ try {
1204
+ if (message.deletable) {
1205
+ await message.delete();
1206
+ }
1207
+ } catch (error) {
1208
+ console.error("[DynaSend] Error deleting message:", error);
1209
+ }
1210
+ }, delay);
1136
1211
  }
1137
- setColors(colors) {
1138
- this.colorScheme = {
1139
- ...LOGGER_COLORS,
1140
- ...colors
1141
- };
1142
- return this;
1212
+ static async send(handler, options) {
1213
+ const sendMethod = options.sendMethod ?? this.detectSendMethod(handler);
1214
+ this.validateSendMethod(handler, sendMethod);
1215
+ const messageData = this.createMessageData(options, sendMethod);
1216
+ const message = await this.executeSend(handler, sendMethod, messageData);
1217
+ if (options.deleteAfter && message) {
1218
+ this.scheduleDelete(message, options.deleteAfter);
1219
+ }
1220
+ return message;
1143
1221
  }
1144
- log(message, ...args) {
1145
- console.log(this.formatTimestamp(), this.formatPrefix(), message, ...args);
1222
+ };
1223
+ async function dynaSend(handler, options) {
1224
+ return DynaSend.send(handler, options);
1225
+ }
1226
+
1227
+ // src/tools/BetterEmbed.ts
1228
+ var BetterEmbed = class _BetterEmbed {
1229
+ embed = new import_discord5.EmbedBuilder();
1230
+ data;
1231
+ config;
1232
+ /** A powerful wrapper for `EmbedBuilder` that introduces useful features
1233
+ *
1234
+ * Auto-shorthand context formatting (_ACF_) is enabled by default
1235
+ *
1236
+ * All functions utilize _ACF_ unless `BetterEmbed.acf` is set to `false`
1237
+ *
1238
+ * ___Use a blackslash___ `\` ___to escape any context___
1239
+ *
1240
+ * \- - - Author Context - - -
1241
+ * - __`$USER`__: _author's mention (@xsqu1znt)_
1242
+ * - __`$USER_NAME`__: _author's username_
1243
+ * - __`$DISPLAY_NAME`__: _author's display name (requires `GuildMember` context)_
1244
+ * - __`$USER_AVATAR`__: _author's avatar_
1245
+ *
1246
+ * \- - - Client Context - - -
1247
+ *
1248
+ * - __`$BOT_AVATAR`__: _bot's avatar_
1249
+ *
1250
+ * \- - - Shorthand Context - - -
1251
+ * - __`$YEAR`__: _YYYY_
1252
+ * - __`$MONTH`__: _MM_
1253
+ * - __`$DAY`__: _DD_
1254
+ * - __`$year`__: _YY_
1255
+ * - __`$month`__: _M or MM_
1256
+ * - __`$day`__: _D or DD_ */
1257
+ constructor(data = {}) {
1258
+ this.config = data.config || globalVimcordToolsConfig;
1259
+ this.data = {
1260
+ context: data.context || null,
1261
+ author: data.author || null,
1262
+ title: data.title || null,
1263
+ thumbnailUrl: data.thumbnailUrl || null,
1264
+ description: data.description || null,
1265
+ imageUrl: data.imageUrl || null,
1266
+ footer: data.footer || null,
1267
+ fields: data.fields || [],
1268
+ color: data.color ?? (this.config.devMode ? this.config.embedColorDev : this.config.embedColor),
1269
+ timestamp: data.timestamp || null,
1270
+ acf: data.acf ?? true
1271
+ };
1272
+ this.build();
1146
1273
  }
1147
- debug(message, ...args) {
1148
- if (!this.shouldLog(0 /* DEBUG */)) return;
1149
- console.log(
1150
- this.formatTimestamp(),
1151
- this.formatPrefix(),
1152
- import_chalk.default.hex(this.colorScheme.muted)("DEBUG"),
1153
- import_chalk.default.dim(message),
1154
- ...args
1155
- );
1274
+ build() {
1275
+ this.normalizeData();
1276
+ this.applyContextFormatting();
1277
+ this.configureEmbed();
1156
1278
  }
1157
- info(message, ...args) {
1158
- if (!this.shouldLog(1 /* INFO */)) return;
1159
- console.log(this.formatTimestamp(), this.formatPrefix(), import_chalk.default.hex("#87CEEB")("INFO"), message, ...args);
1279
+ normalizeData() {
1280
+ if (typeof this.data.author === "string") {
1281
+ this.data.author = { text: this.data.author };
1282
+ }
1283
+ if (typeof this.data.title === "string") {
1284
+ this.data.title = { text: this.data.title };
1285
+ }
1286
+ if (typeof this.data.footer === "string") {
1287
+ this.data.footer = { text: this.data.footer };
1288
+ }
1289
+ if (this.data.timestamp === true) {
1290
+ this.data.timestamp = Date.now();
1291
+ }
1160
1292
  }
1161
- success(message, ...args) {
1162
- if (!this.shouldLog(2 /* SUCCESS */)) return;
1163
- console.log(
1164
- this.formatTimestamp(),
1165
- this.formatPrefix(),
1166
- import_chalk.default.bold.hex(this.colorScheme.success)("\u2713 SUCCESS"),
1167
- import_chalk.default.hex(this.colorScheme.success)(message),
1168
- ...args
1169
- );
1293
+ getContextUser() {
1294
+ const context = this.data.context;
1295
+ if (!context) return null;
1296
+ return context.user || context.interaction?.member || context.interaction?.user || context.message?.member || context.message?.author || null;
1170
1297
  }
1171
- warn(message, ...args) {
1172
- if (!this.shouldLog(3 /* WARN */)) return;
1173
- console.warn(
1174
- this.formatTimestamp(),
1175
- this.formatPrefix(),
1176
- import_chalk.default.bold.hex(this.colorScheme.warn)("\u26A0 WARN"),
1177
- import_chalk.default.hex(this.colorScheme.warn)(message),
1178
- ...args
1179
- );
1298
+ getContextClient() {
1299
+ const context = this.data.context;
1300
+ if (!context) return null;
1301
+ return context.client || context.interaction?.client || context.message?.client || null;
1180
1302
  }
1181
- error(message, error, ...args) {
1182
- if (!this.shouldLog(4 /* ERROR */)) return;
1183
- console.error(
1184
- this.formatTimestamp(),
1185
- this.formatPrefix(),
1186
- import_chalk.default.bold.hex(this.colorScheme.danger)("\u2715 ERROR"),
1187
- import_chalk.default.hex(this.colorScheme.danger)(message),
1188
- ...args
1189
- );
1190
- if (error && error.stack) {
1191
- console.error(import_chalk.default.dim(error.stack));
1303
+ applyContextFormatting(str) {
1304
+ if (!this.data.acf) return;
1305
+ const user = this.getContextUser();
1306
+ const guildMember = user instanceof import_discord5.GuildMember ? user : null;
1307
+ const actualUser = guildMember?.user || (user instanceof import_discord5.User ? user : null);
1308
+ const client = this.getContextClient();
1309
+ const formatString = (str2) => {
1310
+ if (!str2 || !str2.includes("$")) return str2;
1311
+ return str2.replace(/(?<!\\)\$USER\b/g, actualUser?.toString() || "$USER").replace(/(?<!\\)\$USER_NAME\b/g, actualUser?.username || "$USER_NAME").replace(/(?<!\\)\$USER_AVATAR\b/g, actualUser?.avatarURL() || "$USER_AVATAR").replace(/(?<!\\)\$DISPLAY_NAME\b/g, guildMember?.displayName || "$DISPLAY_NAME").replace(/(?<!\\)\$BOT_AVATAR\b/g, client?.user?.avatarURL() || "$BOT_AVATAR").replace(/(?<!\\)\$INVIS\b/g, "\u200B").replace(/(?<!\\)\$YEAR/g, (/* @__PURE__ */ new Date()).getFullYear().toString()).replace(/(?<!\\)\$MONTH/g, String((/* @__PURE__ */ new Date()).getMonth() + 1).padStart(2, "0")).replace(/(?<!\\)\$DAY/g, String((/* @__PURE__ */ new Date()).getDate()).padStart(2, "0")).replace(/(?<!\\)\$year/g, String((/* @__PURE__ */ new Date()).getFullYear()).slice(-2)).replace(/(?<!\\)\$month/g, String((/* @__PURE__ */ new Date()).getMonth() + 1).padStart(2, "0")).replace(/(?<!\\)\$day/g, String((/* @__PURE__ */ new Date()).getDate()).padStart(2, "0")).replace(/(?<!\\|<)@([0-9]+)(?!>)/g, "<@$1>").replace(/(?<!\\|<)@&([0-9]+)(?!>)/g, "<@&$1>").replace(/(?<!\\|<)#([0-9]+)(?!>)/g, "<#$1>");
1312
+ };
1313
+ if (str) {
1314
+ return formatString(str);
1192
1315
  }
1193
- }
1194
- loader(message) {
1195
- const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
1196
- let i = 0;
1197
- const interval = setInterval(() => {
1198
- process.stdout.write(
1199
- `\r${this.formatTimestamp()} ${this.formatPrefix()} ${import_chalk.default.hex(this.colorScheme.warn)(frames[i])} ${message}`
1200
- );
1201
- i = (i + 1) % frames.length;
1202
- }, 100);
1203
- return (newMessage) => {
1204
- clearInterval(interval);
1205
- process.stdout.write(
1206
- `\r${this.formatTimestamp()} ${this.formatPrefix()} ${import_chalk.default.hex(this.colorScheme.success)("\u2713")} ${newMessage || message}
1207
- `
1316
+ if (this.data.author && typeof this.data.author === "object") {
1317
+ this.data.author.text = formatString(this.data.author.text);
1318
+ if (this.data.author.icon === true && actualUser) {
1319
+ this.data.author.icon = actualUser.avatarURL();
1320
+ } else if (typeof this.data.author.icon === "string") {
1321
+ this.data.author.icon = formatString(this.data.author.icon);
1322
+ }
1323
+ }
1324
+ if (this.data.title && typeof this.data.title === "object") {
1325
+ this.data.title.text = formatString(this.data.title.text);
1326
+ }
1327
+ if (this.data.description) {
1328
+ this.data.description = formatString(
1329
+ Array.isArray(this.data.description) ? this.data.description.filter((s) => s !== null && s !== void 0).join("\n") : this.data.description
1208
1330
  );
1209
- };
1210
- }
1211
- table(title, data) {
1212
- console.log(this.formatTimestamp(), this.formatPrefix(), import_chalk.default.bold(title));
1213
- Object.entries(data).forEach(([key, value]) => {
1214
- const formattedKey = import_chalk.default.hex(this.colorScheme.warn)(` ${key}`);
1215
- const formattedValue = import_chalk.default.hex(this.colorScheme.muted)(value);
1216
- console.log(`${formattedKey.padEnd(25)} ${formattedValue}`);
1217
- });
1218
- }
1219
- section(title) {
1220
- const line = "\u2500".repeat(Math.max(30, title.length + 4));
1221
- console.log(import_chalk.default.hex(this.colorScheme.muted)(`
1222
- \u250C\u2500${line}\u2500\u2510`));
1223
- console.log(
1224
- import_chalk.default.hex(this.colorScheme.muted)("\u2502 ") + import_chalk.default.bold.hex(this.colorScheme.text)(title.padEnd(line.length)) + import_chalk.default.hex(this.colorScheme.muted)(" \u2502")
1225
- );
1226
- console.log(import_chalk.default.hex(this.colorScheme.muted)(`\u2514\u2500${line}\u2500\u2518`));
1331
+ }
1332
+ if (this.data.footer && typeof this.data.footer === "object") {
1333
+ this.data.footer.text = formatString(this.data.footer.text);
1334
+ }
1335
+ if (this.data.thumbnailUrl) {
1336
+ this.data.thumbnailUrl = formatString(this.data.thumbnailUrl);
1337
+ }
1338
+ if (this.data.imageUrl) {
1339
+ this.data.imageUrl = formatString(this.data.imageUrl);
1340
+ }
1341
+ this.data.fields = this.data.fields.filter(Boolean).map((field) => ({
1342
+ ...field,
1343
+ name: formatString(field.name),
1344
+ value: formatString(field.value)
1345
+ }));
1227
1346
  }
1228
- };
1229
- var logger = new Logger();
1230
-
1231
- // src/tools/utils.ts
1232
- function __zero(str) {
1233
- return str?.length ? str : "0";
1234
- }
1235
- function isMentionOrSnowflake(str) {
1236
- return str ? str.match(/<@[#&]?[\d]{6,}>/) || str.match(/\d{6,}/) ? true : false : false;
1237
- }
1238
- function cleanMention(str) {
1239
- return str ? str.replaceAll(/[<@#&>]/g, "").trim() : void 0;
1240
- }
1241
- async function getMessageMention(message, content, type, index = 0, idOnly) {
1242
- const args = content?.split(" ");
1243
- const arg = isMentionOrSnowflake(args?.[index]) ? cleanMention(args?.[index]) : void 0;
1244
- switch (type) {
1245
- case "user":
1246
- const userMention2 = message.mentions.users.at(index) || null;
1247
- if (!userMention2 && arg) {
1248
- return idOnly ? arg : await fetchUser(message.client, arg);
1249
- } else {
1250
- return idOnly ? userMention2?.id || null : userMention2;
1347
+ configureEmbed() {
1348
+ if (this.data.author && typeof this.data.author === "object" && this.data.author.text) {
1349
+ try {
1350
+ this.embed.setAuthor({
1351
+ name: this.data.author.text,
1352
+ iconURL: typeof this.data.author.icon === "string" ? this.data.author.icon : void 0,
1353
+ url: this.data.author.hyperlink || void 0
1354
+ });
1355
+ } catch (error) {
1356
+ console.error("[BetterEmbed] Invalid author configuration:", error);
1251
1357
  }
1252
- case "member":
1253
- if (!message.guild) return null;
1254
- const member = await fetchMember(message.guild, message.mentions.users.at(index)?.id ?? arg);
1255
- return idOnly ? member?.id || null : member;
1256
- case "channel":
1257
- const channelMention = message.mentions.channels.at(index) || null;
1258
- if (!channelMention && arg) {
1259
- return idOnly ? arg : message.guild ? await fetchChannel(message.guild, arg) : message.client.channels.cache.get(__zero(arg)) ?? message.client.channels.fetch(__zero(arg));
1260
- } else {
1261
- return idOnly ? channelMention?.id || null : channelMention;
1358
+ }
1359
+ if (this.data.title && typeof this.data.title === "object" && this.data.title.text) {
1360
+ try {
1361
+ this.embed.setTitle(this.data.title.text);
1362
+ if (this.data.title.hyperlink) {
1363
+ this.embed.setURL(this.data.title.hyperlink);
1364
+ }
1365
+ } catch (error) {
1366
+ console.error("[BetterEmbed] Invalid title configuration:", error);
1262
1367
  }
1263
- case "role":
1264
- const roleMention = message.mentions.roles.at(index) || null;
1265
- if (!roleMention && arg) {
1266
- return idOnly ? arg : message.guild ? await fetchRole(message.guild, arg) : null;
1267
- } else {
1268
- return idOnly ? roleMention?.id || null : roleMention;
1368
+ }
1369
+ if (this.data.description) {
1370
+ this.embed.setDescription(
1371
+ Array.isArray(this.data.description) ? this.data.description.join("\n") : this.data.description
1372
+ );
1373
+ }
1374
+ if (this.data.thumbnailUrl) {
1375
+ try {
1376
+ this.embed.setThumbnail(this.data.thumbnailUrl);
1377
+ } catch (error) {
1378
+ console.error("[BetterEmbed] Invalid thumbnail URL:", error);
1269
1379
  }
1270
- default:
1271
- return null;
1272
- }
1273
- }
1274
- function getFirstMentionId(options) {
1275
- let mentionId = "";
1276
- if (options.message) {
1277
- switch (options.type) {
1278
- case "user":
1279
- mentionId = options.message.mentions.users.first()?.id || "";
1280
- break;
1281
- case "channel":
1282
- mentionId = options.message.mentions.channels.first()?.id || "";
1283
- break;
1284
- case "role":
1285
- mentionId = options.message.mentions.roles.first()?.id || "";
1286
- break;
1287
1380
  }
1288
- }
1289
- const firstArg = options.content?.split(" ")[0] || "";
1290
- return mentionId || isMentionOrSnowflake(firstArg) ? cleanMention(firstArg) : "";
1291
- }
1292
- async function fetchUser(client, userId) {
1293
- if (!userId) return null;
1294
- return client.users.cache.get(__zero(userId)) || await client.users.fetch(__zero(userId)).catch(() => null);
1295
- }
1296
- async function fetchGuild(client, guildId) {
1297
- if (!guildId) return null;
1298
- return client.guilds.cache.get(__zero(guildId)) || await client.guilds.fetch(__zero(guildId)).catch(() => null);
1299
- }
1300
- async function fetchMember(guild, memberId) {
1301
- if (!memberId) return null;
1302
- return guild.members.cache.get(__zero(memberId)) || await guild.members.fetch(__zero(memberId)).catch(() => null);
1303
- }
1304
- async function fetchChannel(guild, channelId, type) {
1305
- if (!channelId) return null;
1306
- const channel = guild.channels.cache.get(__zero(channelId)) || await guild.channels.fetch(__zero(channelId)).catch(() => null);
1307
- if (type && channel?.type !== type) return null;
1308
- return channel;
1309
- }
1310
- async function fetchRole(guild, roleId) {
1311
- if (!roleId) return null;
1312
- return guild.roles.cache.get(__zero(roleId)) || await guild.roles.fetch(__zero(roleId)).catch(() => null) || null;
1313
- }
1314
- async function fetchMessage(channel, messageId) {
1315
- if (!messageId) return null;
1316
- return channel.messages.cache.get(__zero(messageId)) || await channel.messages.fetch(__zero(messageId)).catch(() => null) || null;
1317
- }
1318
-
1319
- // src/types/status.ts
1320
- var import_discord4 = require("discord.js");
1321
- var import_lodash11 = __toESM(require("lodash"));
1322
- var StatusType = /* @__PURE__ */ ((StatusType2) => {
1323
- StatusType2["DND"] = "dnd";
1324
- StatusType2["Idle"] = "idle";
1325
- StatusType2["Online"] = "online";
1326
- StatusType2["Invisible"] = "invisible";
1327
- return StatusType2;
1328
- })(StatusType || {});
1329
- var defaultPresence = {
1330
- production: {
1331
- interval: 6e4,
1332
- randomize: false,
1333
- activity: [
1334
- { status: "online" /* Online */, type: import_discord4.ActivityType.Custom, name: "Need help? Use /help or !help" },
1335
- { status: "online" /* Online */, type: import_discord4.ActivityType.Custom, name: "Join our community!" },
1336
- { status: "online" /* Online */, type: import_discord4.ActivityType.Watching, name: "\u2728 $GUILD_COUNT servers" }
1337
- ]
1338
- },
1339
- development: {
1340
- activity: { status: "dnd" /* DND */, type: import_discord4.ActivityType.Custom, name: "In development!" }
1341
- }
1342
- };
1343
- function createVimcordStatusConfig(options = {}) {
1344
- return import_lodash11.default.merge(defaultPresence, options);
1345
- }
1346
-
1347
- // src/utils/number.ts
1348
- function formatThousands(num, sep = ",") {
1349
- return `${num}`.replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, sep);
1350
- }
1351
-
1352
- // src/modules/status.manager.ts
1353
- var import_node_events = __toESM(require("events"));
1354
- var import_qznt2 = require("qznt");
1355
- var StatusManager = class {
1356
- client;
1357
- logger;
1358
- emitter = new import_node_events.default();
1359
- lastActivity = null;
1360
- lastActivityIndex = 0;
1361
- task = null;
1362
- constructor(client) {
1363
- this.client = client;
1364
- this.logger = new Logger({ prefixEmoji: "\u{1F4AC}", prefix: `StatusManager (i${this.client.clientId})` });
1365
- this.emitter.on("changed", (activity) => {
1366
- if (this.client.config.app.verbose) {
1367
- this.logger.debug(`Status changed to '${activity.name}'`);
1381
+ if (this.data.imageUrl) {
1382
+ try {
1383
+ this.embed.setImage(this.data.imageUrl);
1384
+ } catch (error) {
1385
+ console.error("[BetterEmbed] Invalid image URL:", error);
1368
1386
  }
1369
- });
1370
- this.emitter.on("cleared", () => {
1371
- if (this.client.config.app.verbose) {
1372
- this.logger.debug("Status cleared");
1387
+ }
1388
+ if (this.data.footer && typeof this.data.footer === "object" && this.data.footer.text) {
1389
+ try {
1390
+ this.embed.setFooter({
1391
+ text: this.data.footer.text,
1392
+ iconURL: typeof this.data.footer.icon === "string" ? this.data.footer.icon : void 0
1393
+ });
1394
+ } catch (error) {
1395
+ console.error("[BetterEmbed] Invalid footer configuration:", error);
1373
1396
  }
1374
- });
1397
+ }
1398
+ if (this.data.color) {
1399
+ try {
1400
+ const color = Array.isArray(this.data.color) ? this.data.color[Math.floor(Math.random() * this.data.color.length)] ?? null : this.data.color;
1401
+ this.embed.setColor(color);
1402
+ } catch (error) {
1403
+ console.error("[BetterEmbed] Invalid color:", error);
1404
+ }
1405
+ }
1406
+ if (this.data.timestamp && this.data.timestamp !== true) {
1407
+ try {
1408
+ this.embed.setTimestamp(this.data.timestamp);
1409
+ } catch (error) {
1410
+ console.error("[BetterEmbed] Invalid timestamp:", error);
1411
+ }
1412
+ }
1413
+ if (this.data.fields.length > 0) {
1414
+ const validFields = this.data.fields.slice(0, 25);
1415
+ if (this.data.fields.length > 25) {
1416
+ console.warn("[BetterEmbed] Only first 25 fields will be used (Discord limit)");
1417
+ }
1418
+ this.embed.setFields(validFields);
1419
+ }
1375
1420
  }
1376
- clearData() {
1377
- this.task?.stop();
1378
- this.task = null;
1379
- this.lastActivity = null;
1380
- this.lastActivityIndex = 0;
1421
+ setAuthor(author) {
1422
+ this.data.author = author;
1423
+ this.build();
1381
1424
  return this;
1382
1425
  }
1383
- async getReadyClient() {
1384
- const client = await this.client.whenReady();
1385
- if (!client.user) throw new Error("Cannot manage the client's activity when its user is not hydrated");
1386
- return client;
1387
- }
1388
- async formatActivityName(name) {
1389
- name = name.replace("$USER_COUNT", formatThousands(this.client.users.cache.size)).replace("$GUILD_COUNT", formatThousands(this.client.guilds.cache.size)).replace(
1390
- "$INVITE",
1391
- this.client.config.staff.guild.inviteUrl ? this.client.config.staff.guild.inviteUrl : "<STAFF_INVITE_URL_NOT_SET>"
1392
- );
1393
- if (name.includes("$STAFF_GUILD_MEMBER_COUNT")) {
1394
- await fetchGuild(this.client, this.client.config.staff.guild.id).then((guild) => {
1395
- if (!guild) return name = name.replace("$STAFF_GUILD_MEMBER_COUNT", "<STAFF_GUILD_NOT_FOUND>");
1396
- name = name.replace("$STAFF_GUILD_MEMBER_COUNT", formatThousands(guild.members.cache.size));
1397
- }).catch((err) => this.logger.error("Failed to fetch the staff guild", err));
1398
- }
1399
- return name;
1426
+ setTitle(title) {
1427
+ this.data.title = title;
1428
+ this.build();
1429
+ return this;
1400
1430
  }
1401
- async setActivity(activity) {
1402
- const client = await this.getReadyClient();
1403
- activity.name = await this.formatActivityName(activity.name);
1404
- client.user.setStatus(activity.status);
1405
- client.user.setActivity({ name: activity.name, type: activity.type, url: activity.streamUrl });
1406
- this.emitter.emit("changed", activity);
1431
+ setDescription(description) {
1432
+ this.data.description = description;
1433
+ this.build();
1434
+ return this;
1407
1435
  }
1408
- async statusRotationTask(clientStatus) {
1409
- let activity;
1410
- if (clientStatus.randomize && Array.isArray(clientStatus.activity)) {
1411
- activity = import_qznt2.$.rnd.choice(clientStatus.activity, { not: this.lastActivity });
1412
- this.lastActivity = activity;
1413
- } else {
1414
- const activityIndex = (this.lastActivityIndex + 1) % clientStatus.activity.length;
1415
- this.lastActivityIndex = activityIndex;
1416
- activity = clientStatus.activity[activityIndex];
1417
- }
1418
- await this.setActivity(activity);
1419
- this.emitter.emit("rotation", activity);
1436
+ setThumbnail(url) {
1437
+ this.data.thumbnailUrl = url;
1438
+ this.build();
1439
+ return this;
1420
1440
  }
1421
- async scheduleStatusRotation(clientStatus) {
1422
- if (!clientStatus.interval) throw new Error("Cannot create client activity interval without interval time");
1423
- this.task?.stop();
1424
- this.task = null;
1425
- this.task = new import_qznt2.$.Loop(() => this.statusRotationTask(clientStatus), import_qznt2.$.math.ms(clientStatus.interval), true);
1426
- this.start();
1441
+ setImage(url) {
1442
+ this.data.imageUrl = url;
1443
+ this.build();
1444
+ return this;
1427
1445
  }
1428
- start() {
1429
- if (this.task) {
1430
- this.task.start();
1431
- this.emitter.emit("started", this.task);
1432
- }
1446
+ setFooter(footer) {
1447
+ this.data.footer = footer;
1448
+ this.build();
1433
1449
  return this;
1434
1450
  }
1435
- pause() {
1436
- if (this.task) {
1437
- this.task.stop();
1438
- this.emitter.emit("paused", this.task);
1439
- }
1451
+ setColor(color) {
1452
+ this.data.color = color;
1453
+ this.build();
1440
1454
  return this;
1441
1455
  }
1442
- async set(status) {
1443
- const statusConfig = createVimcordStatusConfig(status);
1444
- let clientStatus;
1445
- if (this.client.config.app.devMode) {
1446
- clientStatus = statusConfig.development;
1447
- } else {
1448
- clientStatus = statusConfig.production;
1449
- }
1450
- if (!clientStatus.interval) {
1451
- await this.setActivity(Array.isArray(clientStatus.activity) ? clientStatus.activity[0] : clientStatus.activity);
1452
- } else {
1453
- await this.scheduleStatusRotation(clientStatus);
1454
- }
1456
+ setTimestamp(timestamp) {
1457
+ this.data.timestamp = timestamp;
1458
+ this.build();
1459
+ return this;
1460
+ }
1461
+ addFields(fields) {
1462
+ this.data.fields = [...this.data.fields, ...fields];
1463
+ this.build();
1455
1464
  return this;
1456
1465
  }
1457
- async destroy() {
1458
- if (this.task) {
1459
- this.task.stop();
1460
- this.task = null;
1461
- this.emitter.emit("destroyed");
1462
- await this.clear();
1463
- }
1466
+ setFields(fields) {
1467
+ this.data.fields = fields;
1468
+ this.build();
1464
1469
  return this;
1465
1470
  }
1466
- async clear() {
1467
- const client = await this.getReadyClient();
1468
- this.clearData();
1469
- client.user.setActivity({ name: "" });
1470
- this.emitter.emit("cleared");
1471
+ spliceFields(index, deleteCount, ...fields) {
1472
+ this.data.fields.splice(index, deleteCount, ...fields);
1473
+ this.build();
1471
1474
  return this;
1472
1475
  }
1476
+ clone(overrides = {}) {
1477
+ return new _BetterEmbed({ ...this.data, ...overrides });
1478
+ }
1479
+ toJSON() {
1480
+ return this.embed.toJSON();
1481
+ }
1482
+ async send(handler, options = {}, overrides) {
1483
+ this.build();
1484
+ if (options.content && this.data.acf) {
1485
+ options.content = this.applyContextFormatting(options.content);
1486
+ }
1487
+ return await dynaSend(handler, {
1488
+ ...options,
1489
+ embeds: [
1490
+ overrides ? this.clone(overrides) : this,
1491
+ ...Array.isArray(options?.embeds) ? options?.embeds : options?.embeds ? [options?.embeds] : []
1492
+ ]
1493
+ });
1494
+ }
1473
1495
  };
1474
1496
 
1475
- // src/modules/command.manager.ts
1476
- var import_discord5 = require("discord.js");
1477
- var BaseCommandManager = class {
1478
- type;
1479
- client;
1480
- commands = /* @__PURE__ */ new Map();
1481
- moduleSuffix;
1482
- constructor(client, type, moduleSuffix) {
1483
- this.type = type;
1484
- this.client = client;
1485
- this.moduleSuffix = moduleSuffix;
1486
- }
1487
- /**
1488
- * Gets a command by name.
1489
- */
1490
- get(name) {
1491
- if (this.type === 1 /* Prefix */) {
1492
- const config = this.client.config.prefixCommands;
1493
- const search = config.allowCaseInsensitiveCommandNames ? name.toLowerCase() : name;
1494
- return Array.from(this.commands.values()).find((cmd) => {
1495
- const commandName = "builder" in cmd ? cmd.builder.name : cmd.options.name;
1496
- const trigger = config.allowCaseInsensitiveCommandNames ? commandName.toLowerCase() : commandName;
1497
- if (trigger === search) return true;
1498
- if ("aliases" in cmd.options) {
1499
- return cmd.options.aliases?.some(
1500
- (a) => config.allowCaseInsensitiveCommandNames ? a.toLowerCase() === search : a === search
1501
- );
1502
- }
1503
- });
1504
- } else {
1505
- return this.commands.get(name);
1497
+ // src/utils/sendCommandErrorEmbed.ts
1498
+ async function sendCommandErrorEmbed(client, error, guild, messageOrInteraction) {
1499
+ if (!client.features.enableCommandErrorMessage) return null;
1500
+ const config = typeof client.features.enableCommandErrorMessage !== "boolean" ? client.features.enableCommandErrorMessage : void 0;
1501
+ const buttons = {
1502
+ supportServer: new import_discord6.ButtonBuilder({
1503
+ url: config?.inviteUrl || client.config.staff.guild.inviteUrl || "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
1504
+ // may or may not be a rickroll
1505
+ label: config?.inviteButtonLabel || "Support Support",
1506
+ style: import_discord6.ButtonStyle.Link
1507
+ }),
1508
+ details: new import_discord6.ButtonBuilder({
1509
+ customId: "btn_details",
1510
+ label: config?.detailButtonLabel || "Details",
1511
+ style: import_discord6.ButtonStyle.Secondary
1512
+ })
1513
+ };
1514
+ const actionRow = new import_discord6.ActionRowBuilder({
1515
+ components: config?.inviteUrl && guild?.id !== (config.inviteUrl || client.config.staff.guild.id) ? [buttons.supportServer, buttons.details] : [buttons.details]
1516
+ });
1517
+ const embed_error = config?.embed?.(new BetterEmbed(), error, guild) || new BetterEmbed({
1518
+ color: "Red",
1519
+ title: "Something went wrong",
1520
+ description: "If you keep encountering this error, please report it."
1521
+ });
1522
+ const msg = await embed_error.send(messageOrInteraction, {
1523
+ components: [actionRow],
1524
+ flags: config?.ephemeral ? "Ephemeral" : void 0,
1525
+ deleteAfter: config?.deleteAfter
1526
+ });
1527
+ if (!msg) return null;
1528
+ const collector = msg.createMessageComponentCollector({
1529
+ componentType: import_discord6.ComponentType.Button,
1530
+ idle: config?.detailButtonIdleTimeout ?? 3e4,
1531
+ filter: (i) => i.customId === "btn_details"
1532
+ });
1533
+ collector.on("collect", (i) => {
1534
+ const attachment = new import_discord6.AttachmentBuilder(Buffer.from(`${error.message}
1535
+
1536
+ ${error.stack}`), {
1537
+ name: "error.txt"
1538
+ });
1539
+ i.reply({ files: [attachment], flags: "Ephemeral" });
1540
+ });
1541
+ collector.on("end", () => {
1542
+ buttons.details.setDisabled(true);
1543
+ embed_error.send(messageOrInteraction, {
1544
+ sendMethod: messageOrInteraction instanceof import_discord6.Message ? 5 /* MessageEdit */ : void 0,
1545
+ components: [actionRow]
1546
+ });
1547
+ });
1548
+ return msg;
1549
+ }
1550
+
1551
+ // src/modules/builtins/builtin.slashCommandHandler.ts
1552
+ var BUILTIN_SlashCommandHandler = new EventBuilder({
1553
+ event: "interactionCreate",
1554
+ name: "SlashCommandHandler",
1555
+ async execute(client, interaction) {
1556
+ if (!interaction.isChatInputCommand()) return;
1557
+ const command = client.commands.slash.get(interaction.commandName);
1558
+ if (!command) {
1559
+ const content = `**/\`${interaction.commandName}\`** is not a registered command.`;
1560
+ if (interaction.replied || interaction.deferred) {
1561
+ return interaction.followUp({ content, flags: "Ephemeral" });
1562
+ }
1563
+ return interaction.reply({ content, flags: "Ephemeral" });
1564
+ }
1565
+ try {
1566
+ return await command.run(client, client, interaction);
1567
+ } catch (err) {
1568
+ await sendCommandErrorEmbed(client, err, interaction.guild, interaction);
1569
+ throw err;
1506
1570
  }
1507
1571
  }
1508
- /**
1509
- * Gets/filters commands and orders them alphabetically.
1510
- */
1511
- getAll(options = {}) {
1512
- const matchedCommands = /* @__PURE__ */ new Map();
1513
- const isDev = this.client.config.app.devMode;
1514
- for (const cmd of this.commands.values()) {
1515
- const commandName = "builder" in cmd ? cmd.builder.name : cmd.options.name;
1516
- if (options.names || options.fuzzyNames) {
1517
- const nameMatched = options.names?.includes(commandName) || options.fuzzyNames?.some((fuzzy) => commandName.includes(fuzzy));
1518
- if (!nameMatched) continue;
1572
+ });
1573
+
1574
+ // src/modules/builtins/builtin.prefixCommandHandler.ts
1575
+ var import_discord7 = require("discord.js");
1576
+ var BUILTIN_PrefixCommandHandler = new EventBuilder({
1577
+ event: "messageCreate",
1578
+ name: "PrefixCommandHandler",
1579
+ async execute(client, message) {
1580
+ if (message.author.bot || !message.guild) return;
1581
+ const config = client.config.prefixCommands;
1582
+ let activePrefix = config.defaultPrefix;
1583
+ if (config.guildPrefixResolver) {
1584
+ try {
1585
+ const customPrefix = await config.guildPrefixResolver(client, message.guild.id);
1586
+ if (customPrefix) activePrefix = customPrefix;
1587
+ } catch (err) {
1588
+ client.logger.error(`Error in guildPrefixResolver for guild ${message.guild.id}:`, err);
1519
1589
  }
1520
- if (options.ignoreDeploymentOptions) {
1521
- matchedCommands.set(commandName, cmd);
1522
- continue;
1590
+ }
1591
+ let prefixUsed;
1592
+ if (message.content.startsWith(activePrefix)) {
1593
+ prefixUsed = activePrefix;
1594
+ } else if (config.allowMentionAsPrefix) {
1595
+ const mention = (0, import_discord7.userMention)(client.user.id);
1596
+ if (message.content.startsWith(mention)) {
1597
+ prefixUsed = message.content.startsWith(`${mention} `) ? `${mention} ` : mention;
1523
1598
  }
1524
- const deployment = "deployment" in cmd.options ? cmd.options.deployment ?? {} : {};
1525
- const isProperEnv = !deployment.environments || deployment.environments.includes(isDev ? "development" : "production");
1526
- if (!isProperEnv) continue;
1527
- if (options.globalOnly && deployment.global === false) continue;
1528
- matchedCommands.set(commandName, cmd);
1529
1599
  }
1530
- return Array.from(matchedCommands.values()).sort((a, b) => {
1531
- const commandNameA = "builder" in a ? a.builder.name : a.options.name;
1532
- const commandNameB = "builder" in b ? b.builder.name : b.options.name;
1533
- return commandNameA.localeCompare(commandNameB);
1600
+ if (!prefixUsed) return;
1601
+ const contentWithoutPrefix = message.content.slice(prefixUsed.length).trim();
1602
+ const args = contentWithoutPrefix.split(/\s+/);
1603
+ const trigger = args.shift();
1604
+ if (!trigger) return;
1605
+ const command = client.commands.prefix.get(trigger);
1606
+ if (!command) return;
1607
+ message.content = args.join(" ");
1608
+ try {
1609
+ return await command.run(client, client, message);
1610
+ } catch (err) {
1611
+ await sendCommandErrorEmbed(client, err, message.guild, message);
1612
+ throw err;
1613
+ }
1614
+ }
1615
+ });
1616
+
1617
+ // src/modules/builtins/builtin.contextCommandHandler.ts
1618
+ var BUILTIN_ContextCommandHandler = new EventBuilder({
1619
+ event: "interactionCreate",
1620
+ name: "ContextCommandHandler",
1621
+ async execute(client, interaction) {
1622
+ if (!interaction.isContextMenuCommand()) return;
1623
+ const command = client.commands.context.get(interaction.commandName);
1624
+ if (!command) {
1625
+ const content = `**${interaction.commandName}** is not a registered context command.`;
1626
+ if (interaction.replied || interaction.deferred) {
1627
+ return interaction.followUp({ content, flags: "Ephemeral" });
1628
+ }
1629
+ return interaction.reply({ content, flags: "Ephemeral" });
1630
+ }
1631
+ try {
1632
+ return await command.run(client, client, interaction);
1633
+ } catch (err) {
1634
+ await sendCommandErrorEmbed(client, err, interaction.guild, interaction);
1635
+ throw err;
1636
+ }
1637
+ }
1638
+ });
1639
+
1640
+ // src/tools/Logger.ts
1641
+ var import_chalk = __toESM(require("chalk"));
1642
+ var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
1643
+ LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
1644
+ LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
1645
+ LogLevel2[LogLevel2["SUCCESS"] = 2] = "SUCCESS";
1646
+ LogLevel2[LogLevel2["WARN"] = 3] = "WARN";
1647
+ LogLevel2[LogLevel2["ERROR"] = 4] = "ERROR";
1648
+ return LogLevel2;
1649
+ })(LogLevel || {});
1650
+ var LOGGER_COLORS = {
1651
+ primary: "#5865F2",
1652
+ success: "#57F287",
1653
+ warn: "#FEE75C",
1654
+ danger: "#ED4245",
1655
+ muted: "#747F8D",
1656
+ text: "#FFFFFF"
1657
+ };
1658
+ var Logger = class {
1659
+ logPrefixEmoji;
1660
+ logPrefix;
1661
+ minLevel;
1662
+ showTimestamp;
1663
+ colorScheme;
1664
+ constructor(options) {
1665
+ const { prefixEmoji = null, prefix = null, minLevel = 0 /* DEBUG */, showTimestamp = true } = options || {};
1666
+ this.logPrefixEmoji = prefixEmoji;
1667
+ this.logPrefix = prefix;
1668
+ this.minLevel = minLevel;
1669
+ this.showTimestamp = showTimestamp;
1670
+ this.colorScheme = {
1671
+ ...LOGGER_COLORS,
1672
+ ...options?.colors
1673
+ };
1674
+ }
1675
+ formatTimestamp() {
1676
+ if (!this.showTimestamp) return "";
1677
+ const now = /* @__PURE__ */ new Date();
1678
+ const time = now.toLocaleTimeString("en-US", {
1679
+ hour12: false,
1680
+ hour: "2-digit",
1681
+ minute: "2-digit",
1682
+ second: "2-digit"
1534
1683
  });
1684
+ return import_chalk.default.hex(this.colorScheme.muted)(`[${time}]`);
1535
1685
  }
1536
- /**
1537
- * Groups commands by category alphabetically.
1538
- */
1539
- sortByCategory() {
1540
- const categories = /* @__PURE__ */ new Map();
1541
- for (const cmd of this.commands.values()) {
1542
- const metadata = cmd.options.metadata;
1543
- if (!metadata?.category) continue;
1544
- let entry = categories.get(metadata.category);
1545
- if (!entry) {
1546
- entry = {
1547
- name: metadata.category,
1548
- emoji: metadata.categoryEmoji,
1549
- commands: []
1686
+ formatPrefix() {
1687
+ if (!this.logPrefix) return "";
1688
+ return import_chalk.default.bold.hex(this.colorScheme.primary)(
1689
+ `${this.logPrefixEmoji ? `${this.logPrefixEmoji} ` : ""}${this.logPrefix}`
1690
+ );
1691
+ }
1692
+ shouldLog(level) {
1693
+ return level >= this.minLevel;
1694
+ }
1695
+ get prefixEmoji() {
1696
+ return this.logPrefixEmoji;
1697
+ }
1698
+ get prefix() {
1699
+ return this.logPrefix;
1700
+ }
1701
+ get colors() {
1702
+ return this.colorScheme;
1703
+ }
1704
+ extend(extras) {
1705
+ for (const [key, fn] of Object.entries(extras)) {
1706
+ if (typeof fn === "function") {
1707
+ this[key] = function(...args) {
1708
+ return fn.call(this, ...args);
1550
1709
  };
1551
- categories.set(metadata.category, entry);
1552
1710
  }
1553
- entry.commands.push(cmd);
1554
1711
  }
1555
- return Array.from(categories.values()).sort((a, b) => a.name.localeCompare(b.name)).map((cat) => {
1556
- cat.commands.sort((a, b) => {
1557
- const commandNameA = "builder" in a ? a.builder.name : a.options.name;
1558
- const commandNameB = "builder" in b ? b.builder.name : b.options.name;
1559
- return commandNameA.localeCompare(commandNameB);
1560
- });
1561
- return cat;
1562
- });
1712
+ return this;
1563
1713
  }
1564
- /**
1565
- * Imports command modules from a directory.
1566
- * @param dir Path of one or more folders.
1567
- * @param set Replaces imported command modules with the ones found.
1568
- */
1569
- async importFrom(dir, set = false) {
1570
- if (set) this.commands.clear();
1571
- const dirs = Array.isArray(dir) ? dir : [dir];
1572
- const modules = [];
1573
- for (const _dir of dirs) {
1574
- const results = await importModulesFromDir(_dir, this.moduleSuffix);
1575
- modules.push(...results.map(({ module: module2 }) => module2.default));
1576
- }
1577
- for (const module2 of modules) {
1578
- const commandName = "builder" in module2 ? module2.builder.name : module2.options.name;
1579
- this.commands.set(commandName, module2);
1580
- }
1581
- let moduleType;
1582
- switch (this.type) {
1583
- case 0 /* Slash */:
1584
- moduleType = "Prefix Commands";
1585
- break;
1586
- case 2 /* Context */:
1587
- moduleType = "Context Commands";
1588
- break;
1589
- case 1 /* Prefix */:
1590
- moduleType = "Prefix Commands";
1591
- break;
1592
- }
1593
- this.client.logger.moduleLoaded(moduleType, modules.length);
1594
- return this.commands;
1714
+ setPrefix(prefix) {
1715
+ this.logPrefix = prefix;
1716
+ return this;
1595
1717
  }
1596
- };
1597
- var SlashCommandManager = class extends BaseCommandManager {
1598
- constructor(client) {
1599
- super(client, 0 /* Slash */, client.config.app.moduleSuffixes.slashCommand);
1718
+ setPrefixEmoji(prefixEmoji) {
1719
+ this.logPrefixEmoji = prefixEmoji;
1720
+ return this;
1600
1721
  }
1601
- };
1602
- var ContextCommandManager = class extends BaseCommandManager {
1603
- constructor(client) {
1604
- super(client, 2 /* Context */, client.config.app.moduleSuffixes.contextCommand);
1722
+ setMinLevel(minLevel) {
1723
+ this.minLevel = minLevel;
1724
+ return this;
1605
1725
  }
1606
- };
1607
- var PrefixCommandManager = class extends BaseCommandManager {
1608
- constructor(client) {
1609
- super(client, 1 /* Prefix */, client.config.app.moduleSuffixes.prefixCommand);
1726
+ setShowTimestamp(show) {
1727
+ this.showTimestamp = show;
1728
+ return this;
1610
1729
  }
1611
- };
1612
- var CommandManager = class {
1613
- client;
1614
- slash;
1615
- prefix;
1616
- context;
1617
- constructor(client) {
1618
- this.client = client;
1619
- this.slash = new SlashCommandManager(client);
1620
- this.prefix = new PrefixCommandManager(client);
1621
- this.context = new ContextCommandManager(client);
1730
+ setColors(colors) {
1731
+ this.colorScheme = {
1732
+ ...LOGGER_COLORS,
1733
+ ...colors
1734
+ };
1735
+ return this;
1622
1736
  }
1623
- getAllAppCommands(options = {}) {
1624
- return [...this.slash.getAll(options), ...this.context.getAll(options)];
1737
+ log(message, ...args) {
1738
+ console.log(this.formatTimestamp(), this.formatPrefix(), message, ...args);
1625
1739
  }
1626
- async registerGlobal(options = {}) {
1627
- const client = await this.client.whenReady();
1628
- if (!client.rest) {
1629
- console.error(`[CommandManager] \u2716 Failed to register app commands globally: REST is not initialized`);
1630
- return;
1631
- }
1632
- const commands = this.getAllAppCommands(options);
1633
- if (!commands.length) {
1634
- console.log("[CommandManager] No commands to register globally");
1635
- return;
1636
- }
1637
- console.log(`[CommandManager] Registering (${commands.length}) commands globally...`);
1638
- try {
1639
- await client.rest.put(import_discord5.Routes.applicationCommands(client.user.id), { body: commands });
1640
- console.log(`[CommandManager] \u2714 Registered app commands globally`);
1641
- } catch (err) {
1642
- console.error(`[CommandManager] \u2716 Failed to register app commands globally`, err);
1643
- }
1740
+ debug(message, ...args) {
1741
+ if (!this.shouldLog(0 /* DEBUG */)) return;
1742
+ console.log(
1743
+ this.formatTimestamp(),
1744
+ this.formatPrefix(),
1745
+ import_chalk.default.hex(this.colorScheme.muted)("DEBUG"),
1746
+ import_chalk.default.dim(message),
1747
+ ...args
1748
+ );
1644
1749
  }
1645
- async unregisterGlobal() {
1646
- const client = await this.client.whenReady();
1647
- if (!client.rest) {
1648
- console.error(`[CommandManager] \u2716 Failed to remove app commands globally: REST is not initialized`);
1649
- return;
1650
- }
1651
- try {
1652
- await client.rest.put(import_discord5.Routes.applicationCommands(client.user.id), { body: [] });
1653
- console.log(`[CommandManager] \u2714 Removed app commands globally`);
1654
- } catch (err) {
1655
- console.error(`[CommandManager] \u2716 Failed to remove app commands globally`, err);
1750
+ info(message, ...args) {
1751
+ if (!this.shouldLog(1 /* INFO */)) return;
1752
+ console.log(this.formatTimestamp(), this.formatPrefix(), import_chalk.default.hex("#87CEEB")("INFO"), message, ...args);
1753
+ }
1754
+ success(message, ...args) {
1755
+ if (!this.shouldLog(2 /* SUCCESS */)) return;
1756
+ console.log(
1757
+ this.formatTimestamp(),
1758
+ this.formatPrefix(),
1759
+ import_chalk.default.bold.hex(this.colorScheme.success)("\u2713 SUCCESS"),
1760
+ import_chalk.default.hex(this.colorScheme.success)(message),
1761
+ ...args
1762
+ );
1763
+ }
1764
+ warn(message, ...args) {
1765
+ if (!this.shouldLog(3 /* WARN */)) return;
1766
+ console.warn(
1767
+ this.formatTimestamp(),
1768
+ this.formatPrefix(),
1769
+ import_chalk.default.bold.hex(this.colorScheme.warn)("\u26A0 WARN"),
1770
+ import_chalk.default.hex(this.colorScheme.warn)(message),
1771
+ ...args
1772
+ );
1773
+ }
1774
+ error(message, error, ...args) {
1775
+ if (!this.shouldLog(4 /* ERROR */)) return;
1776
+ console.error(
1777
+ this.formatTimestamp(),
1778
+ this.formatPrefix(),
1779
+ import_chalk.default.bold.hex(this.colorScheme.danger)("\u2715 ERROR"),
1780
+ import_chalk.default.hex(this.colorScheme.danger)(message),
1781
+ ...args
1782
+ );
1783
+ if (error && error.stack) {
1784
+ console.error(import_chalk.default.dim(error.stack));
1656
1785
  }
1657
1786
  }
1658
- async registerGuild(options = {}) {
1659
- const client = await this.client.whenReady();
1660
- if (!client.rest) {
1661
- console.error(`[CommandManager] \u2716 Failed to register app commands by guild: REST is not initialized`);
1662
- return;
1663
- }
1664
- const commands = this.getAllAppCommands(options);
1665
- if (!commands.length) {
1666
- console.log("[CommandManager] No commands to register by guild");
1667
- return;
1787
+ loader(message) {
1788
+ const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
1789
+ let i = 0;
1790
+ const interval = setInterval(() => {
1791
+ process.stdout.write(
1792
+ `\r${this.formatTimestamp()} ${this.formatPrefix()} ${import_chalk.default.hex(this.colorScheme.warn)(frames[i])} ${message}`
1793
+ );
1794
+ i = (i + 1) % frames.length;
1795
+ }, 100);
1796
+ return (newMessage) => {
1797
+ clearInterval(interval);
1798
+ process.stdout.write(
1799
+ `\r${this.formatTimestamp()} ${this.formatPrefix()} ${import_chalk.default.hex(this.colorScheme.success)("\u2713")} ${newMessage || message}
1800
+ `
1801
+ );
1802
+ };
1803
+ }
1804
+ table(title, data) {
1805
+ console.log(this.formatTimestamp(), this.formatPrefix(), import_chalk.default.bold(title));
1806
+ Object.entries(data).forEach(([key, value]) => {
1807
+ const formattedKey = import_chalk.default.hex(this.colorScheme.warn)(` ${key}`);
1808
+ const formattedValue = import_chalk.default.hex(this.colorScheme.muted)(value);
1809
+ console.log(`${formattedKey.padEnd(25)} ${formattedValue}`);
1810
+ });
1811
+ }
1812
+ section(title) {
1813
+ const line = "\u2500".repeat(Math.max(30, title.length + 4));
1814
+ console.log(import_chalk.default.hex(this.colorScheme.muted)(`
1815
+ \u250C\u2500${line}\u2500\u2510`));
1816
+ console.log(
1817
+ import_chalk.default.hex(this.colorScheme.muted)("\u2502 ") + import_chalk.default.bold.hex(this.colorScheme.text)(title.padEnd(line.length)) + import_chalk.default.hex(this.colorScheme.muted)(" \u2502")
1818
+ );
1819
+ console.log(import_chalk.default.hex(this.colorScheme.muted)(`\u2514\u2500${line}\u2500\u2518`));
1820
+ }
1821
+ };
1822
+ var logger = new Logger();
1823
+
1824
+ // src/tools/utils.ts
1825
+ function __zero(str) {
1826
+ return str?.length ? str : "0";
1827
+ }
1828
+ function isMentionOrSnowflake(str) {
1829
+ return str ? str.match(/<@[#&]?[\d]{6,}>/) || str.match(/\d{6,}/) ? true : false : false;
1830
+ }
1831
+ function cleanMention(str) {
1832
+ return str ? str.replaceAll(/[<@#&>]/g, "").trim() : void 0;
1833
+ }
1834
+ async function getMessageMention(message, content, type, index = 0, idOnly) {
1835
+ const args = content?.split(" ");
1836
+ const arg = isMentionOrSnowflake(args?.[index]) ? cleanMention(args?.[index]) : void 0;
1837
+ switch (type) {
1838
+ case "user":
1839
+ const userMention2 = message.mentions.users.at(index) || null;
1840
+ if (!userMention2 && arg) {
1841
+ return idOnly ? arg : await fetchUser(message.client, arg);
1842
+ } else {
1843
+ return idOnly ? userMention2?.id || null : userMention2;
1844
+ }
1845
+ case "member":
1846
+ if (!message.guild) return null;
1847
+ const member = await fetchMember(message.guild, message.mentions.users.at(index)?.id ?? arg);
1848
+ return idOnly ? member?.id || null : member;
1849
+ case "channel":
1850
+ const channelMention = message.mentions.channels.at(index) || null;
1851
+ if (!channelMention && arg) {
1852
+ return idOnly ? arg : message.guild ? await fetchChannel(message.guild, arg) : message.client.channels.cache.get(__zero(arg)) ?? message.client.channels.fetch(__zero(arg));
1853
+ } else {
1854
+ return idOnly ? channelMention?.id || null : channelMention;
1855
+ }
1856
+ case "role":
1857
+ const roleMention = message.mentions.roles.at(index) || null;
1858
+ if (!roleMention && arg) {
1859
+ return idOnly ? arg : message.guild ? await fetchRole(message.guild, arg) : null;
1860
+ } else {
1861
+ return idOnly ? roleMention?.id || null : roleMention;
1862
+ }
1863
+ default:
1864
+ return null;
1865
+ }
1866
+ }
1867
+ function getFirstMentionId(options) {
1868
+ let mentionId = "";
1869
+ if (options.message) {
1870
+ switch (options.type) {
1871
+ case "user":
1872
+ mentionId = options.message.mentions.users.first()?.id || "";
1873
+ break;
1874
+ case "channel":
1875
+ mentionId = options.message.mentions.channels.first()?.id || "";
1876
+ break;
1877
+ case "role":
1878
+ mentionId = options.message.mentions.roles.first()?.id || "";
1879
+ break;
1668
1880
  }
1669
- const guildIds = options.guilds || client.guilds.cache.map((g) => g.id);
1670
- console.log(`[CommandManager] Registering (${commands.length}) commands for ${guildIds.length} guilds...`);
1671
- await Promise.all(
1672
- guildIds.map(
1673
- (guildId) => client.rest.put(import_discord5.Routes.applicationGuildCommands(client.user.id, guildId), { body: commands }).then(() => {
1674
- const gName = client.guilds.cache.get(guildId)?.name || "n/a";
1675
- console.log(`[CommandManager] \u2714 Set app commands in guild: ${guildId} (${gName})`);
1676
- }).catch((err) => {
1677
- const gName = client.guilds.cache.get(guildId)?.name || "n/a";
1678
- console.log(`[CommandManager] \u2716 Failed to set app commands in guild: ${guildId} (${gName})`, err);
1679
- })
1680
- )
1681
- );
1682
1881
  }
1683
- async unregisterGuild(options = {}) {
1684
- const client = await this.client.whenReady();
1685
- if (!client.rest) {
1686
- console.error(`[CommandManager] \u2716 Failed to register app commands by guild: REST is not initialized`);
1687
- return;
1688
- }
1689
- const guildIds = options.guilds || client.guilds.cache.map((g) => g.id);
1690
- console.log(`[CommandManager] Unregistering commands from ${guildIds.length} guilds...`);
1691
- await Promise.all(
1692
- guildIds.map(
1693
- (guildId) => client.rest.put(import_discord5.Routes.applicationGuildCommands(client.user.id, guildId), { body: [] }).then(() => console.log(`[CommandManager] \u2714 Removed app commands in guild: ${guildId}`)).catch((err) => console.log(`[CommandManager] \u2716 Failed to remove app commands in guild: ${guildId}`, err))
1694
- )
1695
- );
1882
+ const firstArg = options.content?.split(" ")[0] || "";
1883
+ return mentionId || isMentionOrSnowflake(firstArg) ? cleanMention(firstArg) : "";
1884
+ }
1885
+ async function fetchUser(client, userId) {
1886
+ if (!userId) return null;
1887
+ return client.users.cache.get(__zero(userId)) || await client.users.fetch(__zero(userId)).catch(() => null);
1888
+ }
1889
+ async function fetchGuild(client, guildId) {
1890
+ if (!guildId) return null;
1891
+ return client.guilds.cache.get(__zero(guildId)) || await client.guilds.fetch(__zero(guildId)).catch(() => null);
1892
+ }
1893
+ async function fetchMember(guild, memberId) {
1894
+ if (!memberId) return null;
1895
+ return guild.members.cache.get(__zero(memberId)) || await guild.members.fetch(__zero(memberId)).catch(() => null);
1896
+ }
1897
+ async function fetchChannel(guild, channelId, type) {
1898
+ if (!channelId) return null;
1899
+ const channel = guild.channels.cache.get(__zero(channelId)) || await guild.channels.fetch(__zero(channelId)).catch(() => null);
1900
+ if (type && channel?.type !== type) return null;
1901
+ return channel;
1902
+ }
1903
+ async function fetchRole(guild, roleId) {
1904
+ if (!roleId) return null;
1905
+ return guild.roles.cache.get(__zero(roleId)) || await guild.roles.fetch(__zero(roleId)).catch(() => null) || null;
1906
+ }
1907
+ async function fetchMessage(channel, messageId) {
1908
+ if (!messageId) return null;
1909
+ return channel.messages.cache.get(__zero(messageId)) || await channel.messages.fetch(__zero(messageId)).catch(() => null) || null;
1910
+ }
1911
+
1912
+ // src/types/status.ts
1913
+ var import_discord8 = require("discord.js");
1914
+ var import_lodash11 = __toESM(require("lodash"));
1915
+ var StatusType = /* @__PURE__ */ ((StatusType2) => {
1916
+ StatusType2["DND"] = "dnd";
1917
+ StatusType2["Idle"] = "idle";
1918
+ StatusType2["Online"] = "online";
1919
+ StatusType2["Invisible"] = "invisible";
1920
+ return StatusType2;
1921
+ })(StatusType || {});
1922
+ var defaultPresence = {
1923
+ production: {
1924
+ interval: 6e4,
1925
+ randomize: false,
1926
+ activity: [
1927
+ { status: "online" /* Online */, type: import_discord8.ActivityType.Custom, name: "Need help? Use /help or !help" },
1928
+ { status: "online" /* Online */, type: import_discord8.ActivityType.Custom, name: "Join our community!" },
1929
+ { status: "online" /* Online */, type: import_discord8.ActivityType.Watching, name: "\u2728 $GUILD_COUNT servers" }
1930
+ ]
1931
+ },
1932
+ development: {
1933
+ activity: { status: "dnd" /* DND */, type: import_discord8.ActivityType.Custom, name: "In development!" }
1696
1934
  }
1697
1935
  };
1936
+ function createVimcordStatusConfig(options = {}) {
1937
+ return import_lodash11.default.merge(defaultPresence, options);
1938
+ }
1698
1939
 
1699
- // src/modules/event.manager.ts
1700
- var import_discord6 = require("discord.js");
1701
- var EventManager = class {
1940
+ // src/modules/status.manager.ts
1941
+ var import_node_events = __toESM(require("events"));
1942
+ var import_qznt2 = require("qznt");
1943
+ var StatusManager = class {
1702
1944
  client;
1703
- events = /* @__PURE__ */ new Map();
1704
1945
  logger;
1946
+ emitter = new import_node_events.default();
1947
+ lastActivity = null;
1948
+ lastActivityIndex = 0;
1949
+ task = null;
1705
1950
  constructor(client) {
1706
1951
  this.client = client;
1707
- this.logger = new Logger({ prefixEmoji: "\u{1F4CB}", prefix: `EventManager (i${this.client.clientId})` });
1708
- for (const event of Object.values(import_discord6.Events)) {
1709
- client.on(
1710
- event.toString(),
1711
- async (...args) => this.executeEvents.apply(this, [event, ...args])
1712
- );
1713
- }
1714
- }
1715
- register(...events) {
1716
- for (const event of events) {
1717
- this.events.set(event.name, event);
1952
+ this.logger = new Logger({ prefixEmoji: "\u{1F4AC}", prefix: `StatusManager (i${this.client.clientId})` });
1953
+ this.emitter.on("changed", (activity) => {
1718
1954
  if (this.client.config.app.verbose) {
1719
- this.logger.debug(`'${event.name}' registered for EventType '${event.event}'`);
1955
+ this.logger.debug(`Status changed to '${activity.name}'`);
1720
1956
  }
1721
- }
1722
- }
1723
- unregister(...names) {
1724
- for (const name of names) {
1725
- const event = this.events.get(name);
1726
- if (!event) continue;
1727
- this.events.delete(name);
1957
+ });
1958
+ this.emitter.on("cleared", () => {
1728
1959
  if (this.client.config.app.verbose) {
1729
- this.logger.debug(`'${event.name}' unregistered for EventType '${event.event}'`);
1960
+ this.logger.debug("Status cleared");
1730
1961
  }
1731
- }
1962
+ });
1732
1963
  }
1733
- clear() {
1734
- this.events.forEach((e) => this.unregister(e.name));
1735
- this.events.clear();
1964
+ clearData() {
1965
+ this.task?.stop();
1966
+ this.task = null;
1967
+ this.lastActivity = null;
1968
+ this.lastActivityIndex = 0;
1969
+ return this;
1736
1970
  }
1737
- get(name) {
1738
- return this.events.get(name);
1971
+ async getReadyClient() {
1972
+ const client = await this.client.waitForReady();
1973
+ if (!client.user) throw new Error("Cannot manage the client's activity when its user is not hydrated");
1974
+ return client;
1739
1975
  }
1740
- getByTag(tag) {
1741
- return Array.from(this.events.values()).filter((event) => event.metadata?.tags?.includes(tag));
1976
+ async formatActivityName(name) {
1977
+ name = name.replace("$USER_COUNT", import_qznt2.$.format.number(this.client.users.cache.size)).replace("$GUILD_COUNT", import_qznt2.$.format.number(this.client.guilds.cache.size)).replace(
1978
+ "$INVITE",
1979
+ this.client.config.staff.guild.inviteUrl ? this.client.config.staff.guild.inviteUrl : "<STAFF_INVITE_URL_NOT_SET>"
1980
+ );
1981
+ if (name.includes("$STAFF_GUILD_MEMBER_COUNT")) {
1982
+ await fetchGuild(this.client, this.client.config.staff.guild.id).then((guild) => {
1983
+ if (!guild) return name = name.replace("$STAFF_GUILD_MEMBER_COUNT", "<STAFF_GUILD_NOT_FOUND>");
1984
+ name = name.replace("$STAFF_GUILD_MEMBER_COUNT", import_qznt2.$.format.number(guild.members.cache.size));
1985
+ }).catch((err) => this.logger.error("Failed to fetch the staff guild", err));
1986
+ }
1987
+ return name;
1742
1988
  }
1743
- getByCategory(category) {
1744
- return Array.from(this.events.values()).filter((event) => event.metadata?.category?.includes(category));
1989
+ async setActivity(activity) {
1990
+ const client = await this.getReadyClient();
1991
+ activity.name = await this.formatActivityName(activity.name);
1992
+ client.user.setStatus(activity.status);
1993
+ client.user.setActivity({ name: activity.name, type: activity.type, url: activity.streamUrl });
1994
+ this.emitter.emit("changed", activity);
1745
1995
  }
1746
- getByEvent(eventType) {
1747
- return Array.from(this.events.values()).filter((event) => event.event === eventType);
1996
+ async statusRotationTask(clientStatus) {
1997
+ let activity;
1998
+ if (clientStatus.randomize && Array.isArray(clientStatus.activity)) {
1999
+ activity = import_qznt2.$.rnd.choice(clientStatus.activity, { not: this.lastActivity });
2000
+ this.lastActivity = activity;
2001
+ } else {
2002
+ const activityIndex = (this.lastActivityIndex + 1) % clientStatus.activity.length;
2003
+ this.lastActivityIndex = activityIndex;
2004
+ activity = clientStatus.activity[activityIndex];
2005
+ }
2006
+ await this.setActivity(activity);
2007
+ this.emitter.emit("rotation", activity);
1748
2008
  }
1749
- async executeEvents(eventType, ...args) {
1750
- const events = this.getByEvent(eventType);
1751
- if (!events.length) return;
1752
- const sortedEvents = events.sort((a, b) => b.priority - a.priority);
1753
- await Promise.all(
1754
- sortedEvents.map(async (event) => {
1755
- try {
1756
- await event.execute?.(this.client, ...args);
1757
- if (event.once) {
1758
- this.unregister(event.name);
1759
- }
1760
- } catch (err) {
1761
- this.logger.error(`'${event.name}' failed to execute`, err);
1762
- }
1763
- })
1764
- );
2009
+ async scheduleStatusRotation(clientStatus) {
2010
+ if (!clientStatus.interval) throw new Error("Cannot create client activity interval without interval time");
2011
+ this.task?.stop();
2012
+ this.task = null;
2013
+ this.task = new import_qznt2.$.Loop(() => this.statusRotationTask(clientStatus), import_qznt2.$.math.ms(clientStatus.interval), true);
2014
+ this.start();
1765
2015
  }
1766
- /** Import event modules that end with `.event` */
1767
- async importFrom(dir, replaceAll) {
1768
- dir = Array.isArray(dir) ? dir : [dir];
1769
- const eventModules = await Promise.all(
1770
- dir.map((dir2) => importModulesFromDir(dir2, "event"))
1771
- );
1772
- if (replaceAll) {
1773
- this.clear();
2016
+ start() {
2017
+ if (this.task) {
2018
+ this.task.start();
2019
+ this.emitter.emit("started", this.task);
2020
+ }
2021
+ return this;
2022
+ }
2023
+ pause() {
2024
+ if (this.task) {
2025
+ this.task.stop();
2026
+ this.emitter.emit("paused", this.task);
2027
+ }
2028
+ return this;
2029
+ }
2030
+ async set(status) {
2031
+ const statusConfig = createVimcordStatusConfig(status);
2032
+ let clientStatus;
2033
+ if (this.client.config.app.devMode) {
2034
+ clientStatus = statusConfig.development;
2035
+ } else {
2036
+ clientStatus = statusConfig.production;
1774
2037
  }
1775
- let importedEvents = 0;
1776
- let ignoredEvents = 0;
1777
- for (const event of eventModules.flat()) {
1778
- if (!event.module.default.enabled) {
1779
- ignoredEvents++;
1780
- } else {
1781
- importedEvents++;
1782
- }
1783
- this.register(event.module.default);
2038
+ if (!clientStatus.interval) {
2039
+ await this.setActivity(Array.isArray(clientStatus.activity) ? clientStatus.activity[0] : clientStatus.activity);
2040
+ } else {
2041
+ await this.scheduleStatusRotation(clientStatus);
1784
2042
  }
1785
- this.client.logger.moduleLoaded("Event Handlers", importedEvents, ignoredEvents);
1786
- return this.events;
2043
+ return this;
2044
+ }
2045
+ async destroy() {
2046
+ if (this.task) {
2047
+ this.task.stop();
2048
+ this.task = null;
2049
+ this.emitter.emit("destroyed");
2050
+ await this.clear();
2051
+ }
2052
+ return this;
2053
+ }
2054
+ async clear() {
2055
+ const client = await this.getReadyClient();
2056
+ this.clearData();
2057
+ client.user.setActivity({ name: "" });
2058
+ this.emitter.emit("cleared");
2059
+ return this;
1787
2060
  }
1788
2061
  };
1789
2062
 
1790
- // src/utils/sendCommandErrorEmbed.ts
2063
+ // src/modules/command.manager.ts
1791
2064
  var import_discord9 = require("discord.js");
1792
-
1793
- // src/tools/BetterEmbed.ts
1794
- var import_discord8 = require("discord.js");
1795
-
1796
- // src/tools/dynaSend.ts
1797
- var import_discord7 = require("discord.js");
1798
-
1799
- // src/tools/types.ts
1800
- var SendMethod = /* @__PURE__ */ ((SendMethod2) => {
1801
- SendMethod2[SendMethod2["Reply"] = 0] = "Reply";
1802
- SendMethod2[SendMethod2["EditReply"] = 1] = "EditReply";
1803
- SendMethod2[SendMethod2["FollowUp"] = 2] = "FollowUp";
1804
- SendMethod2[SendMethod2["Channel"] = 3] = "Channel";
1805
- SendMethod2[SendMethod2["MessageReply"] = 4] = "MessageReply";
1806
- SendMethod2[SendMethod2["MessageEdit"] = 5] = "MessageEdit";
1807
- SendMethod2[SendMethod2["User"] = 6] = "User";
1808
- return SendMethod2;
1809
- })(SendMethod || {});
1810
-
1811
- // src/tools/dynaSend.ts
1812
- var DynaSend = class {
1813
- static forceArray(value) {
1814
- return Array.isArray(value) ? value : [value];
1815
- }
1816
- static isInteractionCallback(obj) {
1817
- return obj instanceof import_discord7.InteractionCallbackResponse;
1818
- }
1819
- static filterFlags(flags, excludeFlags) {
1820
- if (!flags) return void 0;
1821
- const flagArray = this.forceArray(flags);
1822
- return flagArray.filter((flag) => !excludeFlags.includes(flag));
2065
+ var BaseCommandManager = class {
2066
+ type;
2067
+ client;
2068
+ commands = /* @__PURE__ */ new Map();
2069
+ moduleSuffix;
2070
+ constructor(client, type, moduleSuffix) {
2071
+ this.type = type;
2072
+ this.client = client;
2073
+ this.moduleSuffix = moduleSuffix;
1823
2074
  }
1824
- static detectSendMethod(handler) {
1825
- if (handler instanceof import_discord7.BaseInteraction) {
1826
- return handler.replied || handler.deferred ? 1 /* EditReply */ : 0 /* Reply */;
2075
+ /**
2076
+ * Gets a command by name.
2077
+ */
2078
+ get(name) {
2079
+ if (this.type === 1 /* Prefix */) {
2080
+ const config = this.client.config.prefixCommands;
2081
+ const search = config.allowCaseInsensitiveCommandNames ? name.toLowerCase() : name;
2082
+ return Array.from(this.commands.values()).find((cmd) => {
2083
+ const commandName = "builder" in cmd ? cmd.builder.name : cmd.options.name;
2084
+ const trigger = config.allowCaseInsensitiveCommandNames ? commandName.toLowerCase() : commandName;
2085
+ if (trigger === search) return true;
2086
+ if ("aliases" in cmd.options) {
2087
+ return cmd.options.aliases?.some(
2088
+ (a) => config.allowCaseInsensitiveCommandNames ? a.toLowerCase() === search : a === search
2089
+ );
2090
+ }
2091
+ });
2092
+ } else {
2093
+ return this.commands.get(name);
1827
2094
  }
1828
- if (handler instanceof import_discord7.BaseChannel) return 3 /* Channel */;
1829
- if (handler instanceof import_discord7.Message) return 4 /* MessageReply */;
1830
- if (handler instanceof import_discord7.GuildMember || handler instanceof import_discord7.User) return 6 /* User */;
1831
- throw new Error("[DynaSend] Unable to determine send method for handler type");
1832
2095
  }
1833
- static validateSendMethod(handler, method) {
1834
- const interactionMethods = [0 /* Reply */, 1 /* EditReply */, 2 /* FollowUp */];
1835
- if (interactionMethods.includes(method) && !(handler instanceof import_discord7.BaseInteraction)) {
1836
- throw new TypeError(`[DynaSend] SendMethod '${SendMethod[method]}' requires BaseInteraction handler`);
1837
- }
1838
- if (method === 3 /* Channel */ && !(handler instanceof import_discord7.BaseChannel)) {
1839
- throw new TypeError(`[DynaSend] SendMethod '${SendMethod[method]}' requires BaseChannel handler`);
1840
- }
1841
- if ([4 /* MessageReply */, 5 /* MessageEdit */].includes(method) && !(handler instanceof import_discord7.Message)) {
1842
- throw new TypeError(`[DynaSend] SendMethod '${SendMethod[method]}' requires Message handler`);
1843
- }
1844
- if (method === 6 /* User */ && !(handler instanceof import_discord7.GuildMember || handler instanceof import_discord7.User)) {
1845
- throw new TypeError(`[DynaSend] SendMethod '${SendMethod[method]}' requires User or GuildMember handler`);
2096
+ /**
2097
+ * Gets/filters commands and orders them alphabetically.
2098
+ */
2099
+ getAll(options = {}) {
2100
+ const matchedCommands = /* @__PURE__ */ new Map();
2101
+ const isDev = this.client.config.app.devMode;
2102
+ for (const cmd of this.commands.values()) {
2103
+ const commandName = "builder" in cmd ? cmd.builder.name : cmd.options.name;
2104
+ if (options.names || options.fuzzyNames) {
2105
+ const nameMatched = options.names?.includes(commandName) || options.fuzzyNames?.some((fuzzy) => commandName.includes(fuzzy));
2106
+ if (!nameMatched) continue;
2107
+ }
2108
+ if (options.ignoreDeploymentOptions) {
2109
+ matchedCommands.set(commandName, cmd);
2110
+ continue;
2111
+ }
2112
+ const deployment = "deployment" in cmd.options ? cmd.options.deployment ?? {} : {};
2113
+ const isProperEnv = !deployment.environments || deployment.environments.includes(isDev ? "development" : "production");
2114
+ if (!isProperEnv) continue;
2115
+ if (options.globalOnly && deployment.global === false) continue;
2116
+ matchedCommands.set(commandName, cmd);
1846
2117
  }
2118
+ return Array.from(matchedCommands.values()).sort((a, b) => {
2119
+ const commandNameA = "builder" in a ? a.builder.name : a.options.name;
2120
+ const commandNameB = "builder" in b ? b.builder.name : b.options.name;
2121
+ return commandNameA.localeCompare(commandNameB);
2122
+ });
1847
2123
  }
1848
- static createMessageData(options, method) {
1849
- const baseData = {
1850
- content: options.content,
1851
- embeds: options.embeds,
1852
- components: options.components,
1853
- files: options.files,
1854
- allowedMentions: options.allowedMentions,
1855
- tts: options.tts
1856
- };
1857
- switch (method) {
1858
- case 0 /* Reply */:
1859
- return {
1860
- ...baseData,
1861
- flags: options.flags,
1862
- withResponse: options.withResponse,
1863
- poll: options.poll
1864
- };
1865
- case 1 /* EditReply */:
1866
- return {
1867
- ...baseData,
1868
- flags: this.filterFlags(options.flags, ["Ephemeral", "SuppressNotifications"]),
1869
- withResponse: options.withResponse,
1870
- poll: options.poll
1871
- };
1872
- case 2 /* FollowUp */:
1873
- return {
1874
- ...baseData,
1875
- flags: options.flags,
1876
- withResponse: options.withResponse,
1877
- poll: options.poll
1878
- };
1879
- case 3 /* Channel */:
1880
- return {
1881
- ...baseData,
1882
- flags: this.filterFlags(options.flags, ["Ephemeral"]),
1883
- poll: options.poll,
1884
- stickers: options.stickers,
1885
- reply: options.reply
1886
- };
1887
- case 4 /* MessageReply */:
1888
- return {
1889
- ...baseData,
1890
- flags: this.filterFlags(options.flags, ["Ephemeral"]),
1891
- poll: options.poll,
1892
- stickers: options.stickers
1893
- };
1894
- case 5 /* MessageEdit */:
1895
- return {
1896
- ...baseData,
1897
- flags: this.filterFlags(options.flags, ["Ephemeral", "SuppressNotifications"])
1898
- };
1899
- case 6 /* User */:
1900
- return {
1901
- ...baseData,
1902
- flags: this.filterFlags(options.flags, ["Ephemeral"]),
1903
- poll: options.poll,
1904
- forward: options.forward,
1905
- stickers: options.stickers
2124
+ /**
2125
+ * Groups commands by category alphabetically.
2126
+ */
2127
+ sortByCategory() {
2128
+ const categories = /* @__PURE__ */ new Map();
2129
+ for (const cmd of this.commands.values()) {
2130
+ const metadata = cmd.options.metadata;
2131
+ if (!metadata?.category) continue;
2132
+ let entry = categories.get(metadata.category);
2133
+ if (!entry) {
2134
+ entry = {
2135
+ name: metadata.category,
2136
+ emoji: metadata.categoryEmoji,
2137
+ commands: []
1906
2138
  };
1907
- default:
1908
- return baseData;
2139
+ categories.set(metadata.category, entry);
2140
+ }
2141
+ entry.commands.push(cmd);
1909
2142
  }
2143
+ return Array.from(categories.values()).sort((a, b) => a.name.localeCompare(b.name)).map((cat) => {
2144
+ cat.commands.sort((a, b) => {
2145
+ const commandNameA = "builder" in a ? a.builder.name : a.options.name;
2146
+ const commandNameB = "builder" in b ? b.builder.name : b.options.name;
2147
+ return commandNameA.localeCompare(commandNameB);
2148
+ });
2149
+ return cat;
2150
+ });
1910
2151
  }
1911
- static async executeSend(handler, method, data) {
1912
- try {
1913
- switch (method) {
1914
- case 0 /* Reply */: {
1915
- const response = await handler.reply(data);
1916
- return this.isInteractionCallback(response) ? response.resource?.message ?? null : null;
1917
- }
1918
- case 1 /* EditReply */:
1919
- return await handler.editReply(data);
1920
- case 2 /* FollowUp */:
1921
- return await handler.followUp(data);
1922
- case 3 /* Channel */:
1923
- return await handler.send(data);
1924
- case 4 /* MessageReply */:
1925
- return await handler.reply(data);
1926
- case 5 /* MessageEdit */: {
1927
- const message = handler;
1928
- if (!message.editable) {
1929
- console.warn("[DynaSend] Message is not editable");
1930
- return null;
1931
- }
1932
- return await message.edit(data);
1933
- }
1934
- case 6 /* User */:
1935
- return await handler.send(data);
1936
- default:
1937
- throw new Error(`[DynaSend] Unknown send method '${method}'`);
1938
- }
1939
- } catch (error) {
1940
- console.error(`[DynaSend] Error with method '${SendMethod[method]}':`, error);
1941
- return null;
2152
+ /**
2153
+ * Imports command modules from a directory.
2154
+ * @param dir Path of one or more folders.
2155
+ * @param set Replaces imported command modules with the ones found.
2156
+ */
2157
+ async importFrom(dir, set = false) {
2158
+ if (set) this.commands.clear();
2159
+ const dirs = Array.isArray(dir) ? dir : [dir];
2160
+ const modules = [];
2161
+ for (const _dir of dirs) {
2162
+ const results = await importModulesFromDir(_dir, this.moduleSuffix);
2163
+ modules.push(...results.map(({ module: module2 }) => module2.default));
1942
2164
  }
1943
- }
1944
- static scheduleDelete(message, delay) {
1945
- if (delay < 1e3) {
1946
- console.warn(`[DynaSend] Delete delay is less than 1 second (${delay}ms). Is this intentional?`);
2165
+ for (const module2 of modules) {
2166
+ const commandName = "builder" in module2 ? module2.builder.name : module2.options.name;
2167
+ this.commands.set(commandName, module2);
1947
2168
  }
1948
- setTimeout(async () => {
1949
- try {
1950
- if (message.deletable) {
1951
- await message.delete();
1952
- }
1953
- } catch (error) {
1954
- console.error("[DynaSend] Error deleting message:", error);
1955
- }
1956
- }, delay);
1957
- }
1958
- static async send(handler, options) {
1959
- const sendMethod = options.sendMethod ?? this.detectSendMethod(handler);
1960
- this.validateSendMethod(handler, sendMethod);
1961
- const messageData = this.createMessageData(options, sendMethod);
1962
- const message = await this.executeSend(handler, sendMethod, messageData);
1963
- if (options.deleteAfter && message) {
1964
- this.scheduleDelete(message, options.deleteAfter);
2169
+ let moduleType;
2170
+ switch (this.type) {
2171
+ case 0 /* Slash */:
2172
+ moduleType = "Prefix Commands";
2173
+ break;
2174
+ case 2 /* Context */:
2175
+ moduleType = "Context Commands";
2176
+ break;
2177
+ case 1 /* Prefix */:
2178
+ moduleType = "Prefix Commands";
2179
+ break;
1965
2180
  }
1966
- return message;
2181
+ this.client.logger.moduleLoaded(moduleType, modules.length);
2182
+ return this.commands;
1967
2183
  }
1968
2184
  };
1969
- async function dynaSend(handler, options) {
1970
- return DynaSend.send(handler, options);
1971
- }
1972
-
1973
- // src/tools/BetterEmbed.ts
1974
- var BetterEmbed = class _BetterEmbed {
1975
- embed = new import_discord8.EmbedBuilder();
1976
- data;
1977
- config;
1978
- /** A powerful wrapper for `EmbedBuilder` that introduces useful features
1979
- *
1980
- * Auto-shorthand context formatting (_ACF_) is enabled by default
1981
- *
1982
- * All functions utilize _ACF_ unless `BetterEmbed.acf` is set to `false`
1983
- *
1984
- * ___Use a blackslash___ `\` ___to escape any context___
1985
- *
1986
- * \- - - Author Context - - -
1987
- * - __`$USER`__: _author's mention (@xsqu1znt)_
1988
- * - __`$USER_NAME`__: _author's username_
1989
- * - __`$DISPLAY_NAME`__: _author's display name (requires `GuildMember` context)_
1990
- * - __`$USER_AVATAR`__: _author's avatar_
1991
- *
1992
- * \- - - Client Context - - -
1993
- *
1994
- * - __`$BOT_AVATAR`__: _bot's avatar_
1995
- *
1996
- * \- - - Shorthand Context - - -
1997
- * - __`$YEAR`__: _YYYY_
1998
- * - __`$MONTH`__: _MM_
1999
- * - __`$DAY`__: _DD_
2000
- * - __`$year`__: _YY_
2001
- * - __`$month`__: _M or MM_
2002
- * - __`$day`__: _D or DD_ */
2003
- constructor(data = {}) {
2004
- this.config = data.config || globalVimcordToolsConfig;
2005
- this.data = {
2006
- context: data.context || null,
2007
- author: data.author || null,
2008
- title: data.title || null,
2009
- thumbnailUrl: data.thumbnailUrl || null,
2010
- description: data.description || null,
2011
- imageUrl: data.imageUrl || null,
2012
- footer: data.footer || null,
2013
- fields: data.fields || [],
2014
- color: data.color ?? (this.config.devMode ? this.config.embedColorDev : this.config.embedColor),
2015
- timestamp: data.timestamp || null,
2016
- acf: data.acf ?? true
2017
- };
2018
- this.build();
2185
+ var SlashCommandManager = class extends BaseCommandManager {
2186
+ constructor(client) {
2187
+ super(client, 0 /* Slash */, client.config.app.moduleSuffixes.slashCommand);
2019
2188
  }
2020
- build() {
2021
- this.normalizeData();
2022
- this.applyContextFormatting();
2023
- this.configureEmbed();
2189
+ };
2190
+ var ContextCommandManager = class extends BaseCommandManager {
2191
+ constructor(client) {
2192
+ super(client, 2 /* Context */, client.config.app.moduleSuffixes.contextCommand);
2024
2193
  }
2025
- normalizeData() {
2026
- if (typeof this.data.author === "string") {
2027
- this.data.author = { text: this.data.author };
2028
- }
2029
- if (typeof this.data.title === "string") {
2030
- this.data.title = { text: this.data.title };
2031
- }
2032
- if (typeof this.data.footer === "string") {
2033
- this.data.footer = { text: this.data.footer };
2034
- }
2035
- if (this.data.timestamp === true) {
2036
- this.data.timestamp = Date.now();
2037
- }
2194
+ };
2195
+ var PrefixCommandManager = class extends BaseCommandManager {
2196
+ constructor(client) {
2197
+ super(client, 1 /* Prefix */, client.config.app.moduleSuffixes.prefixCommand);
2038
2198
  }
2039
- getContextUser() {
2040
- const context = this.data.context;
2041
- if (!context) return null;
2042
- return context.user || context.interaction?.member || context.interaction?.user || context.message?.member || context.message?.author || null;
2199
+ };
2200
+ var CommandManager = class {
2201
+ client;
2202
+ slash;
2203
+ prefix;
2204
+ context;
2205
+ constructor(client) {
2206
+ this.client = client;
2207
+ this.slash = new SlashCommandManager(client);
2208
+ this.prefix = new PrefixCommandManager(client);
2209
+ this.context = new ContextCommandManager(client);
2043
2210
  }
2044
- getContextClient() {
2045
- const context = this.data.context;
2046
- if (!context) return null;
2047
- return context.client || context.interaction?.client || context.message?.client || null;
2211
+ getAllAppCommands(options = {}) {
2212
+ return [...this.slash.getAll(options), ...this.context.getAll(options)];
2048
2213
  }
2049
- applyContextFormatting(str) {
2050
- if (!this.data.acf) return;
2051
- const user = this.getContextUser();
2052
- const guildMember = user instanceof import_discord8.GuildMember ? user : null;
2053
- const actualUser = guildMember?.user || (user instanceof import_discord8.User ? user : null);
2054
- const client = this.getContextClient();
2055
- const formatString = (str2) => {
2056
- if (!str2 || !str2.includes("$")) return str2;
2057
- return str2.replace(/(?<!\\)\$USER\b/g, actualUser?.toString() || "$USER").replace(/(?<!\\)\$USER_NAME\b/g, actualUser?.username || "$USER_NAME").replace(/(?<!\\)\$USER_AVATAR\b/g, actualUser?.avatarURL() || "$USER_AVATAR").replace(/(?<!\\)\$DISPLAY_NAME\b/g, guildMember?.displayName || "$DISPLAY_NAME").replace(/(?<!\\)\$BOT_AVATAR\b/g, client?.user?.avatarURL() || "$BOT_AVATAR").replace(/(?<!\\)\$INVIS\b/g, "\u200B").replace(/(?<!\\)\$YEAR/g, (/* @__PURE__ */ new Date()).getFullYear().toString()).replace(/(?<!\\)\$MONTH/g, String((/* @__PURE__ */ new Date()).getMonth() + 1).padStart(2, "0")).replace(/(?<!\\)\$DAY/g, String((/* @__PURE__ */ new Date()).getDate()).padStart(2, "0")).replace(/(?<!\\)\$year/g, String((/* @__PURE__ */ new Date()).getFullYear()).slice(-2)).replace(/(?<!\\)\$month/g, String((/* @__PURE__ */ new Date()).getMonth() + 1).padStart(2, "0")).replace(/(?<!\\)\$day/g, String((/* @__PURE__ */ new Date()).getDate()).padStart(2, "0")).replace(/(?<!\\|<)@([0-9]+)(?!>)/g, "<@$1>").replace(/(?<!\\|<)@&([0-9]+)(?!>)/g, "<@&$1>").replace(/(?<!\\|<)#([0-9]+)(?!>)/g, "<#$1>");
2058
- };
2059
- if (str) {
2060
- return formatString(str);
2061
- }
2062
- if (this.data.author && typeof this.data.author === "object") {
2063
- this.data.author.text = formatString(this.data.author.text);
2064
- if (this.data.author.icon === true && actualUser) {
2065
- this.data.author.icon = actualUser.avatarURL();
2066
- } else if (typeof this.data.author.icon === "string") {
2067
- this.data.author.icon = formatString(this.data.author.icon);
2068
- }
2069
- }
2070
- if (this.data.title && typeof this.data.title === "object") {
2071
- this.data.title.text = formatString(this.data.title.text);
2072
- }
2073
- if (this.data.description) {
2074
- this.data.description = formatString(
2075
- Array.isArray(this.data.description) ? this.data.description.filter((s) => s !== null && s !== void 0).join("\n") : this.data.description
2076
- );
2077
- }
2078
- if (this.data.footer && typeof this.data.footer === "object") {
2079
- this.data.footer.text = formatString(this.data.footer.text);
2214
+ async registerGlobal(options = {}) {
2215
+ const client = await this.client.waitForReady();
2216
+ if (!client.rest) {
2217
+ console.error(`[CommandManager] \u2716 Failed to register app commands globally: REST is not initialized`);
2218
+ return;
2080
2219
  }
2081
- if (this.data.thumbnailUrl) {
2082
- this.data.thumbnailUrl = formatString(this.data.thumbnailUrl);
2220
+ const commands = this.getAllAppCommands(options);
2221
+ if (!commands.length) {
2222
+ console.log("[CommandManager] No commands to register globally");
2223
+ return;
2083
2224
  }
2084
- if (this.data.imageUrl) {
2085
- this.data.imageUrl = formatString(this.data.imageUrl);
2225
+ console.log(`[CommandManager] Registering (${commands.length}) commands globally...`);
2226
+ try {
2227
+ await client.rest.put(import_discord9.Routes.applicationCommands(client.user.id), { body: commands });
2228
+ console.log(`[CommandManager] \u2714 Registered app commands globally`);
2229
+ } catch (err) {
2230
+ console.error(`[CommandManager] \u2716 Failed to register app commands globally`, err);
2086
2231
  }
2087
- this.data.fields = this.data.fields.filter(Boolean).map((field) => ({
2088
- ...field,
2089
- name: formatString(field.name),
2090
- value: formatString(field.value)
2091
- }));
2092
2232
  }
2093
- configureEmbed() {
2094
- if (this.data.author && typeof this.data.author === "object" && this.data.author.text) {
2095
- try {
2096
- this.embed.setAuthor({
2097
- name: this.data.author.text,
2098
- iconURL: typeof this.data.author.icon === "string" ? this.data.author.icon : void 0,
2099
- url: this.data.author.hyperlink || void 0
2100
- });
2101
- } catch (error) {
2102
- console.error("[BetterEmbed] Invalid author configuration:", error);
2103
- }
2104
- }
2105
- if (this.data.title && typeof this.data.title === "object" && this.data.title.text) {
2106
- try {
2107
- this.embed.setTitle(this.data.title.text);
2108
- if (this.data.title.hyperlink) {
2109
- this.embed.setURL(this.data.title.hyperlink);
2110
- }
2111
- } catch (error) {
2112
- console.error("[BetterEmbed] Invalid title configuration:", error);
2113
- }
2233
+ async unregisterGlobal() {
2234
+ const client = await this.client.waitForReady();
2235
+ if (!client.rest) {
2236
+ console.error(`[CommandManager] \u2716 Failed to remove app commands globally: REST is not initialized`);
2237
+ return;
2114
2238
  }
2115
- if (this.data.description) {
2116
- this.embed.setDescription(
2117
- Array.isArray(this.data.description) ? this.data.description.join("\n") : this.data.description
2118
- );
2239
+ try {
2240
+ await client.rest.put(import_discord9.Routes.applicationCommands(client.user.id), { body: [] });
2241
+ console.log(`[CommandManager] \u2714 Removed app commands globally`);
2242
+ } catch (err) {
2243
+ console.error(`[CommandManager] \u2716 Failed to remove app commands globally`, err);
2119
2244
  }
2120
- if (this.data.thumbnailUrl) {
2121
- try {
2122
- this.embed.setThumbnail(this.data.thumbnailUrl);
2123
- } catch (error) {
2124
- console.error("[BetterEmbed] Invalid thumbnail URL:", error);
2125
- }
2245
+ }
2246
+ async registerGuild(options = {}) {
2247
+ const client = await this.client.waitForReady();
2248
+ if (!client.rest) {
2249
+ console.error(`[CommandManager] \u2716 Failed to register app commands by guild: REST is not initialized`);
2250
+ return;
2126
2251
  }
2127
- if (this.data.imageUrl) {
2128
- try {
2129
- this.embed.setImage(this.data.imageUrl);
2130
- } catch (error) {
2131
- console.error("[BetterEmbed] Invalid image URL:", error);
2132
- }
2252
+ const commands = this.getAllAppCommands(options);
2253
+ if (!commands.length) {
2254
+ console.log("[CommandManager] No commands to register by guild");
2255
+ return;
2133
2256
  }
2134
- if (this.data.footer && typeof this.data.footer === "object" && this.data.footer.text) {
2135
- try {
2136
- this.embed.setFooter({
2137
- text: this.data.footer.text,
2138
- iconURL: typeof this.data.footer.icon === "string" ? this.data.footer.icon : void 0
2139
- });
2140
- } catch (error) {
2141
- console.error("[BetterEmbed] Invalid footer configuration:", error);
2142
- }
2257
+ const guildIds = options.guilds || client.guilds.cache.map((g) => g.id);
2258
+ console.log(`[CommandManager] Registering (${commands.length}) commands for ${guildIds.length} guilds...`);
2259
+ await Promise.all(
2260
+ guildIds.map(
2261
+ (guildId) => client.rest.put(import_discord9.Routes.applicationGuildCommands(client.user.id, guildId), { body: commands }).then(() => {
2262
+ const gName = client.guilds.cache.get(guildId)?.name || "n/a";
2263
+ console.log(`[CommandManager] \u2714 Set app commands in guild: ${guildId} (${gName})`);
2264
+ }).catch((err) => {
2265
+ const gName = client.guilds.cache.get(guildId)?.name || "n/a";
2266
+ console.log(`[CommandManager] \u2716 Failed to set app commands in guild: ${guildId} (${gName})`, err);
2267
+ })
2268
+ )
2269
+ );
2270
+ }
2271
+ async unregisterGuild(options = {}) {
2272
+ const client = await this.client.waitForReady();
2273
+ if (!client.rest) {
2274
+ console.error(`[CommandManager] \u2716 Failed to register app commands by guild: REST is not initialized`);
2275
+ return;
2143
2276
  }
2144
- if (this.data.color) {
2145
- try {
2146
- const color = Array.isArray(this.data.color) ? this.data.color[Math.floor(Math.random() * this.data.color.length)] ?? null : this.data.color;
2147
- this.embed.setColor(color);
2148
- } catch (error) {
2149
- console.error("[BetterEmbed] Invalid color:", error);
2150
- }
2277
+ const guildIds = options.guilds || client.guilds.cache.map((g) => g.id);
2278
+ console.log(`[CommandManager] Unregistering commands from ${guildIds.length} guilds...`);
2279
+ await Promise.all(
2280
+ guildIds.map(
2281
+ (guildId) => client.rest.put(import_discord9.Routes.applicationGuildCommands(client.user.id, guildId), { body: [] }).then(() => console.log(`[CommandManager] \u2714 Removed app commands in guild: ${guildId}`)).catch((err) => console.log(`[CommandManager] \u2716 Failed to remove app commands in guild: ${guildId}`, err))
2282
+ )
2283
+ );
2284
+ }
2285
+ };
2286
+
2287
+ // src/modules/event.manager.ts
2288
+ var import_discord10 = require("discord.js");
2289
+ var EventManager = class {
2290
+ client;
2291
+ events = /* @__PURE__ */ new Map();
2292
+ logger;
2293
+ constructor(client) {
2294
+ this.client = client;
2295
+ this.logger = new Logger({ prefixEmoji: "\u{1F4CB}", prefix: `EventManager (i${this.client.clientId})` });
2296
+ for (const event of Object.values(import_discord10.Events)) {
2297
+ client.on(
2298
+ event.toString(),
2299
+ async (...args) => this.executeEvents.apply(this, [event, ...args])
2300
+ );
2151
2301
  }
2152
- if (this.data.timestamp && this.data.timestamp !== true) {
2153
- try {
2154
- this.embed.setTimestamp(this.data.timestamp);
2155
- } catch (error) {
2156
- console.error("[BetterEmbed] Invalid timestamp:", error);
2302
+ }
2303
+ register(...events) {
2304
+ for (const event of events) {
2305
+ this.events.set(event.name, event);
2306
+ if (this.client.config.app.verbose) {
2307
+ this.logger.debug(`'${event.name}' registered for EventType '${event.event}'`);
2157
2308
  }
2158
2309
  }
2159
- if (this.data.fields.length > 0) {
2160
- const validFields = this.data.fields.slice(0, 25);
2161
- if (this.data.fields.length > 25) {
2162
- console.warn("[BetterEmbed] Only first 25 fields will be used (Discord limit)");
2310
+ }
2311
+ unregister(...names) {
2312
+ for (const name of names) {
2313
+ const event = this.events.get(name);
2314
+ if (!event) continue;
2315
+ this.events.delete(name);
2316
+ if (this.client.config.app.verbose) {
2317
+ this.logger.debug(`'${event.name}' unregistered for EventType '${event.event}'`);
2163
2318
  }
2164
- this.embed.setFields(validFields);
2165
2319
  }
2166
2320
  }
2167
- setAuthor(author) {
2168
- this.data.author = author;
2169
- this.build();
2170
- return this;
2321
+ clear() {
2322
+ this.events.forEach((e) => this.unregister(e.name));
2323
+ this.events.clear();
2171
2324
  }
2172
- setTitle(title) {
2173
- this.data.title = title;
2174
- this.build();
2175
- return this;
2325
+ get(name) {
2326
+ return this.events.get(name);
2176
2327
  }
2177
- setDescription(description) {
2178
- this.data.description = description;
2179
- this.build();
2180
- return this;
2328
+ getByTag(tag) {
2329
+ return Array.from(this.events.values()).filter((event) => event.metadata?.tags?.includes(tag));
2181
2330
  }
2182
- setThumbnail(url) {
2183
- this.data.thumbnailUrl = url;
2184
- this.build();
2185
- return this;
2331
+ getByCategory(category) {
2332
+ return Array.from(this.events.values()).filter((event) => event.metadata?.category?.includes(category));
2186
2333
  }
2187
- setImage(url) {
2188
- this.data.imageUrl = url;
2189
- this.build();
2190
- return this;
2334
+ getByEvent(eventType) {
2335
+ return Array.from(this.events.values()).filter((event) => event.event === eventType);
2191
2336
  }
2192
- setFooter(footer) {
2193
- this.data.footer = footer;
2194
- this.build();
2195
- return this;
2337
+ async executeEvents(eventType, ...args) {
2338
+ const events = this.getByEvent(eventType);
2339
+ if (!events.length) return;
2340
+ const sortedEvents = events.sort((a, b) => b.priority - a.priority);
2341
+ await Promise.all(
2342
+ sortedEvents.map(async (event) => {
2343
+ try {
2344
+ await event.execute?.(this.client, ...args);
2345
+ if (event.once) {
2346
+ this.unregister(event.name);
2347
+ }
2348
+ } catch (err) {
2349
+ this.logger.error(`'${event.name}' failed to execute`, err);
2350
+ }
2351
+ })
2352
+ );
2196
2353
  }
2197
- setColor(color) {
2198
- this.data.color = color;
2199
- this.build();
2200
- return this;
2354
+ /** Import event modules that end with `.event` */
2355
+ async importFrom(dir, replaceAll) {
2356
+ dir = Array.isArray(dir) ? dir : [dir];
2357
+ const eventModules = await Promise.all(
2358
+ dir.map((dir2) => importModulesFromDir(dir2, "event"))
2359
+ );
2360
+ if (replaceAll) {
2361
+ this.clear();
2362
+ }
2363
+ let importedEvents = 0;
2364
+ let ignoredEvents = 0;
2365
+ for (const event of eventModules.flat()) {
2366
+ if (!event.module.default.enabled) {
2367
+ ignoredEvents++;
2368
+ } else {
2369
+ importedEvents++;
2370
+ }
2371
+ this.register(event.module.default);
2372
+ }
2373
+ this.client.logger.moduleLoaded("Event Handlers", importedEvents, ignoredEvents);
2374
+ return this.events;
2201
2375
  }
2202
- setTimestamp(timestamp) {
2203
- this.data.timestamp = timestamp;
2204
- this.build();
2205
- return this;
2376
+ };
2377
+
2378
+ // package.json
2379
+ var version = "1.0.30";
2380
+
2381
+ // src/client.ts
2382
+ var import_node_crypto3 = require("crypto");
2383
+ var import_qznt4 = require("qznt");
2384
+
2385
+ // src/utils/VimcordCLI.ts
2386
+ var import_node_readline = require("readline");
2387
+ var import_qznt3 = require("qznt");
2388
+
2389
+ // src/utils/clientUtils.ts
2390
+ function useClient(clientId = 0) {
2391
+ return Vimcord.instances.get(clientId);
2392
+ }
2393
+ async function useReadyClient(clientId = 0) {
2394
+ return useClient(clientId)?.waitForReady();
2395
+ }
2396
+ function createClient(options, features = {}, config = {}) {
2397
+ return new Vimcord(options, features, config);
2398
+ }
2399
+
2400
+ // src/utils/VimcordCLI.ts
2401
+ var VimcordCLI = class {
2402
+ rl;
2403
+ options;
2404
+ commands = /* @__PURE__ */ new Map();
2405
+ logger = new Logger({ prefixEmoji: "\u{1F680}", prefix: "CLI", showTimestamp: false });
2406
+ constructor(options) {
2407
+ this.options = options;
2408
+ this.rl = (0, import_node_readline.createInterface)({
2409
+ input: process.stdin,
2410
+ output: process.stdout,
2411
+ terminal: false
2412
+ });
2413
+ this.rl.on("line", (line) => {
2414
+ const { isCommand, commandName, content, args } = this.parseLine(line);
2415
+ if (!isCommand) return;
2416
+ const command = this.commands.get(commandName);
2417
+ if (!command) {
2418
+ const nearestMatches = Array.from(this.commands.keys()).filter(
2419
+ (cmd) => cmd.toLowerCase().includes(commandName.toLowerCase())
2420
+ );
2421
+ return this.logger.error(
2422
+ `Unknown command '${commandName}'${nearestMatches.length ? `. Did you mean ${nearestMatches.length > 1 ? `[${nearestMatches.map((m) => `'${this.options.prefix}${m}'`).join(", ")}]` : `'${this.options.prefix}${nearestMatches[0]}'`}?` : ""}`
2423
+ );
2424
+ }
2425
+ command.fn(args, content);
2426
+ });
2206
2427
  }
2207
- addFields(fields) {
2208
- this.data.fields = [...this.data.fields, ...fields];
2209
- this.build();
2210
- return this;
2428
+ parseLine(line) {
2429
+ if (line.startsWith(this.options.prefix)) {
2430
+ line = line.slice(this.options.prefix.length);
2431
+ } else {
2432
+ return { isCommand: false };
2433
+ }
2434
+ const args = line.split(" ").map((s) => s.trim());
2435
+ const commandName = args.shift();
2436
+ return { isCommand: true, commandName, content: args.join(" "), args };
2211
2437
  }
2212
- setFields(fields) {
2213
- this.data.fields = fields;
2214
- this.build();
2215
- return this;
2438
+ getClientInstance(line) {
2439
+ const clientIndex = import_qznt3.$.str.getFlag(line, "--client", 1) || import_qznt3.$.str.getFlag(line, "-c", 1);
2440
+ if (clientIndex) {
2441
+ const idx = Number(clientIndex);
2442
+ if (isNaN(idx)) {
2443
+ CLI.logger.error(`'${clientIndex}' is not a valid number`);
2444
+ return void 0;
2445
+ }
2446
+ const client = useClient(idx);
2447
+ if (!client) {
2448
+ CLI.logger.error("Client instance not found");
2449
+ return void 0;
2450
+ }
2451
+ return client;
2452
+ } else {
2453
+ const client = useClient(0);
2454
+ if (!client) {
2455
+ CLI.logger.error("Client instance not found");
2456
+ return void 0;
2457
+ }
2458
+ return client;
2459
+ }
2216
2460
  }
2217
- spliceFields(index, deleteCount, ...fields) {
2218
- this.data.fields.splice(index, deleteCount, ...fields);
2219
- this.build();
2220
- return this;
2461
+ addCommand(commandName, description, fn) {
2462
+ this.commands.set(commandName, { description, fn });
2463
+ }
2464
+ removeCommand(commandName) {
2465
+ if (!this.commands.has(commandName)) return false;
2466
+ this.commands.delete(commandName);
2467
+ return true;
2468
+ }
2469
+ };
2470
+ var initCalled = false;
2471
+ var CLI = new VimcordCLI({ prefix: "/" });
2472
+ CLI.addCommand("help", "View information about a command, or the available CLI options", (args) => {
2473
+ const prefix = CLI.options.prefix;
2474
+ const helpList = {};
2475
+ for (const cmd of CLI.commands.entries()) {
2476
+ const commandName = cmd[0];
2477
+ const commandDescription = cmd[1].description;
2478
+ helpList[`${prefix}${commandName}`] = `~ ${commandDescription}`;
2479
+ }
2480
+ CLI.logger.table("(help)", helpList);
2481
+ });
2482
+ CLI.addCommand("register", "Register app commands (slash & context) globally, or per guild", async (args, content) => {
2483
+ const client = CLI.getClientInstance(content);
2484
+ if (!client) return;
2485
+ const mode = args[0]?.toLowerCase() || "";
2486
+ if (!["guild", "global"].includes(mode)) {
2487
+ return CLI.logger.error(`'${mode}' is not a valid option. Your options are [guild|global]`);
2221
2488
  }
2222
- clone(overrides = {}) {
2223
- return new _BetterEmbed({ ...this.data, ...overrides });
2489
+ let guildIds = (import_qznt3.$.str.getFlag(content, "--guilds", 1) || import_qznt3.$.str.getFlag(content, "-g", 1) || "").replaceAll(/["']/g, "").split(" ").filter(Boolean).map((s) => s.replaceAll(",", "").trim());
2490
+ if (!guildIds.length) guildIds = client.guilds.cache.map((g) => g.id);
2491
+ switch (mode) {
2492
+ case "guild":
2493
+ CLI.logger.info("Registering guild commands...");
2494
+ await client.commands.registerGuild({ guilds: guildIds });
2495
+ break;
2496
+ case "global":
2497
+ CLI.logger.info("Registering global commands...");
2498
+ await client.commands.registerGlobal();
2499
+ break;
2224
2500
  }
2225
- toJSON() {
2226
- return this.embed.toJSON();
2501
+ });
2502
+ CLI.addCommand("unregister", "Unregister app commands globally, or per guild", async (args, content) => {
2503
+ const client = CLI.getClientInstance(content);
2504
+ if (!client) return;
2505
+ const mode = args[0]?.toLowerCase() || "";
2506
+ if (!["guild", "global"].includes(mode)) {
2507
+ return CLI.logger.error(`'${mode}' is not a valid option. Your options are [guild|global]`);
2227
2508
  }
2228
- async send(handler, options = {}, overrides) {
2229
- this.build();
2230
- if (options.content && this.data.acf) {
2231
- options.content = this.applyContextFormatting(options.content);
2232
- }
2233
- return await dynaSend(handler, {
2234
- ...options,
2235
- embeds: [
2236
- overrides ? this.clone(overrides) : this,
2237
- ...Array.isArray(options?.embeds) ? options?.embeds : options?.embeds ? [options?.embeds] : []
2238
- ]
2239
- });
2509
+ let guildIds = (import_qznt3.$.str.getFlag(content, "--guilds", 1) || import_qznt3.$.str.getFlag(content, "-g", 1) || "").replaceAll(/["']/g, "").split(" ").filter(Boolean).map((s) => s.replaceAll(",", "").trim());
2510
+ if (!guildIds.length) guildIds = client.guilds.cache.map((g) => g.id);
2511
+ switch (mode) {
2512
+ case "guild":
2513
+ CLI.logger.info("Unregistering guild commands...");
2514
+ await client.commands.unregisterGuild({ guilds: guildIds });
2515
+ break;
2516
+ case "global":
2517
+ CLI.logger.info("Unregistering global commands...");
2518
+ await client.commands.unregisterGlobal();
2519
+ break;
2240
2520
  }
2241
- };
2242
-
2243
- // src/utils/sendCommandErrorEmbed.ts
2244
- async function sendCommandErrorEmbed(client, error, guild, messageOrInteraction) {
2245
- if (!client.features.enableCommandErrorMessage) return null;
2246
- const config = typeof client.features.enableCommandErrorMessage !== "boolean" ? client.features.enableCommandErrorMessage : void 0;
2247
- const buttons = {
2248
- supportServer: new import_discord9.ButtonBuilder({
2249
- url: config?.inviteUrl || client.config.staff.guild.inviteUrl || "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
2250
- // may or may not be a rickroll
2251
- label: config?.inviteButtonLabel || "Support Support",
2252
- style: import_discord9.ButtonStyle.Link
2253
- }),
2254
- details: new import_discord9.ButtonBuilder({
2255
- customId: "btn_details",
2256
- label: config?.detailButtonLabel || "Details",
2257
- style: import_discord9.ButtonStyle.Secondary
2258
- })
2259
- };
2260
- const actionRow = new import_discord9.ActionRowBuilder({
2261
- components: config?.inviteUrl && guild?.id !== (config.inviteUrl || client.config.staff.guild.id) ? [buttons.supportServer, buttons.details] : [buttons.details]
2262
- });
2263
- const embed_error = config?.embed?.(new BetterEmbed(), error, guild) || new BetterEmbed({
2264
- color: "Red",
2265
- title: "Something went wrong",
2266
- description: "If you keep encountering this error, please report it."
2267
- });
2268
- const msg = await embed_error.send(messageOrInteraction, {
2269
- components: [actionRow],
2270
- flags: config?.ephemeral ? "Ephemeral" : void 0,
2271
- deleteAfter: config?.deleteAfter
2272
- });
2273
- if (!msg) return null;
2274
- const collector = msg.createMessageComponentCollector({
2275
- componentType: import_discord9.ComponentType.Button,
2276
- idle: config?.detailButtonIdleTimeout ?? 3e4,
2277
- filter: (i) => i.customId === "btn_details"
2278
- });
2279
- collector.on("collect", (i) => {
2280
- const attachment = new import_discord9.AttachmentBuilder(Buffer.from(`${error.message}
2281
-
2282
- ${error.stack}`), {
2283
- name: "error.txt"
2284
- });
2285
- i.reply({ files: [attachment], flags: "Ephemeral" });
2286
- });
2287
- collector.on("end", () => {
2288
- buttons.details.setDisabled(true);
2289
- embed_error.send(messageOrInteraction, {
2290
- sendMethod: messageOrInteraction instanceof import_discord9.Message ? 5 /* MessageEdit */ : void 0,
2291
- components: [actionRow]
2292
- });
2521
+ });
2522
+ CLI.addCommand("stats", "View statistics about a client instance", (args, content) => {
2523
+ const client = CLI.getClientInstance(content);
2524
+ if (!client) return;
2525
+ CLI.logger.table(`(stats) ~ ${client.config.app.name}`, {
2526
+ "Guilds:": import_qznt3.$.format.number(client.guilds.cache.size),
2527
+ "Ping:": `${client.ws.ping || 0}ms`,
2528
+ "Uptime:": `${import_qznt3.$.math.secs(client.uptime || 0)}s`,
2529
+ "Process Uptime:": `${Math.floor(process.uptime())}s`,
2530
+ "Memory Usage:": `${(process.memoryUsage().rss / 1024 / 1024).toFixed(2)} MB`
2293
2531
  });
2294
- return msg;
2295
- }
2296
-
2297
- // src/utils/async.ts
2298
- async function retryExponentialBackoff(fn, maxRetries = 3, retryDelay = 1e3) {
2299
- let attempts = 0;
2300
- while (true) {
2301
- try {
2302
- return await fn(attempts);
2303
- } catch (error) {
2304
- if (attempts >= maxRetries) throw error;
2305
- await new Promise((resolve) => setTimeout(resolve, Math.pow(1.75, attempts) * retryDelay + Math.random() * 500));
2306
- attempts++;
2532
+ });
2533
+ CLI.addCommand("cmds", "List the loaded commands", async (args, content) => {
2534
+ const client = CLI.getClientInstance(content);
2535
+ if (!client) return;
2536
+ const mode = (args[0] || "slash").toLowerCase();
2537
+ switch (mode) {
2538
+ case "slash": {
2539
+ const commands = Array.from(client.commands.slash.commands.values());
2540
+ commands.sort((a, b) => a.builder.name.localeCompare(b.builder.name));
2541
+ const tableData = {};
2542
+ for (const cmd of commands) {
2543
+ tableData[`/${cmd.builder.name}`] = `~ ${cmd.builder.description || "No description"}`;
2544
+ }
2545
+ return CLI.logger.table(`(cmds) ~ slash (${import_qznt3.$.format.number(commands.length)})`, tableData);
2546
+ }
2547
+ case "prefix": {
2548
+ const commands = Array.from(client.commands.prefix.commands.values());
2549
+ commands.sort((a, b) => {
2550
+ const nameA = a.toConfig().name;
2551
+ const nameB = b.toConfig().name;
2552
+ return nameA.localeCompare(nameB);
2553
+ });
2554
+ const tableData = {};
2555
+ const defaultPrefix = client.config.prefixCommands.defaultPrefix;
2556
+ for (const cmd of commands) {
2557
+ const config = cmd.toConfig();
2558
+ const aliasIndicator = config.aliases?.length ? ` [${config.aliases.join(", ")}]` : "";
2559
+ tableData[`${defaultPrefix}${config.name}${aliasIndicator}`] = `~ ${config.description || "No description"}`;
2560
+ }
2561
+ return CLI.logger.table(`(cmds) ~ prefix (${import_qznt3.$.format.number(commands.length)})`, tableData);
2562
+ }
2563
+ case "ctx": {
2564
+ const commands = Array.from(client.commands.context.commands.values());
2565
+ commands.sort((a, b) => a.builder.name.localeCompare(b.builder.name));
2566
+ const tableData = {};
2567
+ for (const cmd of commands) {
2568
+ const type = cmd.builder.type === 2 ? "User" : "Msg";
2569
+ tableData[`[${type}] ${cmd.builder.name}`] = "";
2570
+ }
2571
+ return CLI.logger.table(`(cmds) ~ ctx (${import_qznt3.$.format.number(commands.length)})`, tableData);
2307
2572
  }
2573
+ default:
2574
+ return CLI.logger.error(`'${mode}' is not a valid option. Valid options: [slash|prefix|ctx]`);
2308
2575
  }
2576
+ });
2577
+ function initCLI() {
2578
+ if (initCalled) return;
2579
+ CLI.logger.log(`~ Type ${CLI.options.prefix}help to view available commands`);
2580
+ initCalled = true;
2309
2581
  }
2310
2582
 
2311
- // package.json
2312
- var version = "1.0.27";
2313
-
2314
2583
  // src/client.ts
2315
- var import_node_crypto3 = require("crypto");
2316
2584
  var import_chalk2 = __toESM(require("chalk"));
2317
- var clientInstances = [];
2318
- var Vimcord = class _Vimcord extends import_discord10.Client {
2585
+ var Vimcord = class _Vimcord extends import_discord11.Client {
2586
+ static instances = /* @__PURE__ */ new Map();
2319
2587
  uuid = (0, import_node_crypto3.randomUUID)();
2320
- clientId = clientInstances.length;
2588
+ clientId = _Vimcord.instances.size;
2321
2589
  clientOptions;
2322
2590
  features;
2323
2591
  config;
@@ -2420,13 +2688,16 @@ var Vimcord = class _Vimcord extends import_discord10.Client {
2420
2688
  this.on("error", (err) => this.logger.error("Client Error", err));
2421
2689
  this.on("shardError", (err) => this.logger.error("Client Shard Error", err));
2422
2690
  }
2423
- this.logger.clientBanner(this);
2691
+ if (!this.config.app.disableBanner === false) {
2692
+ this.logger.clientBanner(this);
2693
+ }
2424
2694
  this.once("clientReady", (client) => {
2425
2695
  this.logger.clientReady(client.user.tag, client.guilds.cache.size);
2426
2696
  });
2427
- clientInstances.push(this);
2697
+ _Vimcord.instances.set(this.clientId, this);
2698
+ initCLI();
2428
2699
  }
2429
- /** Returns the options, features, and config of this client */
2700
+ /** Returns the options, features, and config of this client. */
2430
2701
  toJSON() {
2431
2702
  return {
2432
2703
  options: this.clientOptions,
@@ -2434,7 +2705,7 @@ var Vimcord = class _Vimcord extends import_discord10.Client {
2434
2705
  config: this.config
2435
2706
  };
2436
2707
  }
2437
- /** Make a clone of this client */
2708
+ /** Makes a clone of this client. */
2438
2709
  clone() {
2439
2710
  const { options, features, config } = this.toJSON();
2440
2711
  return new _Vimcord(options, features, config);
@@ -2450,31 +2721,31 @@ var Vimcord = class _Vimcord extends import_discord10.Client {
2450
2721
  this.config.staff = createVimcordStaffConfig(options);
2451
2722
  return this;
2452
2723
  }
2453
- configureSlashCommands(options) {
2724
+ configureSlashCommands(options = {}) {
2454
2725
  this.config.slashCommands = createVimcordSlashCommandConfig(options);
2455
2726
  return this;
2456
2727
  }
2457
- configurePrefixCommands(options) {
2728
+ configurePrefixCommands(options = {}) {
2458
2729
  this.config.prefixCommands = createVimcordPrefixCommandConfig(options);
2459
2730
  return this;
2460
2731
  }
2461
- configureContextCommands(options) {
2732
+ configureContextCommands(options = {}) {
2462
2733
  this.config.contextCommands = createVimcordContextCommandConfig(options);
2463
2734
  return this;
2464
2735
  }
2465
- async addEventModules(dir, replaceAll) {
2736
+ async importEventModules(dir, replaceAll) {
2466
2737
  await this.events.importFrom(dir, replaceAll);
2467
2738
  return this;
2468
2739
  }
2469
- async addSlashCommandModules(dir, replaceAll) {
2740
+ async importSlashCommandModules(dir, replaceAll) {
2470
2741
  await this.commands.slash.importFrom(dir, replaceAll);
2471
2742
  return this;
2472
2743
  }
2473
- async addPrefixCommandModules(dir, replaceAll) {
2744
+ async importPrefixCommandModules(dir, replaceAll) {
2474
2745
  await this.commands.prefix.importFrom(dir, replaceAll);
2475
2746
  return this;
2476
2747
  }
2477
- async addContextCommandModules(dir, replaceAll) {
2748
+ async importContextCommandModules(dir, replaceAll) {
2478
2749
  await this.commands.context.importFrom(dir, replaceAll);
2479
2750
  return this;
2480
2751
  }
@@ -2483,10 +2754,13 @@ var Vimcord = class _Vimcord extends import_discord10.Client {
2483
2754
  this.logger.database("Using", db.moduleName);
2484
2755
  return this.db.connect();
2485
2756
  }
2486
- async whenReady() {
2757
+ async waitForReady() {
2487
2758
  if (this.isReady()) return this;
2488
2759
  return new Promise((resolve, reject) => {
2489
- const timeout = setTimeout(() => reject(new Error("Client is not ready")), 45e3);
2760
+ const timeout = setTimeout(
2761
+ () => reject(new Error(`Client (i${this.clientId}) timed out waiting for ready`)),
2762
+ 6e4
2763
+ );
2490
2764
  this.once("clientReady", () => {
2491
2765
  clearTimeout(timeout);
2492
2766
  resolve(this);
@@ -2502,20 +2776,20 @@ var Vimcord = class _Vimcord extends import_discord10.Client {
2502
2776
  if (this.features.importModules) {
2503
2777
  const importModules = this.features.importModules;
2504
2778
  await Promise.all([
2505
- importModules.events && this.addEventModules(importModules.events),
2506
- importModules.slashCommands && this.addSlashCommandModules(importModules.slashCommands),
2507
- importModules.prefixCommands && this.addPrefixCommandModules(importModules.prefixCommands),
2508
- importModules.contextCommands && this.addContextCommandModules(importModules.contextCommands)
2779
+ importModules.events && this.importEventModules(importModules.events),
2780
+ importModules.slashCommands && this.importSlashCommandModules(importModules.slashCommands),
2781
+ importModules.prefixCommands && this.importPrefixCommandModules(importModules.prefixCommands),
2782
+ importModules.contextCommands && this.importContextCommandModules(importModules.contextCommands)
2509
2783
  ]);
2510
2784
  }
2511
2785
  if (this.features.useDefaultSlashCommandHandler) {
2512
- this.events.register(defaultSlashCommandHandler);
2513
- }
2514
- if (this.features.useDefaultPrefixCommandHandler) {
2515
- this.events.register(defaultPrefixCommandHandler);
2786
+ this.events.register(BUILTIN_SlashCommandHandler);
2516
2787
  }
2517
2788
  if (this.features.useDefaultContextCommandHandler) {
2518
- this.events.register(defaultContextCommandHandler);
2789
+ this.events.register(BUILTIN_ContextCommandHandler);
2790
+ }
2791
+ if (this.features.useDefaultPrefixCommandHandler) {
2792
+ this.events.register(BUILTIN_PrefixCommandHandler);
2519
2793
  }
2520
2794
  return this;
2521
2795
  }
@@ -2535,161 +2809,107 @@ var Vimcord = class _Vimcord extends import_discord10.Client {
2535
2809
  await tokenOrPreHook(this);
2536
2810
  } else {
2537
2811
  await preHook?.(this);
2538
- }
2539
- const stopLoader = this.logger.loader("Connecting to Discord...");
2540
- const loginResult = await retryExponentialBackoff(
2541
- () => super.login(token),
2542
- this.features.loginAttempts ?? 3,
2543
- 1e3
2544
- );
2545
- stopLoader("Connected to Discord ");
2546
- this.config.app.verbose && this.logger.debug("\u23F3 Waiting for ready...");
2547
- return loginResult;
2548
- } catch (err) {
2549
- this.logger.error(
2550
- `Failed to log into Discord after ${this.features.loginAttempts} attempt(s))`,
2551
- err
2552
- );
2553
- return null;
2554
- } finally {
2555
- this.clientStartingPromise = null;
2556
- }
2557
- };
2558
- this.clientStartingPromise = main();
2559
- return this.clientStartingPromise;
2560
- }
2561
- async kill() {
2562
- await super.destroy();
2563
- const idx = clientInstances.indexOf(this);
2564
- if (idx >= 0) clientInstances.splice(idx, 1);
2565
- this.logger.debug("\u{1F6AA} Logged out of Discord");
2566
- }
2567
- /** Shortcut for {@link fetchUser tools.fetchUser} */
2568
- async fetchUser(id) {
2569
- const client = await this.whenReady();
2570
- return fetchUser(client, id);
2571
- }
2572
- /** Shortcut for {@link fetchGuild tools.fetchGuild} */
2573
- async fetchGuild(id) {
2574
- const client = await this.whenReady();
2575
- return fetchGuild(client, id);
2576
- }
2577
- };
2578
- var defaultPrefixCommandHandler = new EventBuilder({
2579
- event: "messageCreate",
2580
- name: "PrefixCommandHandler",
2581
- async execute(client, message) {
2582
- if (message.author.bot || !message.guild) return;
2583
- const config = client.config.prefixCommands;
2584
- let activePrefix = config.defaultPrefix;
2585
- if (config.guildPrefixResolver) {
2586
- try {
2587
- const customPrefix = await config.guildPrefixResolver(client, message.guild.id);
2588
- if (customPrefix) activePrefix = customPrefix;
2589
- } catch (err) {
2590
- client.logger.error(`Error in guildPrefixResolver for guild ${message.guild.id}:`, err);
2591
- }
2592
- }
2593
- let prefixUsed;
2594
- if (message.content.startsWith(activePrefix)) {
2595
- prefixUsed = activePrefix;
2596
- } else if (config.allowMentionAsPrefix) {
2597
- const mention = (0, import_discord10.userMention)(client.user.id);
2598
- if (message.content.startsWith(mention)) {
2599
- prefixUsed = message.content.startsWith(`${mention} `) ? `${mention} ` : mention;
2600
- }
2601
- }
2602
- if (!prefixUsed) return;
2603
- const contentWithoutPrefix = message.content.slice(prefixUsed.length).trim();
2604
- const args = contentWithoutPrefix.split(/\s+/);
2605
- const trigger = args.shift();
2606
- if (!trigger) return;
2607
- const command = client.commands.prefix.get(trigger);
2608
- if (!command) return;
2609
- message.content = args.join(" ");
2610
- try {
2611
- return await command.run(client, client, message);
2612
- } catch (err) {
2613
- await sendCommandErrorEmbed(client, err, message.guild, message);
2614
- throw err;
2615
- }
2616
- }
2617
- });
2618
- var defaultSlashCommandHandler = new EventBuilder({
2619
- event: "interactionCreate",
2620
- name: "SlashCommandHandler",
2621
- async execute(client, interaction) {
2622
- if (!interaction.isChatInputCommand()) return;
2623
- const command = client.commands.slash.get(interaction.commandName);
2624
- if (!command) {
2625
- const content = `**/\`${interaction.commandName}\`** is not a registered command.`;
2626
- if (interaction.replied || interaction.deferred) {
2627
- return interaction.followUp({ content, flags: "Ephemeral" });
2628
- }
2629
- return interaction.reply({ content, flags: "Ephemeral" });
2630
- }
2631
- try {
2632
- return await command.run(client, client, interaction);
2633
- } catch (err) {
2634
- await sendCommandErrorEmbed(client, err, interaction.guild, interaction);
2635
- throw err;
2636
- }
2637
- }
2638
- });
2639
- var defaultContextCommandHandler = new EventBuilder({
2640
- event: "interactionCreate",
2641
- name: "ContextCommandHandler",
2642
- async execute(client, interaction) {
2643
- if (!interaction.isContextMenuCommand()) return;
2644
- const command = client.commands.context.get(interaction.commandName);
2645
- if (!command) {
2646
- const content = `**${interaction.commandName}** is not a registered context command.`;
2647
- if (interaction.replied || interaction.deferred) {
2648
- return interaction.followUp({ content, flags: "Ephemeral" });
2812
+ }
2813
+ const stopLoader = this.logger.loader("Connecting to Discord...");
2814
+ const loginResult = await import_qznt4.$.async.retry(() => super.login(token), this.features.loginAttempts ?? 3, 1e3);
2815
+ stopLoader("Connected to Discord ");
2816
+ this.config.app.verbose && this.logger.debug("\u23F3 Waiting for ready...");
2817
+ return loginResult;
2818
+ } catch (err) {
2819
+ this.logger.error(
2820
+ `Failed to log into Discord after ${this.features.loginAttempts} attempt(s))`,
2821
+ err
2822
+ );
2823
+ return null;
2824
+ } finally {
2825
+ this.clientStartingPromise = null;
2649
2826
  }
2650
- return interaction.reply({ content, flags: "Ephemeral" });
2651
- }
2652
- try {
2653
- return await command.run(client, client, interaction);
2654
- } catch (err) {
2655
- await sendCommandErrorEmbed(client, err, interaction.guild, interaction);
2656
- throw err;
2657
- }
2827
+ };
2828
+ this.clientStartingPromise = main();
2829
+ return this.clientStartingPromise;
2658
2830
  }
2659
- });
2831
+ async kill() {
2832
+ await super.destroy();
2833
+ _Vimcord.instances.delete(this.clientId);
2834
+ this.logger.debug("\u{1F6AA} Logged out of Discord");
2835
+ }
2836
+ /** Shortcut for {@link fetchUser tools.fetchUser} */
2837
+ async fetchUser(id) {
2838
+ const client = await this.waitForReady();
2839
+ return fetchUser(client, id);
2840
+ }
2841
+ /** Shortcut for {@link fetchGuild tools.fetchGuild} */
2842
+ async fetchGuild(id) {
2843
+ const client = await this.waitForReady();
2844
+ return fetchGuild(client, id);
2845
+ }
2846
+ };
2660
2847
 
2661
2848
  // src/modules/db/mongo/mongo.ts
2662
2849
  var import_mongoose = __toESM(require("mongoose"));
2663
2850
  var import_node_events2 = __toESM(require("events"));
2664
- var import_qznt3 = require("qznt");
2851
+ var import_qznt5 = require("qznt");
2665
2852
  try {
2666
2853
  import("mongoose");
2667
2854
  } catch {
2668
2855
  throw new Error("MongoDatabase requires the mongoose package, install it with `npm install mongoose`");
2669
2856
  }
2670
- var instances = /* @__PURE__ */ new Map();
2671
- var emitter = new import_node_events2.default();
2672
2857
  var MongoDatabase = class _MongoDatabase {
2858
+ static instances = /* @__PURE__ */ new Map();
2859
+ static emitter = new import_node_events2.default();
2673
2860
  moduleName = "MongoDatabase";
2674
2861
  clientId;
2675
2862
  client;
2676
2863
  mongoose;
2677
2864
  isConnecting = false;
2678
- static getInstance(instanceId) {
2679
- const id = (typeof instanceId === "number" ? instanceId : instanceId?.clientId) ?? 0;
2680
- return instances.get(id);
2865
+ /**
2866
+ * Returns an instance of MongoDatabase.
2867
+ * @param clientId [default: 0]
2868
+ */
2869
+ static getInstance(clientId) {
2870
+ const id = (typeof clientId === "number" ? clientId : clientId?.clientId) ?? 0;
2871
+ return _MongoDatabase.instances.get(id);
2681
2872
  }
2682
- static async getReadyInstance(instanceId) {
2683
- return await _MongoDatabase.getInstance(instanceId)?.waitForReady();
2873
+ /**
2874
+ * Waits for a MongoDatabase instance to be ready. First waiting for the instance to initialize if it doesn't exist.
2875
+ * @param clientId [default: 0]
2876
+ * @param timeoutMs [default: 60000]
2877
+ */
2878
+ static async getReadyInstance(clientId, timeoutMs = 6e4) {
2879
+ const existing = _MongoDatabase.getInstance(clientId);
2880
+ if (existing) return await existing.waitForReady();
2881
+ return new Promise((resolve, reject) => {
2882
+ const timeout = setTimeout(() => {
2883
+ _MongoDatabase.emitter.off("ready", listener);
2884
+ reject(
2885
+ new Error(
2886
+ `MongoDatabase instance (${clientId}) failed to initialize within ${timeoutMs / 1e3}s. Check your connection logic.`
2887
+ )
2888
+ );
2889
+ }, timeoutMs);
2890
+ const listener = (db) => {
2891
+ if (db.clientId === clientId) {
2892
+ clearTimeout(timeout);
2893
+ _MongoDatabase.emitter.off("ready", listener);
2894
+ resolve(db);
2895
+ }
2896
+ };
2897
+ _MongoDatabase.emitter.on("ready", listener);
2898
+ });
2684
2899
  }
2685
- static async startSession(options, instanceId) {
2686
- return (await _MongoDatabase.getReadyInstance(instanceId))?.startSession(options);
2900
+ /**
2901
+ * Starts a new Mongo client session.
2902
+ * @param options Options for the new session
2903
+ * @param clientId [default: 0]
2904
+ */
2905
+ static async startSession(options, clientId) {
2906
+ return (await _MongoDatabase.getReadyInstance(clientId))?.startSession(options);
2687
2907
  }
2688
2908
  constructor(client, options) {
2689
2909
  this.client = client;
2690
2910
  this.mongoose = new import_mongoose.default.Mongoose(options);
2691
2911
  this.clientId = this.client.clientId;
2692
- instances.set(this.clientId, this);
2912
+ _MongoDatabase.instances.set(this.clientId, this);
2693
2913
  }
2694
2914
  get connection() {
2695
2915
  return this.mongoose.connection;
@@ -2699,7 +2919,7 @@ var MongoDatabase = class _MongoDatabase {
2699
2919
  }
2700
2920
  async waitForReady() {
2701
2921
  if (!this.isReady && this.isConnecting) {
2702
- return new Promise((resolve) => emitter.once("ready", (db) => resolve(db)));
2922
+ return new Promise((resolve) => _MongoDatabase.emitter.once("ready", (db) => resolve(db)));
2703
2923
  }
2704
2924
  return this;
2705
2925
  }
@@ -2709,7 +2929,7 @@ var MongoDatabase = class _MongoDatabase {
2709
2929
  return true;
2710
2930
  }
2711
2931
  if (!this.isReady && this.isConnecting) {
2712
- return new Promise((resolve) => emitter.once("ready", () => resolve(true)));
2932
+ return new Promise((resolve) => _MongoDatabase.emitter.once("ready", () => resolve(true)));
2713
2933
  }
2714
2934
  const connectionUri = uri ?? (this.client.config.app.devMode ? process.env.MONGO_URI_DEV : process.env.MONGO_URI);
2715
2935
  options = { ...options, maxRetries: options.maxRetries ?? 3 };
@@ -2721,11 +2941,11 @@ var MongoDatabase = class _MongoDatabase {
2721
2941
  this.isConnecting = true;
2722
2942
  try {
2723
2943
  const stopLoader = this.client.logger.loader("Connecting to MongoDB...");
2724
- await import_qznt3.$.async.retry(
2944
+ await import_qznt5.$.async.retry(
2725
2945
  () => this.mongoose.connect(connectionUri, { autoIndex: true, ...connectionOptions }),
2726
2946
  maxRetries
2727
2947
  );
2728
- emitter.emit("ready", this);
2948
+ _MongoDatabase.emitter.emit("ready", this);
2729
2949
  stopLoader("Connected to MongoDB ");
2730
2950
  } catch (err) {
2731
2951
  this.client.logger.error(`Failed to connect to MongoDB after ${maxRetries} attempt(s)`, err);
@@ -2758,7 +2978,7 @@ var MongoDatabase = class _MongoDatabase {
2758
2978
  // src/modules/db/mongo/mongoSchema.builder.ts
2759
2979
  var import_mongoose2 = require("mongoose");
2760
2980
  var import_node_crypto4 = require("crypto");
2761
- var import_qznt4 = require("qznt");
2981
+ var import_qznt6 = require("qznt");
2762
2982
  try {
2763
2983
  import("mongoose");
2764
2984
  } catch {
@@ -2795,7 +3015,7 @@ var MongoSchemaBuilder = class _MongoSchemaBuilder {
2795
3015
  this.schema = new import_mongoose2.Schema(definition, { versionKey: false });
2796
3016
  this.logger = new Logger({
2797
3017
  prefixEmoji: "\u{1F96D}",
2798
- prefix: `MongoSchema (c${instanceId}) [${collection}]`,
3018
+ prefix: `MongoSchema (i${instanceId}) [${collection}]`,
2799
3019
  colors: { primary: "#F29B58" }
2800
3020
  });
2801
3021
  for (const plugin of _MongoSchemaBuilder.globalPlugins) {
@@ -2805,7 +3025,7 @@ var MongoSchemaBuilder = class _MongoSchemaBuilder {
2805
3025
  async getModel() {
2806
3026
  if (this.model) return this.model;
2807
3027
  if (this.compilingModel) return this.compilingModel;
2808
- this.compilingModel = (async () => {
3028
+ const fn = async () => {
2809
3029
  this.db = await MongoDatabase.getReadyInstance(this.instanceId) || null;
2810
3030
  if (!this.db) {
2811
3031
  throw new Error(`MongoDatabase instance (${this.instanceId}) not found for schema ${this.collection}`);
@@ -2814,10 +3034,11 @@ var MongoSchemaBuilder = class _MongoSchemaBuilder {
2814
3034
  if (this.db?.client.config.app.verbose) {
2815
3035
  this.logger.debug(`Compiled! | ${this.db?.client.config.app.name}`);
2816
3036
  }
3037
+ this.compilingModel = null;
2817
3038
  return this.model;
2818
- })();
3039
+ };
3040
+ this.compilingModel = fn();
2819
3041
  const res = await this.compilingModel;
2820
- this.compilingModel = null;
2821
3042
  return res;
2822
3043
  }
2823
3044
  extend(extras) {
@@ -2843,7 +3064,7 @@ var MongoSchemaBuilder = class _MongoSchemaBuilder {
2843
3064
  * @param maxRetries [default: 3]
2844
3065
  */
2845
3066
  async execute(fn, maxRetries = 3) {
2846
- return await import_qznt4.$.async.retry(async () => {
3067
+ return await import_qznt6.$.async.retry(async () => {
2847
3068
  const model = await this.getModel();
2848
3069
  return await fn(model);
2849
3070
  }, maxRetries);
@@ -3132,9 +3353,9 @@ var BetterCollector = class _BetterCollector {
3132
3353
  };
3133
3354
 
3134
3355
  // src/tools/BetterContainer.ts
3135
- var import_discord11 = require("discord.js");
3356
+ var import_discord12 = require("discord.js");
3136
3357
  var BetterContainer = class {
3137
- container = new import_discord11.ContainerBuilder();
3358
+ container = new import_discord12.ContainerBuilder();
3138
3359
  data;
3139
3360
  config;
3140
3361
  constructor(data = {}) {
@@ -3202,8 +3423,8 @@ var BetterContainer = class {
3202
3423
  )
3203
3424
  );
3204
3425
  }
3205
- if (data.thumbnail) sb.setThumbnailAccessory(new import_discord11.ThumbnailBuilder(data.thumbnail));
3206
- if (data.button) sb.setButtonAccessory(new import_discord11.ButtonBuilder(data.button));
3426
+ if (data.thumbnail) sb.setThumbnailAccessory(new import_discord12.ThumbnailBuilder(data.thumbnail));
3427
+ if (data.button) sb.setButtonAccessory(new import_discord12.ButtonBuilder(data.button));
3207
3428
  return sb;
3208
3429
  });
3209
3430
  return this;
@@ -3227,11 +3448,8 @@ var BetterContainer = class {
3227
3448
  };
3228
3449
 
3229
3450
  // src/tools/BetterModal.ts
3230
- var import_discord12 = require("discord.js");
3231
- function randomCharString(length) {
3232
- const chars = "ABCDEFGHJKLMOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
3233
- return Array.from({ length }, () => chars.charAt(Math.floor(Math.random() * chars.length))).join("");
3234
- }
3451
+ var import_discord13 = require("discord.js");
3452
+ var import_qznt7 = require("qznt");
3235
3453
  var BetterModal = class {
3236
3454
  id;
3237
3455
  options;
@@ -3241,7 +3459,7 @@ var BetterModal = class {
3241
3459
  constructor(options = {}) {
3242
3460
  this.id = options.id || this.createModalId();
3243
3461
  this.options = options;
3244
- this.modal = new import_discord12.ModalBuilder().setCustomId(this.id);
3462
+ this.modal = new import_discord13.ModalBuilder().setCustomId(this.id);
3245
3463
  this.config = options.config || globalVimcordToolsConfig;
3246
3464
  if (options.title) {
3247
3465
  this.setTitle(options.title);
@@ -3251,10 +3469,10 @@ var BetterModal = class {
3251
3469
  }
3252
3470
  }
3253
3471
  createModalId() {
3254
- return `modal:${randomCharString(10)}-${Date.now()}`;
3472
+ return `modal:${import_qznt7.$.rnd.str(10, "alpha", { casing: "mixed" })}-${Date.now()}`;
3255
3473
  }
3256
3474
  createComponentId() {
3257
- return `modal-component:${randomCharString(4)}-${Date.now().toString().slice(-4)}`;
3475
+ return `modal-component:${this.id}-${import_qznt7.$.rnd.str(4, "alpha", { casing: "mixed" })}-${Date.now().toString().slice(-4)}`;
3258
3476
  }
3259
3477
  validateComponentLength() {
3260
3478
  if (this.components.size >= 5) throw new Error("Modal can only have 5 components");
@@ -3299,9 +3517,9 @@ var BetterModal = class {
3299
3517
  this.validateComponentLength();
3300
3518
  let { label, description, custom_id, ...rest } = data;
3301
3519
  custom_id ||= this.createComponentId();
3302
- const textInputComponent = new import_discord12.TextInputBuilder(rest).setCustomId(custom_id);
3303
- if (!rest.style) textInputComponent.setStyle(import_discord12.TextInputStyle.Short);
3304
- const labelComponent = new import_discord12.LabelBuilder().setLabel(label).setTextInputComponent(textInputComponent);
3520
+ const textInputComponent = new import_discord13.TextInputBuilder(rest).setCustomId(custom_id);
3521
+ if (!rest.style) textInputComponent.setStyle(import_discord13.TextInputStyle.Short);
3522
+ const labelComponent = new import_discord13.LabelBuilder().setLabel(label).setTextInputComponent(textInputComponent);
3305
3523
  if (description) labelComponent.setDescription(description);
3306
3524
  this.components.set(custom_id, labelComponent);
3307
3525
  return this;
@@ -3310,8 +3528,8 @@ var BetterModal = class {
3310
3528
  this.validateComponentLength();
3311
3529
  let { label, description, custom_id, ...rest } = data;
3312
3530
  custom_id ||= this.createComponentId();
3313
- const stringSelectComponent = new import_discord12.StringSelectMenuBuilder(rest).setCustomId(custom_id);
3314
- const labelComponent = new import_discord12.LabelBuilder().setLabel(label).setStringSelectMenuComponent(stringSelectComponent);
3531
+ const stringSelectComponent = new import_discord13.StringSelectMenuBuilder(rest).setCustomId(custom_id);
3532
+ const labelComponent = new import_discord13.LabelBuilder().setLabel(label).setStringSelectMenuComponent(stringSelectComponent);
3315
3533
  if (description) labelComponent.setDescription(description);
3316
3534
  this.components.set(custom_id, labelComponent);
3317
3535
  return this;
@@ -3320,8 +3538,8 @@ var BetterModal = class {
3320
3538
  this.validateComponentLength();
3321
3539
  let { label, description, custom_id, ...rest } = data;
3322
3540
  custom_id ||= this.createComponentId();
3323
- const channelSelectComponent = new import_discord12.ChannelSelectMenuBuilder(rest).setCustomId(custom_id);
3324
- const labelComponent = new import_discord12.LabelBuilder().setLabel(label).setChannelSelectMenuComponent(channelSelectComponent);
3541
+ const channelSelectComponent = new import_discord13.ChannelSelectMenuBuilder(rest).setCustomId(custom_id);
3542
+ const labelComponent = new import_discord13.LabelBuilder().setLabel(label).setChannelSelectMenuComponent(channelSelectComponent);
3325
3543
  if (description) labelComponent.setDescription(description);
3326
3544
  this.components.set(custom_id, labelComponent);
3327
3545
  return this;
@@ -3330,8 +3548,8 @@ var BetterModal = class {
3330
3548
  this.validateComponentLength();
3331
3549
  let { label, description, custom_id, ...rest } = data;
3332
3550
  custom_id ||= this.createComponentId();
3333
- const userSelectComponent = new import_discord12.UserSelectMenuBuilder(rest).setCustomId(custom_id);
3334
- const labelComponent = new import_discord12.LabelBuilder().setLabel(label).setUserSelectMenuComponent(userSelectComponent);
3551
+ const userSelectComponent = new import_discord13.UserSelectMenuBuilder(rest).setCustomId(custom_id);
3552
+ const labelComponent = new import_discord13.LabelBuilder().setLabel(label).setUserSelectMenuComponent(userSelectComponent);
3335
3553
  if (description) labelComponent.setDescription(description);
3336
3554
  this.components.set(custom_id, labelComponent);
3337
3555
  return this;
@@ -3340,8 +3558,8 @@ var BetterModal = class {
3340
3558
  this.validateComponentLength();
3341
3559
  let { label, description, custom_id, ...rest } = data;
3342
3560
  custom_id ||= this.createComponentId();
3343
- const roleSelectComponent = new import_discord12.RoleSelectMenuBuilder(rest).setCustomId(custom_id);
3344
- const labelComponent = new import_discord12.LabelBuilder().setLabel(label).setRoleSelectMenuComponent(roleSelectComponent);
3561
+ const roleSelectComponent = new import_discord13.RoleSelectMenuBuilder(rest).setCustomId(custom_id);
3562
+ const labelComponent = new import_discord13.LabelBuilder().setLabel(label).setRoleSelectMenuComponent(roleSelectComponent);
3345
3563
  if (description) labelComponent.setDescription(description);
3346
3564
  this.components.set(custom_id, labelComponent);
3347
3565
  return this;
@@ -3350,8 +3568,8 @@ var BetterModal = class {
3350
3568
  this.validateComponentLength();
3351
3569
  let { label, description, custom_id, ...rest } = data;
3352
3570
  custom_id ||= this.createComponentId();
3353
- const mentionableSelectComponent = new import_discord12.MentionableSelectMenuBuilder(rest).setCustomId(custom_id);
3354
- const labelComponent = new import_discord12.LabelBuilder().setLabel(label).setMentionableSelectMenuComponent(mentionableSelectComponent);
3571
+ const mentionableSelectComponent = new import_discord13.MentionableSelectMenuBuilder(rest).setCustomId(custom_id);
3572
+ const labelComponent = new import_discord13.LabelBuilder().setLabel(label).setMentionableSelectMenuComponent(mentionableSelectComponent);
3355
3573
  if (description) labelComponent.setDescription(description);
3356
3574
  this.components.set(custom_id, labelComponent);
3357
3575
  return this;
@@ -3360,13 +3578,16 @@ var BetterModal = class {
3360
3578
  this.validateComponentLength();
3361
3579
  let { label, description, custom_id, ...rest } = data;
3362
3580
  custom_id ||= this.createComponentId();
3363
- const fileUploadComponent = new import_discord12.FileUploadBuilder(rest).setCustomId(custom_id);
3364
- const labelComponent = new import_discord12.LabelBuilder().setLabel(label).setFileUploadComponent(fileUploadComponent);
3581
+ const fileUploadComponent = new import_discord13.FileUploadBuilder(rest).setCustomId(custom_id);
3582
+ const labelComponent = new import_discord13.LabelBuilder().setLabel(label).setFileUploadComponent(fileUploadComponent);
3365
3583
  if (description) labelComponent.setDescription(description);
3366
3584
  this.components.set(custom_id, labelComponent);
3367
3585
  return this;
3368
3586
  }
3369
- /** Show the modal via interaction */
3587
+ /**
3588
+ * Shows the modal via interaction.
3589
+ * @param interaction The interaction used to show the modal
3590
+ */
3370
3591
  async show(interaction) {
3371
3592
  if (!("showModal" in interaction)) throw new Error("Interaction does not support showing modals");
3372
3593
  if (!this.modal.data.title) throw new Error("Modal must have a title");
@@ -3375,18 +3596,22 @@ var BetterModal = class {
3375
3596
  console.error("Modal failed to send", err);
3376
3597
  });
3377
3598
  }
3378
- /** Waits for the modal to be submitted and returns the component data
3599
+ /**
3600
+ * Waits for the modal to be submitted and returns the component data.
3379
3601
  * @param interaction The interaction used to show the modal
3380
3602
  * @param options Options */
3381
3603
  async awaitSubmit(interaction, options) {
3382
3604
  if (!("showModal" in interaction)) throw new Error("Interaction does not support showing modals");
3383
3605
  try {
3384
3606
  const modalSubmit = await interaction.awaitModalSubmit({
3385
- time: this.config.timeouts.modalSubmit,
3386
- ...options,
3387
- filter: (i) => i.customId === this.id
3607
+ filter: (i) => i.customId === this.id,
3608
+ time: options?.timeout ?? this.config.timeouts.modalSubmit,
3609
+ ...options
3388
3610
  });
3389
- const fields = {};
3611
+ if (options?.autoDefer) {
3612
+ await modalSubmit.deferUpdate();
3613
+ }
3614
+ const fields = /* @__PURE__ */ new Map();
3390
3615
  const values = [];
3391
3616
  for (const [customId] of this.components) {
3392
3617
  let value = null;
@@ -3401,23 +3626,34 @@ var BetterModal = class {
3401
3626
  } catch {
3402
3627
  }
3403
3628
  }
3404
- fields[customId] = value;
3629
+ fields.set(customId, value);
3405
3630
  values.push(value);
3406
3631
  }
3407
- return { fields, values, interaction: modalSubmit };
3632
+ return {
3633
+ getField(customId) {
3634
+ return fields.get(customId);
3635
+ },
3636
+ values,
3637
+ interaction: modalSubmit,
3638
+ reply: (options2) => dynaSend(modalSubmit, options2),
3639
+ deferUpdate: async (options2) => await modalSubmit.deferUpdate(options2)
3640
+ };
3408
3641
  } catch (error) {
3409
3642
  return null;
3410
3643
  }
3411
3644
  }
3645
+ /**
3646
+ * Shows the modal and waits for the modal to be submitted, returning the component data.
3647
+ * @param interaction The interaction used to show the modal
3648
+ * @param options Options */
3412
3649
  async showAndAwait(interaction, options) {
3413
- if (!("showModal" in interaction)) throw new Error("Interaction does not support showing modals");
3414
3650
  await this.show(interaction);
3415
3651
  return this.awaitSubmit(interaction, options);
3416
3652
  }
3417
3653
  };
3418
3654
 
3419
3655
  // src/tools/Paginator.ts
3420
- var import_discord13 = require("discord.js");
3656
+ var import_discord14 = require("discord.js");
3421
3657
  var import_node_events3 = __toESM(require("events"));
3422
3658
  var PaginationType = /* @__PURE__ */ ((PaginationType2) => {
3423
3659
  PaginationType2[PaginationType2["Short"] = 0] = "Short";
@@ -3438,7 +3674,7 @@ function wrapPositive(num, max) {
3438
3674
  }
3439
3675
  function createNavButton(id, config) {
3440
3676
  const data = config.paginator.buttons[id];
3441
- const btn = new import_discord13.ButtonBuilder({ customId: `btn_${id}`, style: import_discord13.ButtonStyle.Secondary });
3677
+ const btn = new import_discord14.ButtonBuilder({ customId: `btn_${id}`, style: import_discord14.ButtonStyle.Secondary });
3442
3678
  if (data.label) {
3443
3679
  btn.setLabel(data.label);
3444
3680
  } else {
@@ -3447,7 +3683,7 @@ function createNavButton(id, config) {
3447
3683
  return btn;
3448
3684
  }
3449
3685
  function isEmbed(item) {
3450
- return item instanceof import_discord13.EmbedBuilder || item instanceof BetterEmbed;
3686
+ return item instanceof import_discord14.EmbedBuilder || item instanceof BetterEmbed;
3451
3687
  }
3452
3688
  function resolvePages(pages) {
3453
3689
  if (Array.isArray(pages)) {
@@ -3484,7 +3720,7 @@ var Paginator = class {
3484
3720
  navigation: { reactions: [], isRequired: false, isLong: false, canJump: false },
3485
3721
  collectors: { component: null, reaction: null },
3486
3722
  components: {
3487
- chapterSelect: new import_discord13.StringSelectMenuBuilder({ customId: "ssm_chapterSelect" }),
3723
+ chapterSelect: new import_discord14.StringSelectMenuBuilder({ customId: "ssm_chapterSelect" }),
3488
3724
  navigation: {
3489
3725
  first: createNavButton("first", this.config),
3490
3726
  back: createNavButton("back", this.config),
@@ -3493,8 +3729,8 @@ var Paginator = class {
3493
3729
  last: createNavButton("last", this.config)
3494
3730
  },
3495
3731
  actionRows: {
3496
- chapterSelect: new import_discord13.ActionRowBuilder(),
3497
- navigation: new import_discord13.ActionRowBuilder()
3732
+ chapterSelect: new import_discord14.ActionRowBuilder(),
3733
+ navigation: new import_discord14.ActionRowBuilder()
3498
3734
  }
3499
3735
  }
3500
3736
  };
@@ -3594,9 +3830,9 @@ var Paginator = class {
3594
3830
  sendOptions.content = page;
3595
3831
  } else if (isEmbed(page)) {
3596
3832
  sendOptions.embeds.push(page);
3597
- } else if (page instanceof import_discord13.AttachmentBuilder) {
3833
+ } else if (page instanceof import_discord14.AttachmentBuilder) {
3598
3834
  sendOptions.files.push(page);
3599
- } else if (page instanceof import_discord13.ContainerBuilder || page instanceof BetterContainer) {
3835
+ } else if (page instanceof import_discord14.ContainerBuilder || page instanceof BetterContainer) {
3600
3836
  sendOptions.components.push(page);
3601
3837
  if (!sendOptions.flags.includes("IsComponentsV2")) {
3602
3838
  sendOptions.flags.push("IsComponentsV2");
@@ -3619,20 +3855,20 @@ var Paginator = class {
3619
3855
  }
3620
3856
  return component;
3621
3857
  });
3622
- const disabledNavRow = import_discord13.ActionRowBuilder.from(this.data.components.actionRows.navigation).setComponents(
3858
+ const disabledNavRow = import_discord14.ActionRowBuilder.from(this.data.components.actionRows.navigation).setComponents(
3623
3859
  disabledNavComponents
3624
3860
  );
3625
3861
  const newComponents = [];
3626
3862
  const currentPage = this.data.page.current;
3627
- if (currentPage instanceof import_discord13.ContainerBuilder || currentPage instanceof BetterContainer) {
3863
+ if (currentPage instanceof import_discord14.ContainerBuilder || currentPage instanceof BetterContainer) {
3628
3864
  newComponents.push(currentPage);
3629
3865
  }
3630
3866
  if (this.chapters.length > 1) {
3631
- const disabledSelect = import_discord13.StringSelectMenuBuilder.from(this.data.components.chapterSelect).setDisabled(
3867
+ const disabledSelect = import_discord14.StringSelectMenuBuilder.from(this.data.components.chapterSelect).setDisabled(
3632
3868
  true
3633
3869
  );
3634
3870
  newComponents.push(
3635
- import_discord13.ActionRowBuilder.from(this.data.components.actionRows.chapterSelect).setComponents(disabledSelect)
3871
+ import_discord14.ActionRowBuilder.from(this.data.components.actionRows.chapterSelect).setComponents(disabledSelect)
3636
3872
  );
3637
3873
  }
3638
3874
  if (disabledNavRow.components.length > 0) {
@@ -3661,7 +3897,7 @@ var Paginator = class {
3661
3897
  if (this.options.useReactions) {
3662
3898
  await this.data.message.reactions.removeAll().catch(Boolean);
3663
3899
  } else {
3664
- const newComponents = this.data.message.components.filter((c) => c.type !== import_discord13.ComponentType.Container);
3900
+ const newComponents = this.data.message.components.filter((c) => c.type !== import_discord14.ComponentType.Container);
3665
3901
  await this.data.message.edit({ components: newComponents }).catch(Boolean);
3666
3902
  }
3667
3903
  }
@@ -3872,7 +4108,7 @@ var Paginator = class {
3872
4108
  };
3873
4109
 
3874
4110
  // src/tools/Prompt.ts
3875
- var import_discord14 = require("discord.js");
4111
+ var import_discord15 = require("discord.js");
3876
4112
  var PromptResolveType = /* @__PURE__ */ ((PromptResolveType2) => {
3877
4113
  PromptResolveType2[PromptResolveType2["DisableComponents"] = 0] = "DisableComponents";
3878
4114
  PromptResolveType2[PromptResolveType2["ClearComponents"] = 1] = "ClearComponents";
@@ -3915,13 +4151,13 @@ var Prompt = class {
3915
4151
  buttonOptions?.confirm,
3916
4152
  "btn_confirm",
3917
4153
  this.config.prompt.confirmLabel,
3918
- import_discord14.ButtonStyle.Success
4154
+ import_discord15.ButtonStyle.Success
3919
4155
  );
3920
4156
  const reject = this.buildButton(
3921
4157
  buttonOptions?.reject,
3922
4158
  "btn_reject",
3923
4159
  this.config.prompt.rejectLabel,
3924
- import_discord14.ButtonStyle.Danger
4160
+ import_discord15.ButtonStyle.Danger
3925
4161
  );
3926
4162
  return { confirm, reject };
3927
4163
  }
@@ -3929,19 +4165,19 @@ var Prompt = class {
3929
4165
  const map = /* @__PURE__ */ new Map();
3930
4166
  if (!customOptions) return map;
3931
4167
  for (const [customId, { builder, handler, index = 2 }] of Object.entries(customOptions)) {
3932
- const button = this.buildButton(builder, customId, customId, import_discord14.ButtonStyle.Primary);
4168
+ const button = this.buildButton(builder, customId, customId, import_discord15.ButtonStyle.Primary);
3933
4169
  map.set(customId, { button, handler, index });
3934
4170
  }
3935
4171
  return map;
3936
4172
  }
3937
4173
  buildButton(option, customId, defaultLabel, defaultStyle) {
3938
4174
  if (typeof option === "function") {
3939
- return option(new import_discord14.ButtonBuilder());
4175
+ return option(new import_discord15.ButtonBuilder());
3940
4176
  }
3941
- if (option instanceof import_discord14.ButtonBuilder) {
4177
+ if (option instanceof import_discord15.ButtonBuilder) {
3942
4178
  return option;
3943
4179
  }
3944
- return new import_discord14.ButtonBuilder({
4180
+ return new import_discord15.ButtonBuilder({
3945
4181
  customId,
3946
4182
  label: defaultLabel,
3947
4183
  style: defaultStyle,
@@ -3949,12 +4185,12 @@ var Prompt = class {
3949
4185
  });
3950
4186
  }
3951
4187
  buildActionRow(disable = {}) {
3952
- const confirmBtn = disable.confirm ? new import_discord14.ButtonBuilder(this.buttons.confirm.data).setDisabled(true) : this.buttons.confirm;
3953
- const rejectBtn = disable.reject ? new import_discord14.ButtonBuilder(this.buttons.reject.data).setDisabled(true) : this.buttons.reject;
4188
+ const confirmBtn = disable.confirm ? new import_discord15.ButtonBuilder(this.buttons.confirm.data).setDisabled(true) : this.buttons.confirm;
4189
+ const rejectBtn = disable.reject ? new import_discord15.ButtonBuilder(this.buttons.reject.data).setDisabled(true) : this.buttons.reject;
3954
4190
  const buttons = [];
3955
4191
  const customButtonsArray = Array.from(this.customButtons.entries()).map(([customId, data]) => ({
3956
4192
  customId,
3957
- button: disable[customId] ? new import_discord14.ButtonBuilder(data.button.data).setDisabled(true) : data.button,
4193
+ button: disable[customId] ? new import_discord15.ButtonBuilder(data.button.data).setDisabled(true) : data.button,
3958
4194
  index: data.index
3959
4195
  }));
3960
4196
  customButtonsArray.sort((a, b) => a.index - b.index);
@@ -3975,7 +4211,7 @@ var Prompt = class {
3975
4211
  buttons.push(custom.button);
3976
4212
  }
3977
4213
  }
3978
- return new import_discord14.ActionRowBuilder({ components: buttons });
4214
+ return new import_discord15.ActionRowBuilder({ components: buttons });
3979
4215
  }
3980
4216
  buildSendOptions(options) {
3981
4217
  const sendData = { ...options };
@@ -4035,7 +4271,7 @@ var Prompt = class {
4035
4271
  const validCustomIds = /* @__PURE__ */ new Set(["btn_confirm", "btn_reject", ...this.customButtons.keys()]);
4036
4272
  try {
4037
4273
  const interaction = await this.message.awaitMessageComponent({
4038
- componentType: import_discord14.ComponentType.Button,
4274
+ componentType: import_discord15.ComponentType.Button,
4039
4275
  filter: (i) => validCustomIds.has(i.customId) && this.isParticipant(i.user.id),
4040
4276
  time: this.timeout
4041
4277
  });
@@ -4073,209 +4309,11 @@ async function prompt(handler, options, sendOptions) {
4073
4309
  await p.send(handler, sendOptions);
4074
4310
  return await p.awaitResponse();
4075
4311
  }
4076
-
4077
- // src/utils/VimcordCLI.ts
4078
- var import_node_readline = require("readline");
4079
- var import_qznt5 = require("qznt");
4080
- var VimcordCLI = class {
4081
- rl;
4082
- options;
4083
- commands = /* @__PURE__ */ new Map();
4084
- logger = new Logger({ prefixEmoji: "\u{1F680}", prefix: "CLI", showTimestamp: false });
4085
- constructor(options) {
4086
- this.options = options;
4087
- this.rl = (0, import_node_readline.createInterface)({
4088
- input: process.stdin,
4089
- output: process.stdout,
4090
- terminal: false
4091
- });
4092
- this.rl.on("line", (line) => {
4093
- const { isCommand, commandName, content, args } = this.parseLine(line);
4094
- if (!isCommand) return;
4095
- const command = this.commands.get(commandName);
4096
- if (!command) {
4097
- const nearestMatches = Array.from(this.commands.keys()).filter(
4098
- (cmd) => cmd.toLowerCase().includes(commandName.toLowerCase())
4099
- );
4100
- return this.logger.error(
4101
- `Unknown command '${commandName}'${nearestMatches.length ? `. Did you mean ${nearestMatches.length > 1 ? `[${nearestMatches.map((m) => `'${this.options.prefix}${m}'`).join(", ")}]` : `'${this.options.prefix}${nearestMatches[0]}'`}?` : ""}`
4102
- );
4103
- }
4104
- command.fn(args, content);
4105
- });
4106
- }
4107
- parseLine(line) {
4108
- if (line.startsWith(this.options.prefix)) {
4109
- line = line.slice(this.options.prefix.length);
4110
- } else {
4111
- return { isCommand: false };
4112
- }
4113
- const args = line.split(" ").map((s) => s.trim());
4114
- const commandName = args.shift();
4115
- return { isCommand: true, commandName, content: args.join(" "), args };
4116
- }
4117
- getClientInstance(line) {
4118
- const clientIndex = import_qznt5.$.str.getFlag(line, "--client", 1) || import_qznt5.$.str.getFlag(line, "-c", 1);
4119
- if (clientIndex) {
4120
- const idx = Number(clientIndex);
4121
- if (isNaN(idx)) {
4122
- CLI.logger.error(`'${clientIndex}' is not a valid number`);
4123
- return void 0;
4124
- }
4125
- const client = clientInstances[idx];
4126
- if (!client) {
4127
- CLI.logger.error("Client instance not found");
4128
- return void 0;
4129
- }
4130
- return client;
4131
- } else {
4132
- const client = clientInstances[0];
4133
- if (!client) {
4134
- CLI.logger.error("Client instance not found");
4135
- return void 0;
4136
- }
4137
- return client;
4138
- }
4139
- }
4140
- addCommand(commandName, description, fn) {
4141
- this.commands.set(commandName, { description, fn });
4142
- }
4143
- removeCommand(commandName) {
4144
- if (!this.commands.has(commandName)) return false;
4145
- this.commands.delete(commandName);
4146
- return true;
4147
- }
4148
- };
4149
- var initCalled = false;
4150
- var CLI = new VimcordCLI({ prefix: "/" });
4151
- CLI.addCommand("help", "View information about a command, or the available CLI options", (args) => {
4152
- const prefix = CLI.options.prefix;
4153
- const helpList = {};
4154
- for (const cmd of CLI.commands.entries()) {
4155
- const commandName = cmd[0];
4156
- const commandDescription = cmd[1].description;
4157
- helpList[`${prefix}${commandName}`] = `~ ${commandDescription}`;
4158
- }
4159
- CLI.logger.table("(help)", helpList);
4160
- });
4161
- CLI.addCommand("register", "Register app commands (slash & context) globally, or per guild", async (args, content) => {
4162
- const client = CLI.getClientInstance(content);
4163
- if (!client) return;
4164
- const mode = args[0]?.toLowerCase() || "";
4165
- if (!["guild", "global"].includes(mode)) {
4166
- return CLI.logger.error(`'${mode}' is not a valid option. Your options are [guild|global]`);
4167
- }
4168
- let guildIds = (import_qznt5.$.str.getFlag(content, "--guilds", 1) || import_qznt5.$.str.getFlag(content, "-g", 1) || "").replaceAll(/["']/g, "").split(" ").filter(Boolean).map((s) => s.replaceAll(",", "").trim());
4169
- if (!guildIds.length) guildIds = client.guilds.cache.map((g) => g.id);
4170
- switch (mode) {
4171
- case "guild":
4172
- CLI.logger.info("Registering guild commands...");
4173
- await client.commands.registerGuild({ guilds: guildIds });
4174
- break;
4175
- case "global":
4176
- CLI.logger.info("Registering global commands...");
4177
- await client.commands.registerGlobal();
4178
- break;
4179
- }
4180
- });
4181
- CLI.addCommand("unregister", "Unregister app commands globally, or per guild", async (args, content) => {
4182
- const client = CLI.getClientInstance(content);
4183
- if (!client) return;
4184
- const mode = args[0]?.toLowerCase() || "";
4185
- if (!["guild", "global"].includes(mode)) {
4186
- return CLI.logger.error(`'${mode}' is not a valid option. Your options are [guild|global]`);
4187
- }
4188
- let guildIds = (import_qznt5.$.str.getFlag(content, "--guilds", 1) || import_qznt5.$.str.getFlag(content, "-g", 1) || "").replaceAll(/["']/g, "").split(" ").filter(Boolean).map((s) => s.replaceAll(",", "").trim());
4189
- if (!guildIds.length) guildIds = client.guilds.cache.map((g) => g.id);
4190
- switch (mode) {
4191
- case "guild":
4192
- CLI.logger.info("Unregistering guild commands...");
4193
- await client.commands.unregisterGuild({ guilds: guildIds });
4194
- break;
4195
- case "global":
4196
- CLI.logger.info("Unregistering global commands...");
4197
- await client.commands.unregisterGlobal();
4198
- break;
4199
- }
4200
- });
4201
- CLI.addCommand("stats", "View statistics about a client instance", (args, content) => {
4202
- const client = CLI.getClientInstance(content);
4203
- if (!client) return;
4204
- CLI.logger.table(`(stats) ~ ${client.config.app.name}`, {
4205
- "Guilds:": import_qznt5.$.format.number(client.guilds.cache.size),
4206
- "Ping:": `${client.ws.ping || 0}ms`,
4207
- "Uptime:": `${import_qznt5.$.math.secs(client.uptime || 0)}s`,
4208
- "Process Uptime:": `${Math.floor(process.uptime())}s`,
4209
- "Memory Usage:": `${(process.memoryUsage().rss / 1024 / 1024).toFixed(2)} MB`
4210
- });
4211
- });
4212
- CLI.addCommand("cmds", "List the loaded commands", async (args, content) => {
4213
- const client = CLI.getClientInstance(content);
4214
- if (!client) return;
4215
- const mode = (args[0] || "slash").toLowerCase();
4216
- switch (mode) {
4217
- case "slash": {
4218
- const commands = Array.from(client.commands.slash.commands.values());
4219
- commands.sort((a, b) => a.builder.name.localeCompare(b.builder.name));
4220
- const tableData = {};
4221
- for (const cmd of commands) {
4222
- tableData[`/${cmd.builder.name}`] = `~ ${cmd.builder.description || "No description"}`;
4223
- }
4224
- return CLI.logger.table(`(cmds) ~ slash (${import_qznt5.$.format.number(commands.length)})`, tableData);
4225
- }
4226
- case "prefix": {
4227
- const commands = Array.from(client.commands.prefix.commands.values());
4228
- commands.sort((a, b) => {
4229
- const nameA = a.toConfig().name;
4230
- const nameB = b.toConfig().name;
4231
- return nameA.localeCompare(nameB);
4232
- });
4233
- const tableData = {};
4234
- const defaultPrefix = client.config.prefixCommands.defaultPrefix;
4235
- for (const cmd of commands) {
4236
- const config = cmd.toConfig();
4237
- const aliasIndicator = config.aliases?.length ? ` [${config.aliases.join(", ")}]` : "";
4238
- tableData[`${defaultPrefix}${config.name}${aliasIndicator}`] = `~ ${config.description || "No description"}`;
4239
- }
4240
- return CLI.logger.table(`(cmds) ~ prefix (${import_qznt5.$.format.number(commands.length)})`, tableData);
4241
- }
4242
- case "ctx": {
4243
- const commands = Array.from(client.commands.context.commands.values());
4244
- commands.sort((a, b) => a.builder.name.localeCompare(b.builder.name));
4245
- const tableData = {};
4246
- for (const cmd of commands) {
4247
- const type = cmd.builder.type === 2 ? "User" : "Msg";
4248
- tableData[`[${type}] ${cmd.builder.name}`] = "";
4249
- }
4250
- return CLI.logger.table(`(cmds) ~ ctx (${import_qznt5.$.format.number(commands.length)})`, tableData);
4251
- }
4252
- default:
4253
- return CLI.logger.error(`'${mode}' is not a valid option. Valid options: [slash|prefix|ctx]`);
4254
- }
4255
- });
4256
- function initCLI() {
4257
- if (initCalled) return;
4258
- CLI.logger.log(`~ Type ${CLI.options.prefix}help to view available commands`);
4259
- initCalled = true;
4260
- }
4261
-
4262
- // src/utils/clientUtils.ts
4263
- function useClient(index = 0) {
4264
- return clientInstances.at(index);
4265
- }
4266
- async function useReadyClient(index = 0) {
4267
- return useClient(index)?.whenReady();
4268
- }
4269
- function createClient(options, features = {}, config = {}) {
4270
- const client = new Vimcord(options, features, config);
4271
- initCLI();
4272
- return client;
4273
- }
4274
- function getClientInstances() {
4275
- return clientInstances;
4276
- }
4277
4312
  // Annotate the CommonJS export names for ESM import in node:
4278
4313
  0 && (module.exports = {
4314
+ BUILTIN_ContextCommandHandler,
4315
+ BUILTIN_PrefixCommandHandler,
4316
+ BUILTIN_SlashCommandHandler,
4279
4317
  BaseCommandBuilder,
4280
4318
  BaseCommandManager,
4281
4319
  BetterCollector,
@@ -4314,7 +4352,6 @@ function getClientInstances() {
4314
4352
  VimcordCLI,
4315
4353
  __zero,
4316
4354
  cleanMention,
4317
- clientInstances,
4318
4355
  createClient,
4319
4356
  createMongoPlugin,
4320
4357
  createMongoSchema,
@@ -4333,9 +4370,7 @@ function getClientInstances() {
4333
4370
  fetchMessage,
4334
4371
  fetchRole,
4335
4372
  fetchUser,
4336
- formatThousands,
4337
4373
  getCallerFileName,
4338
- getClientInstances,
4339
4374
  getFirstMentionId,
4340
4375
  getMessageMention,
4341
4376
  getProcessDir,
@@ -4345,7 +4380,6 @@ function getClientInstances() {
4345
4380
  isMentionOrSnowflake,
4346
4381
  logger,
4347
4382
  prompt,
4348
- retryExponentialBackoff,
4349
4383
  sendCommandErrorEmbed,
4350
4384
  useClient,
4351
4385
  useReadyClient,