vimcord 1.0.0
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 +4203 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.mts +1493 -0
- package/dist/index.d.ts +1493 -0
- package/dist/index.js +4151 -0
- package/dist/index.js.map +1 -0
- package/dist/metafile-cjs.json +1 -0
- package/dist/metafile-esm.json +1 -0
- package/package.json +58 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,4151 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
// src/types/command.base.ts
|
|
9
|
+
var CommandType = /* @__PURE__ */ ((CommandType3) => {
|
|
10
|
+
CommandType3[CommandType3["Slash"] = 0] = "Slash";
|
|
11
|
+
CommandType3[CommandType3["Prefix"] = 1] = "Prefix";
|
|
12
|
+
CommandType3[CommandType3["Context"] = 2] = "Context";
|
|
13
|
+
return CommandType3;
|
|
14
|
+
})(CommandType || {});
|
|
15
|
+
var MissingPermissionReason = /* @__PURE__ */ ((MissingPermissionReason2) => {
|
|
16
|
+
MissingPermissionReason2[MissingPermissionReason2["User"] = 0] = "User";
|
|
17
|
+
MissingPermissionReason2[MissingPermissionReason2["Bot"] = 1] = "Bot";
|
|
18
|
+
MissingPermissionReason2[MissingPermissionReason2["Role"] = 2] = "Role";
|
|
19
|
+
MissingPermissionReason2[MissingPermissionReason2["UserBlacklisted"] = 3] = "UserBlacklisted";
|
|
20
|
+
MissingPermissionReason2[MissingPermissionReason2["RoleBlacklisted"] = 4] = "RoleBlacklisted";
|
|
21
|
+
MissingPermissionReason2[MissingPermissionReason2["NotInGuild"] = 5] = "NotInGuild";
|
|
22
|
+
MissingPermissionReason2[MissingPermissionReason2["NotGuildOwner"] = 6] = "NotGuildOwner";
|
|
23
|
+
MissingPermissionReason2[MissingPermissionReason2["NotBotOwner"] = 7] = "NotBotOwner";
|
|
24
|
+
MissingPermissionReason2[MissingPermissionReason2["NotBotStaff"] = 8] = "NotBotStaff";
|
|
25
|
+
return MissingPermissionReason2;
|
|
26
|
+
})(MissingPermissionReason || {});
|
|
27
|
+
var RateLimitScope = /* @__PURE__ */ ((RateLimitScope2) => {
|
|
28
|
+
RateLimitScope2[RateLimitScope2["User"] = 0] = "User";
|
|
29
|
+
RateLimitScope2[RateLimitScope2["Guild"] = 1] = "Guild";
|
|
30
|
+
RateLimitScope2[RateLimitScope2["Channel"] = 2] = "Channel";
|
|
31
|
+
RateLimitScope2[RateLimitScope2["Global"] = 3] = "Global";
|
|
32
|
+
return RateLimitScope2;
|
|
33
|
+
})(RateLimitScope || {});
|
|
34
|
+
|
|
35
|
+
// src/validators/permissions.validator.ts
|
|
36
|
+
import { BaseInteraction } from "discord.js";
|
|
37
|
+
function __existsAndTrue(value) {
|
|
38
|
+
return value !== void 0 && value;
|
|
39
|
+
}
|
|
40
|
+
function validateCommandPermissions(permissions, client, user, command) {
|
|
41
|
+
const inGuild = "guild" in user;
|
|
42
|
+
const missingUserPermissions = [];
|
|
43
|
+
const missingBotPermissions = [];
|
|
44
|
+
const missingRoles = [];
|
|
45
|
+
if (permissions.user?.length && inGuild) {
|
|
46
|
+
for (const permission of permissions.user) {
|
|
47
|
+
if (!user.permissions.has(permission)) {
|
|
48
|
+
missingUserPermissions.push(permission);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (missingUserPermissions.length) {
|
|
52
|
+
return { validated: false, failReason: 0 /* User */, missingUserPermissions };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (permissions.bot?.length && inGuild && user.guild.members.me) {
|
|
56
|
+
for (const permission of permissions.bot) {
|
|
57
|
+
if (!user.guild.members.me.permissions.has(permission)) {
|
|
58
|
+
missingBotPermissions.push(permission);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (missingBotPermissions.length) {
|
|
62
|
+
return { validated: false, failReason: 1 /* Bot */, missingBotPermissions };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (permissions.roles?.length && inGuild) {
|
|
66
|
+
for (const role of permissions.roles) {
|
|
67
|
+
if (!user.roles.cache.has(role)) {
|
|
68
|
+
missingRoles.push(role);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (missingRoles.length) {
|
|
72
|
+
return { validated: false, failReason: 2 /* Role */, missingRoles };
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (permissions.userBlacklist?.length) {
|
|
76
|
+
if (permissions.userBlacklist.includes(user.id)) {
|
|
77
|
+
return { validated: false, failReason: 3 /* UserBlacklisted */, blacklistedUser: user.id };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (permissions.roleBlacklist?.length && inGuild) {
|
|
81
|
+
if (user.roles.cache.some((role) => permissions.roleBlacklist.includes(role.id))) {
|
|
82
|
+
return { validated: false, failReason: 4 /* RoleBlacklisted */, blacklistedRole: user.id };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (__existsAndTrue(permissions.guildOnly) && !inGuild) {
|
|
86
|
+
return { validated: false, failReason: 5 /* NotInGuild */ };
|
|
87
|
+
}
|
|
88
|
+
if (__existsAndTrue(permissions.guildOwnerOnly) && inGuild && user.id !== user.guild.ownerId) {
|
|
89
|
+
return { validated: false, failReason: 6 /* NotGuildOwner */ };
|
|
90
|
+
}
|
|
91
|
+
if (__existsAndTrue(permissions.botOwnerOnly) && user.id !== client.config.staff.ownerId) {
|
|
92
|
+
return { validated: false, failReason: 7 /* NotBotOwner */ };
|
|
93
|
+
}
|
|
94
|
+
if (__existsAndTrue(permissions.botStaffOnly)) {
|
|
95
|
+
if (!client.config.staff.superUsers.includes(user.id)) {
|
|
96
|
+
return { validated: false, failReason: 8 /* NotBotStaff */ };
|
|
97
|
+
}
|
|
98
|
+
if (inGuild) {
|
|
99
|
+
for (const [k, role] of user.roles.cache) {
|
|
100
|
+
if (!user.roles.cache.has(role.id)) {
|
|
101
|
+
missingRoles.push(role.id);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (missingRoles.length) {
|
|
105
|
+
return { validated: false, failReason: 8 /* NotBotStaff */, missingRoles };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (command instanceof BaseInteraction && command.isCommand()) {
|
|
109
|
+
let commandName = null;
|
|
110
|
+
if (command.isChatInputCommand()) {
|
|
111
|
+
const subcommand = command.options.getSubcommand();
|
|
112
|
+
commandName = `${command.commandName}${subcommand ? ` ${subcommand}` : ""}`;
|
|
113
|
+
} else {
|
|
114
|
+
commandName = command.commandName;
|
|
115
|
+
}
|
|
116
|
+
if (!client.config.staff.bypassers.some(
|
|
117
|
+
(bypass) => bypass.commandName.toLowerCase() === commandName.toLowerCase() && bypass.userIds.includes(user.id)
|
|
118
|
+
)) {
|
|
119
|
+
return { validated: false, failReason: 8 /* NotBotStaff */ };
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (typeof command === "string" && !client.config.staff.bypassers.some(
|
|
123
|
+
(bypass) => bypass.commandName.toLowerCase() === command.toLowerCase() && bypass.userIds.includes(user.id)
|
|
124
|
+
)) {
|
|
125
|
+
return { validated: false, failReason: 8 /* NotBotStaff */ };
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return { validated: true };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// src/builders/baseCommand.builder.ts
|
|
132
|
+
import { randomUUID } from "crypto";
|
|
133
|
+
import _ from "lodash";
|
|
134
|
+
var BaseCommandBuilder = class {
|
|
135
|
+
uuid = randomUUID();
|
|
136
|
+
commandType;
|
|
137
|
+
client;
|
|
138
|
+
enabled;
|
|
139
|
+
conditions;
|
|
140
|
+
permissions;
|
|
141
|
+
metadata;
|
|
142
|
+
rateLimit;
|
|
143
|
+
beforeExecute;
|
|
144
|
+
execute;
|
|
145
|
+
afterExecute;
|
|
146
|
+
onRateLimit;
|
|
147
|
+
onMissingPermissions;
|
|
148
|
+
onConditionsNotMet;
|
|
149
|
+
onUsedWhenDisabled;
|
|
150
|
+
onError;
|
|
151
|
+
globalRateLimitData = { executions: 0, timestamp: 0 };
|
|
152
|
+
userRateLimitData = /* @__PURE__ */ new Map();
|
|
153
|
+
guildRateLimitData = /* @__PURE__ */ new Map();
|
|
154
|
+
channelRateLimitData = /* @__PURE__ */ new Map();
|
|
155
|
+
constructor(type, config) {
|
|
156
|
+
this.commandType = type;
|
|
157
|
+
this.enabled = config?.enabled ?? true;
|
|
158
|
+
this.conditions = config?.conditions;
|
|
159
|
+
this.permissions = config?.permissions;
|
|
160
|
+
this.metadata = config?.metadata;
|
|
161
|
+
this.rateLimit = config?.rateLimit;
|
|
162
|
+
this.beforeExecute = config?.beforeExecute;
|
|
163
|
+
this.execute = config?.execute;
|
|
164
|
+
this.afterExecute = config?.afterExecute;
|
|
165
|
+
this.onRateLimit = config?.onRateLimit;
|
|
166
|
+
this.onMissingPermissions = config?.onMissingPermissions;
|
|
167
|
+
this.onConditionsNotMet = config?.onConditionsNotMet;
|
|
168
|
+
this.onUsedWhenDisabled = config?.onUsedWhenDisabled;
|
|
169
|
+
this.onError = config?.onError;
|
|
170
|
+
this.validateBaseConfig();
|
|
171
|
+
}
|
|
172
|
+
validateBaseConfig() {
|
|
173
|
+
if (this.rateLimit) {
|
|
174
|
+
if (this.rateLimit.max <= 0) {
|
|
175
|
+
throw new Error("DJSSlashCommandBuilder: Rate limit max must be greater than 0");
|
|
176
|
+
}
|
|
177
|
+
if (this.rateLimit.interval <= 0) {
|
|
178
|
+
throw new Error("DJSSlashCommandBuilder: Rate limit interval must be greater than 0");
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
toConfig() {
|
|
183
|
+
return {
|
|
184
|
+
enabled: this.enabled,
|
|
185
|
+
conditions: this.conditions,
|
|
186
|
+
permissions: this.permissions,
|
|
187
|
+
metadata: this.metadata,
|
|
188
|
+
rateLimit: this.rateLimit,
|
|
189
|
+
beforeExecute: this.beforeExecute,
|
|
190
|
+
execute: this.execute,
|
|
191
|
+
afterExecute: this.afterExecute,
|
|
192
|
+
onMissingPermissions: this.onMissingPermissions,
|
|
193
|
+
onConditionsNotMet: this.onConditionsNotMet,
|
|
194
|
+
onUsedWhenDisabled: this.onUsedWhenDisabled,
|
|
195
|
+
onRateLimit: this.onRateLimit,
|
|
196
|
+
onError: this.onError
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
setEnabled(enabled) {
|
|
200
|
+
this.enabled = enabled;
|
|
201
|
+
return this;
|
|
202
|
+
}
|
|
203
|
+
addConditions(...conditions) {
|
|
204
|
+
if (!this.conditions) this.conditions = [];
|
|
205
|
+
this.conditions.push(...conditions);
|
|
206
|
+
return this;
|
|
207
|
+
}
|
|
208
|
+
setConditions(conditions) {
|
|
209
|
+
this.conditions = conditions;
|
|
210
|
+
return this;
|
|
211
|
+
}
|
|
212
|
+
setPermissions(permissions) {
|
|
213
|
+
this.permissions = _.merge(this.permissions, permissions);
|
|
214
|
+
return this;
|
|
215
|
+
}
|
|
216
|
+
setMetadata(metadata) {
|
|
217
|
+
this.metadata = _.merge(this.metadata, metadata);
|
|
218
|
+
return this;
|
|
219
|
+
}
|
|
220
|
+
setRateLimit(options) {
|
|
221
|
+
this.rateLimit = options;
|
|
222
|
+
return this;
|
|
223
|
+
}
|
|
224
|
+
setBeforeExecute(callback) {
|
|
225
|
+
this.beforeExecute = callback;
|
|
226
|
+
return this;
|
|
227
|
+
}
|
|
228
|
+
setExecute(callback) {
|
|
229
|
+
this.execute = callback;
|
|
230
|
+
return this;
|
|
231
|
+
}
|
|
232
|
+
setAfterExecute(callback) {
|
|
233
|
+
this.afterExecute = callback;
|
|
234
|
+
return this;
|
|
235
|
+
}
|
|
236
|
+
setOnRateLimit(callback) {
|
|
237
|
+
this.onRateLimit = callback;
|
|
238
|
+
return this;
|
|
239
|
+
}
|
|
240
|
+
setOnMissingPermissions(callback) {
|
|
241
|
+
this.onMissingPermissions = callback;
|
|
242
|
+
return this;
|
|
243
|
+
}
|
|
244
|
+
setOnConditionsNotMet(callback) {
|
|
245
|
+
this.onConditionsNotMet = callback;
|
|
246
|
+
return this;
|
|
247
|
+
}
|
|
248
|
+
setOnUsedWhenDisabled(callback) {
|
|
249
|
+
this.onUsedWhenDisabled = callback;
|
|
250
|
+
return this;
|
|
251
|
+
}
|
|
252
|
+
setOnError(callback) {
|
|
253
|
+
this.onError = callback;
|
|
254
|
+
return this;
|
|
255
|
+
}
|
|
256
|
+
getRateLimitInfo(user, guild, channel) {
|
|
257
|
+
if (!this.rateLimit) return null;
|
|
258
|
+
let rateLimitData;
|
|
259
|
+
switch (this.rateLimit.scope) {
|
|
260
|
+
case 0 /* User */:
|
|
261
|
+
if (!user) return null;
|
|
262
|
+
rateLimitData = this.userRateLimitData.get(user.id) ?? { executions: 0, timestamp: 0 };
|
|
263
|
+
break;
|
|
264
|
+
case 1 /* Guild */:
|
|
265
|
+
if (!guild) return null;
|
|
266
|
+
rateLimitData = this.guildRateLimitData.get(guild.id) ?? { executions: 0, timestamp: 0 };
|
|
267
|
+
break;
|
|
268
|
+
case 2 /* Channel */:
|
|
269
|
+
if (!channel) return null;
|
|
270
|
+
rateLimitData = this.channelRateLimitData.get(channel.id) ?? { executions: 0, timestamp: 0 };
|
|
271
|
+
break;
|
|
272
|
+
case 3 /* Global */:
|
|
273
|
+
rateLimitData = this.globalRateLimitData;
|
|
274
|
+
break;
|
|
275
|
+
default:
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
return { ...rateLimitData, isLimited: this.isRateLimited(user, guild, channel, false) };
|
|
279
|
+
}
|
|
280
|
+
isRateLimited(user, guild, channel, updateExecutions = true) {
|
|
281
|
+
if (!this.rateLimit) return false;
|
|
282
|
+
const now = Date.now();
|
|
283
|
+
let rateLimitData;
|
|
284
|
+
switch (this.rateLimit.scope) {
|
|
285
|
+
case 0 /* User */:
|
|
286
|
+
if (!user) return false;
|
|
287
|
+
rateLimitData = this.userRateLimitData.get(user.id) ?? { executions: 0, timestamp: 0 };
|
|
288
|
+
if (now - rateLimitData.timestamp >= this.rateLimit.interval) {
|
|
289
|
+
rateLimitData.executions = 0;
|
|
290
|
+
rateLimitData.timestamp = now;
|
|
291
|
+
}
|
|
292
|
+
if (updateExecutions) rateLimitData.executions++;
|
|
293
|
+
this.userRateLimitData.set(user.id, rateLimitData);
|
|
294
|
+
break;
|
|
295
|
+
case 1 /* Guild */:
|
|
296
|
+
if (!guild) return false;
|
|
297
|
+
rateLimitData = this.guildRateLimitData.get(guild.id) ?? { executions: 0, timestamp: 0 };
|
|
298
|
+
if (now - rateLimitData.timestamp >= this.rateLimit.interval) {
|
|
299
|
+
rateLimitData.executions = 0;
|
|
300
|
+
rateLimitData.timestamp = now;
|
|
301
|
+
}
|
|
302
|
+
if (updateExecutions) rateLimitData.executions++;
|
|
303
|
+
this.guildRateLimitData.set(guild.id, rateLimitData);
|
|
304
|
+
break;
|
|
305
|
+
case 2 /* Channel */:
|
|
306
|
+
if (!channel) return false;
|
|
307
|
+
rateLimitData = this.guildRateLimitData.get(channel.id) ?? { executions: 0, timestamp: 0 };
|
|
308
|
+
if (now - rateLimitData.timestamp >= this.rateLimit.interval) {
|
|
309
|
+
rateLimitData.executions = 0;
|
|
310
|
+
rateLimitData.timestamp = now;
|
|
311
|
+
}
|
|
312
|
+
if (updateExecutions) rateLimitData.executions++;
|
|
313
|
+
this.channelRateLimitData.set(channel.id, rateLimitData);
|
|
314
|
+
break;
|
|
315
|
+
case 3 /* Global */:
|
|
316
|
+
rateLimitData = this.globalRateLimitData;
|
|
317
|
+
if (now - rateLimitData.timestamp >= this.rateLimit.interval) {
|
|
318
|
+
rateLimitData.executions = 0;
|
|
319
|
+
rateLimitData.timestamp = now;
|
|
320
|
+
}
|
|
321
|
+
if (updateExecutions) rateLimitData.executions++;
|
|
322
|
+
this.globalRateLimitData = rateLimitData;
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
return rateLimitData.executions >= this.rateLimit.max;
|
|
326
|
+
}
|
|
327
|
+
checkPermissions(client, user, command) {
|
|
328
|
+
if (!this.permissions) return { validated: true };
|
|
329
|
+
return validateCommandPermissions(this.permissions, client, user, command);
|
|
330
|
+
}
|
|
331
|
+
async checkConditions(...args) {
|
|
332
|
+
if (!this.conditions?.length) return true;
|
|
333
|
+
const results = await Promise.all(this.conditions.map((condition) => condition(...args)));
|
|
334
|
+
return results.every(Boolean);
|
|
335
|
+
}
|
|
336
|
+
async _runPreflight(clientConfig, command, ...args) {
|
|
337
|
+
try {
|
|
338
|
+
if (!this.enabled) {
|
|
339
|
+
await clientConfig.onUsedWhenDisabled?.(...args);
|
|
340
|
+
await this.onUsedWhenDisabled?.(...args);
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
if (this.isRateLimited(args[1]?.member || args[1]?.author, args[1].guild, args[1].channel)) {
|
|
344
|
+
await clientConfig.onRateLimit?.(...args);
|
|
345
|
+
await this.rateLimit?.onRateLimit?.(...args);
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
const permissionResults = this.checkPermissions(
|
|
349
|
+
args[0],
|
|
350
|
+
args[1]?.member || args[1]?.author || args[1]?.user,
|
|
351
|
+
command
|
|
352
|
+
);
|
|
353
|
+
if (!permissionResults.validated) {
|
|
354
|
+
await clientConfig.onMissingPermissions?.(permissionResults, ...args);
|
|
355
|
+
await this.onMissingPermissions?.(permissionResults, ...args);
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
if (!await this.checkConditions(...args)) {
|
|
359
|
+
await clientConfig.onConditionsNotMet?.(...args);
|
|
360
|
+
await this.onConditionsNotMet?.(...args);
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
} catch (err) {
|
|
364
|
+
if (this.onError) {
|
|
365
|
+
return this.onError(err, ...args);
|
|
366
|
+
} else if (clientConfig.onError) {
|
|
367
|
+
return clientConfig.onError(err, ...args);
|
|
368
|
+
} else {
|
|
369
|
+
throw err;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
async _runCabinCheck(clientConfig, ...args) {
|
|
374
|
+
try {
|
|
375
|
+
await clientConfig.beforeExecute?.(...args);
|
|
376
|
+
await this.beforeExecute?.(...args);
|
|
377
|
+
} catch (err) {
|
|
378
|
+
if (this.onError) {
|
|
379
|
+
return this.onError(err, ...args);
|
|
380
|
+
} else if (clientConfig.onError) {
|
|
381
|
+
return clientConfig.onError(err, ...args);
|
|
382
|
+
} else {
|
|
383
|
+
throw err;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
async _runFlyAndLand(clientConfig, ...args) {
|
|
388
|
+
try {
|
|
389
|
+
const result = await this.execute?.(...args);
|
|
390
|
+
await clientConfig?.afterExecute?.(result, ...args);
|
|
391
|
+
await this.afterExecute?.(result, ...args);
|
|
392
|
+
} catch (err) {
|
|
393
|
+
if (this.onError) {
|
|
394
|
+
return this.onError(err, ...args);
|
|
395
|
+
} else if (clientConfig.onError) {
|
|
396
|
+
return clientConfig.onError(err, ...args);
|
|
397
|
+
} else {
|
|
398
|
+
throw err;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
// src/builders/contextCommand.builder.ts
|
|
405
|
+
import { ContextMenuCommandBuilder } from "discord.js";
|
|
406
|
+
import _2 from "lodash";
|
|
407
|
+
var ContextCommandBuilder = class extends BaseCommandBuilder {
|
|
408
|
+
builder;
|
|
409
|
+
deferReply;
|
|
410
|
+
deployment;
|
|
411
|
+
constructor(config) {
|
|
412
|
+
super(2 /* Context */, config);
|
|
413
|
+
this.setBuilder(config.builder);
|
|
414
|
+
this.deferReply = config?.deferReply;
|
|
415
|
+
this.deployment = config?.deployment;
|
|
416
|
+
this.validateConfig();
|
|
417
|
+
}
|
|
418
|
+
validateBuilder() {
|
|
419
|
+
if (!this.builder.name) {
|
|
420
|
+
throw new Error("VContextCommandBuilder: Command name is required");
|
|
421
|
+
}
|
|
422
|
+
try {
|
|
423
|
+
this.builder.toJSON();
|
|
424
|
+
} catch (err) {
|
|
425
|
+
throw new Error("VContextCommandBuilder: Invalid command builder", { cause: err });
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
validateConfig() {
|
|
429
|
+
}
|
|
430
|
+
toConfig() {
|
|
431
|
+
return {
|
|
432
|
+
...super.toConfig(),
|
|
433
|
+
builder: this.builder,
|
|
434
|
+
deferReply: this.deferReply,
|
|
435
|
+
deployment: this.deployment
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
setBuilder(builder) {
|
|
439
|
+
if (typeof builder === "function") {
|
|
440
|
+
this.builder = builder(new ContextMenuCommandBuilder());
|
|
441
|
+
} else {
|
|
442
|
+
this.builder = builder;
|
|
443
|
+
}
|
|
444
|
+
this.validateBuilder();
|
|
445
|
+
return this;
|
|
446
|
+
}
|
|
447
|
+
setDeferReply(defer) {
|
|
448
|
+
this.deferReply = defer;
|
|
449
|
+
return this;
|
|
450
|
+
}
|
|
451
|
+
setDeployment(deployment) {
|
|
452
|
+
this.deployment = _2.merge(this.deployment, deployment);
|
|
453
|
+
return this;
|
|
454
|
+
}
|
|
455
|
+
async executeCommand(client, interaction) {
|
|
456
|
+
try {
|
|
457
|
+
await this._runPreflight(client.config.contextCommands, interaction, client, interaction);
|
|
458
|
+
await this._runCabinCheck(client.config.contextCommands, client, interaction);
|
|
459
|
+
if (this.deferReply && !interaction.replied && !interaction.deferred) {
|
|
460
|
+
await interaction.deferReply(typeof this.deferReply === "object" ? this.deferReply : void 0);
|
|
461
|
+
}
|
|
462
|
+
return this._runFlyAndLand(client.config.contextCommands, client, interaction);
|
|
463
|
+
} catch (err) {
|
|
464
|
+
if (this.onError) {
|
|
465
|
+
return this.onError(err, client, interaction);
|
|
466
|
+
} else if (client.config.contextCommands.onError) {
|
|
467
|
+
return client.config.contextCommands.onError(err, client, interaction);
|
|
468
|
+
} else {
|
|
469
|
+
throw err;
|
|
470
|
+
}
|
|
471
|
+
} finally {
|
|
472
|
+
if (client.config.contextCommands.logExecution) {
|
|
473
|
+
client.logger.commandExecuted(this.builder.name, interaction.user.username, interaction.guild?.name);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
// src/utils/dir.ts
|
|
480
|
+
import jsTools from "jstools";
|
|
481
|
+
import path from "path";
|
|
482
|
+
function getProcessDir() {
|
|
483
|
+
const mainPath = process.argv[1];
|
|
484
|
+
if (!mainPath) return "";
|
|
485
|
+
return path.dirname(mainPath);
|
|
486
|
+
}
|
|
487
|
+
async function importModulesFromDir(dir, fnPrefix) {
|
|
488
|
+
const cwd = getProcessDir();
|
|
489
|
+
const MODULE_RELATIVE_PATH = path.join(cwd, dir);
|
|
490
|
+
const MODULE_LOG_PATH = dir;
|
|
491
|
+
const files = jsTools.readDir(MODULE_RELATIVE_PATH, { recursive: true }).filter(
|
|
492
|
+
(fn) => fn.endsWith(`${fnPrefix ? `.${fnPrefix}` : ""}.js`) || fn.endsWith(`${fnPrefix ? `.${fnPrefix}` : ""}.ts`)
|
|
493
|
+
);
|
|
494
|
+
if (!files.length) {
|
|
495
|
+
return [];
|
|
496
|
+
}
|
|
497
|
+
const modules = await Promise.all(
|
|
498
|
+
files.map(async (fn) => {
|
|
499
|
+
let _path = path.join(MODULE_RELATIVE_PATH, fn);
|
|
500
|
+
let _logPath = `./${path.join(MODULE_LOG_PATH, fn)}`;
|
|
501
|
+
let _module;
|
|
502
|
+
try {
|
|
503
|
+
delete __require.cache[__require.resolve(_path)];
|
|
504
|
+
_module = __require(_path);
|
|
505
|
+
} catch (err) {
|
|
506
|
+
console.warn(`Failed to import module at '${_logPath}'`, err);
|
|
507
|
+
_module = null;
|
|
508
|
+
}
|
|
509
|
+
return { module: _module, path: _logPath };
|
|
510
|
+
})
|
|
511
|
+
);
|
|
512
|
+
const filteredModules = modules.filter((m) => m.module);
|
|
513
|
+
if (!filteredModules.length) {
|
|
514
|
+
console.warn(`No valid modules were found in directory '${dir}'`);
|
|
515
|
+
}
|
|
516
|
+
return filteredModules;
|
|
517
|
+
}
|
|
518
|
+
function getCallerFileName() {
|
|
519
|
+
const stack = new Error().stack?.split("\n");
|
|
520
|
+
return stack?.at(4)?.split("at file")?.at(1)?.split("/").at(-1)?.split(":").at(0)?.split(".").at(0);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// src/builders/event.builder.ts
|
|
524
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
525
|
+
import _3 from "lodash";
|
|
526
|
+
var EventBuilder = class _EventBuilder {
|
|
527
|
+
uuid = randomUUID2();
|
|
528
|
+
event;
|
|
529
|
+
name = getCallerFileName() || this.uuid;
|
|
530
|
+
enabled;
|
|
531
|
+
once;
|
|
532
|
+
priority;
|
|
533
|
+
conditions;
|
|
534
|
+
metadata;
|
|
535
|
+
deployment;
|
|
536
|
+
rateLimit;
|
|
537
|
+
beforeExecute;
|
|
538
|
+
execute;
|
|
539
|
+
afterExecute;
|
|
540
|
+
onError;
|
|
541
|
+
rateLimitData = { executions: 0, timestamp: 0 };
|
|
542
|
+
static create(event, name) {
|
|
543
|
+
return new _EventBuilder({ event, name });
|
|
544
|
+
}
|
|
545
|
+
constructor(config) {
|
|
546
|
+
this.event = config.event;
|
|
547
|
+
this.name = config.name || this.name;
|
|
548
|
+
this.enabled = config.enabled ?? true;
|
|
549
|
+
this.once = config.once ?? false;
|
|
550
|
+
this.priority = config.priority ?? 0;
|
|
551
|
+
this.conditions = config.conditions;
|
|
552
|
+
this.metadata = config.metadata;
|
|
553
|
+
this.deployment = config.deployment;
|
|
554
|
+
this.rateLimit = config.rateLimit;
|
|
555
|
+
this.beforeExecute = config.beforeExecute;
|
|
556
|
+
this.execute = config.execute;
|
|
557
|
+
this.afterExecute = config.afterExecute;
|
|
558
|
+
this.onError = config.onError;
|
|
559
|
+
this.validate();
|
|
560
|
+
}
|
|
561
|
+
validate() {
|
|
562
|
+
if (!this.event) {
|
|
563
|
+
throw new Error("Event name is required");
|
|
564
|
+
}
|
|
565
|
+
if (this.priority !== void 0 && this.priority < 0) {
|
|
566
|
+
throw new Error("Priority must be non-negative");
|
|
567
|
+
}
|
|
568
|
+
if (this.rateLimit) {
|
|
569
|
+
if (this.rateLimit.max <= 0) {
|
|
570
|
+
throw new Error("Rate limit max must be greater than 0");
|
|
571
|
+
}
|
|
572
|
+
if (this.rateLimit.interval <= 0) {
|
|
573
|
+
throw new Error("Rate limit interval must be greater than 0");
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
if (!this.execute) {
|
|
577
|
+
throw new Error("Execute function is required");
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
clone() {
|
|
581
|
+
return new _EventBuilder(this.toConfig());
|
|
582
|
+
}
|
|
583
|
+
toConfig() {
|
|
584
|
+
return {
|
|
585
|
+
event: this.event,
|
|
586
|
+
name: this.name,
|
|
587
|
+
enabled: this.enabled,
|
|
588
|
+
once: this.once,
|
|
589
|
+
priority: this.priority,
|
|
590
|
+
conditions: this.conditions,
|
|
591
|
+
metadata: this.metadata,
|
|
592
|
+
deployment: this.deployment,
|
|
593
|
+
rateLimit: this.rateLimit,
|
|
594
|
+
beforeExecute: this.beforeExecute,
|
|
595
|
+
execute: this.execute,
|
|
596
|
+
afterExecute: this.afterExecute,
|
|
597
|
+
onError: this.onError
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
setEvent(event) {
|
|
601
|
+
this.event = event;
|
|
602
|
+
return this;
|
|
603
|
+
}
|
|
604
|
+
setName(name) {
|
|
605
|
+
this.name = name;
|
|
606
|
+
return this;
|
|
607
|
+
}
|
|
608
|
+
setEnabled(enabled) {
|
|
609
|
+
this.enabled = enabled;
|
|
610
|
+
return this;
|
|
611
|
+
}
|
|
612
|
+
enable() {
|
|
613
|
+
return this.setEnabled(true);
|
|
614
|
+
}
|
|
615
|
+
disable() {
|
|
616
|
+
return this.setEnabled(false);
|
|
617
|
+
}
|
|
618
|
+
setOnce(once = true) {
|
|
619
|
+
this.once = once;
|
|
620
|
+
return this;
|
|
621
|
+
}
|
|
622
|
+
setPriority(priority) {
|
|
623
|
+
this.priority = priority;
|
|
624
|
+
return this;
|
|
625
|
+
}
|
|
626
|
+
addCondition(condition) {
|
|
627
|
+
if (!this.conditions) this.conditions = [];
|
|
628
|
+
this.conditions.push(condition);
|
|
629
|
+
return this;
|
|
630
|
+
}
|
|
631
|
+
setConditions(conditions) {
|
|
632
|
+
this.conditions = conditions;
|
|
633
|
+
return this;
|
|
634
|
+
}
|
|
635
|
+
setMetadata(metadata) {
|
|
636
|
+
this.metadata = _3.merge(this.metadata, metadata);
|
|
637
|
+
return this;
|
|
638
|
+
}
|
|
639
|
+
setDeployment(deployment) {
|
|
640
|
+
this.deployment = _3.merge(this.deployment, deployment);
|
|
641
|
+
return this;
|
|
642
|
+
}
|
|
643
|
+
setRateLimit(options) {
|
|
644
|
+
this.rateLimit = options;
|
|
645
|
+
return this;
|
|
646
|
+
}
|
|
647
|
+
setBeforeExecute(beforeExecute) {
|
|
648
|
+
this.beforeExecute = beforeExecute;
|
|
649
|
+
return this;
|
|
650
|
+
}
|
|
651
|
+
setExecute(execute) {
|
|
652
|
+
this.execute = execute;
|
|
653
|
+
return this;
|
|
654
|
+
}
|
|
655
|
+
setAfterExecute(afterExecute) {
|
|
656
|
+
this.afterExecute = afterExecute;
|
|
657
|
+
return this;
|
|
658
|
+
}
|
|
659
|
+
setOnError(onError) {
|
|
660
|
+
this.onError = onError;
|
|
661
|
+
return this;
|
|
662
|
+
}
|
|
663
|
+
getRateLimitInfo() {
|
|
664
|
+
if (!this.rateLimit) return null;
|
|
665
|
+
return { ...this.rateLimitData, isLimited: this.isRateLimited(false) };
|
|
666
|
+
}
|
|
667
|
+
resetRateLimit() {
|
|
668
|
+
this.rateLimitData = { executions: 0, timestamp: 0 };
|
|
669
|
+
return this;
|
|
670
|
+
}
|
|
671
|
+
isRateLimited(updateExecutions = true) {
|
|
672
|
+
if (!this.rateLimit) return false;
|
|
673
|
+
const now = Date.now();
|
|
674
|
+
if (now - this.rateLimitData.timestamp >= this.rateLimit.interval) {
|
|
675
|
+
this.rateLimitData.executions = 0;
|
|
676
|
+
this.rateLimitData.timestamp = now;
|
|
677
|
+
}
|
|
678
|
+
if (updateExecutions) {
|
|
679
|
+
this.rateLimitData.executions++;
|
|
680
|
+
}
|
|
681
|
+
return this.rateLimitData.executions >= this.rateLimit.max;
|
|
682
|
+
}
|
|
683
|
+
async checkConditions(...args) {
|
|
684
|
+
if (!this.conditions?.length) return true;
|
|
685
|
+
const results = await Promise.all(this.conditions.map((condition) => condition(...args)));
|
|
686
|
+
return results.every(Boolean);
|
|
687
|
+
}
|
|
688
|
+
async executeEvent(...args) {
|
|
689
|
+
try {
|
|
690
|
+
if (!this.enabled) {
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
if (this.isRateLimited()) {
|
|
694
|
+
if (this.rateLimit?.onRateLimit) {
|
|
695
|
+
return await this.rateLimit.onRateLimit(...args);
|
|
696
|
+
}
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
if (!await this.checkConditions(...args)) {
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
if (this.beforeExecute) {
|
|
703
|
+
await this.beforeExecute(...args);
|
|
704
|
+
}
|
|
705
|
+
const result = await this.execute?.(...args);
|
|
706
|
+
if (this.afterExecute) {
|
|
707
|
+
await this.afterExecute(result, ...args);
|
|
708
|
+
}
|
|
709
|
+
return result;
|
|
710
|
+
} catch (err) {
|
|
711
|
+
if (this.onError) {
|
|
712
|
+
return await this.onError(err, ...args);
|
|
713
|
+
}
|
|
714
|
+
console.error(`Error in event '${this.name}':`, err);
|
|
715
|
+
throw err;
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
};
|
|
719
|
+
|
|
720
|
+
// src/builders/prefixCommand.builder.ts
|
|
721
|
+
var PrefixCommandBuilder = class extends BaseCommandBuilder {
|
|
722
|
+
name;
|
|
723
|
+
aliases;
|
|
724
|
+
description;
|
|
725
|
+
constructor(config) {
|
|
726
|
+
super(1 /* Prefix */, config);
|
|
727
|
+
this.name = config.name;
|
|
728
|
+
this.description = config.description;
|
|
729
|
+
this.validateConfig();
|
|
730
|
+
}
|
|
731
|
+
validateConfig() {
|
|
732
|
+
}
|
|
733
|
+
toConfig() {
|
|
734
|
+
return {
|
|
735
|
+
...super.toConfig(),
|
|
736
|
+
name: this.name,
|
|
737
|
+
description: this.description
|
|
738
|
+
};
|
|
739
|
+
}
|
|
740
|
+
async executeCommand(client, message) {
|
|
741
|
+
try {
|
|
742
|
+
await this._runPreflight(client.config.prefixCommands, this.name, client, message);
|
|
743
|
+
await this._runCabinCheck(client.config.prefixCommands, client, message);
|
|
744
|
+
return this._runFlyAndLand(client.config.prefixCommands, client, message);
|
|
745
|
+
} catch (err) {
|
|
746
|
+
this.onError?.(err, client, message);
|
|
747
|
+
client.config.prefixCommands.onError?.(err, client, message);
|
|
748
|
+
throw err;
|
|
749
|
+
} finally {
|
|
750
|
+
if (client.config.prefixCommands.logExecution) {
|
|
751
|
+
client.logger.commandExecuted(this.name, message.author.username, message.guild?.name);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
};
|
|
756
|
+
|
|
757
|
+
// src/builders/slashCommand.builder.ts
|
|
758
|
+
import { SlashCommandBuilder as DJSSlashCommandBuilder } from "discord.js";
|
|
759
|
+
import _4 from "lodash";
|
|
760
|
+
var SlashCommandBuilder = class extends BaseCommandBuilder {
|
|
761
|
+
builder;
|
|
762
|
+
deferReply;
|
|
763
|
+
deployment;
|
|
764
|
+
routes = /* @__PURE__ */ new Map();
|
|
765
|
+
onUnknownRouteHandler;
|
|
766
|
+
constructor(config) {
|
|
767
|
+
super(0 /* Slash */, config);
|
|
768
|
+
this.setBuilder(config.builder);
|
|
769
|
+
this.setRoutes(...config.routes ?? []);
|
|
770
|
+
this.deferReply = config?.deferReply;
|
|
771
|
+
this.deployment = config?.deployment;
|
|
772
|
+
this.validateConfig();
|
|
773
|
+
}
|
|
774
|
+
validateBuilder() {
|
|
775
|
+
if (!this.builder.name) {
|
|
776
|
+
throw new Error("VSlashCommandBuilder: Command name is required");
|
|
777
|
+
}
|
|
778
|
+
if (!this.builder.description) {
|
|
779
|
+
throw new Error("VSlashCommandBuilder: Command description is required");
|
|
780
|
+
}
|
|
781
|
+
try {
|
|
782
|
+
this.builder.toJSON();
|
|
783
|
+
} catch (err) {
|
|
784
|
+
throw new Error("VSlashCommandBuilder: Invalid command builder", { cause: err });
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
validateConfig() {
|
|
788
|
+
}
|
|
789
|
+
toConfig() {
|
|
790
|
+
return {
|
|
791
|
+
...super.toConfig(),
|
|
792
|
+
builder: this.builder,
|
|
793
|
+
deferReply: this.deferReply,
|
|
794
|
+
deployment: this.deployment,
|
|
795
|
+
routes: this.routes.size ? Array.from(this.routes.entries()).map(([k, v]) => ({ name: k, handler: v })) : void 0,
|
|
796
|
+
onUnknownRouteHandler: this.onUnknownRouteHandler
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
setBuilder(builder) {
|
|
800
|
+
if (typeof builder === "function") {
|
|
801
|
+
this.builder = builder(new DJSSlashCommandBuilder());
|
|
802
|
+
} else {
|
|
803
|
+
this.builder = builder;
|
|
804
|
+
}
|
|
805
|
+
this.validateBuilder();
|
|
806
|
+
return this;
|
|
807
|
+
}
|
|
808
|
+
setDeferReply(defer) {
|
|
809
|
+
this.deferReply = defer;
|
|
810
|
+
return this;
|
|
811
|
+
}
|
|
812
|
+
setDeployment(deployment) {
|
|
813
|
+
this.deployment = _4.merge(this.deployment, deployment);
|
|
814
|
+
return this;
|
|
815
|
+
}
|
|
816
|
+
setRoutes(...routes) {
|
|
817
|
+
if (routes.length) {
|
|
818
|
+
for (const route of routes) {
|
|
819
|
+
this.routes.set(route.name.toLowerCase(), route.handler);
|
|
820
|
+
}
|
|
821
|
+
} else {
|
|
822
|
+
this.routes = /* @__PURE__ */ new Map();
|
|
823
|
+
}
|
|
824
|
+
return this;
|
|
825
|
+
}
|
|
826
|
+
addRoutes(...routes) {
|
|
827
|
+
for (const route of routes) {
|
|
828
|
+
this.routes.set(route.name.toLowerCase(), route.handler);
|
|
829
|
+
}
|
|
830
|
+
return this;
|
|
831
|
+
}
|
|
832
|
+
setUnknownRouteHandler(handler) {
|
|
833
|
+
this.onUnknownRouteHandler = handler;
|
|
834
|
+
return this;
|
|
835
|
+
}
|
|
836
|
+
async executeCommand(client, interaction) {
|
|
837
|
+
try {
|
|
838
|
+
await this._runPreflight(client.config.slashCommands, interaction, client, interaction);
|
|
839
|
+
await this._runCabinCheck(client.config.slashCommands, client, interaction);
|
|
840
|
+
if (this.deferReply && !interaction.replied && !interaction.deferred) {
|
|
841
|
+
await interaction.deferReply(typeof this.deferReply === "object" ? this.deferReply : void 0);
|
|
842
|
+
}
|
|
843
|
+
if (this.routes.size) {
|
|
844
|
+
const subCommand = interaction.options.getSubcommand();
|
|
845
|
+
const route = this.routes.get(subCommand);
|
|
846
|
+
if (route) {
|
|
847
|
+
const result = await route(client, interaction);
|
|
848
|
+
await this.afterExecute?.(result, client, interaction);
|
|
849
|
+
return result;
|
|
850
|
+
} else if (this.onUnknownRouteHandler) {
|
|
851
|
+
const result = await this.onUnknownRouteHandler?.(client, interaction);
|
|
852
|
+
await this.afterExecute?.(result, client, interaction);
|
|
853
|
+
return result;
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
return this._runFlyAndLand(client.config.slashCommands, client, interaction);
|
|
857
|
+
} catch (err) {
|
|
858
|
+
this.onError?.(err, client, interaction);
|
|
859
|
+
client.config.slashCommands.onError?.(err, client, interaction);
|
|
860
|
+
throw err;
|
|
861
|
+
} finally {
|
|
862
|
+
if (client.config.slashCommands.logExecution) {
|
|
863
|
+
client.logger.commandExecuted(this.builder.name, interaction.user.username, interaction.guild?.name);
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
};
|
|
868
|
+
|
|
869
|
+
// src/client.ts
|
|
870
|
+
import { Client as Client2, userMention } from "discord.js";
|
|
871
|
+
import dotEnv from "dotenv";
|
|
872
|
+
|
|
873
|
+
// src/configs/tools.config.ts
|
|
874
|
+
import _5 from "lodash";
|
|
875
|
+
var globalVimcordToolsConfig = {
|
|
876
|
+
devMode: false,
|
|
877
|
+
embedColor: [],
|
|
878
|
+
embedColorDev: [],
|
|
879
|
+
timeouts: {
|
|
880
|
+
pagination: 6e4,
|
|
881
|
+
prompt: 3e4,
|
|
882
|
+
modalSubmit: 6e4
|
|
883
|
+
},
|
|
884
|
+
paginator: {
|
|
885
|
+
notAParticipantMessage: "You are not allowed to use this!",
|
|
886
|
+
jumpableThreshold: 5,
|
|
887
|
+
longThreshold: 4,
|
|
888
|
+
buttons: {
|
|
889
|
+
first: { label: "\u25C0\u25C0", emoji: { name: "\u23EE\uFE0F", id: "\u23EE\uFE0F" } },
|
|
890
|
+
back: { label: "\u25C0", emoji: { name: "\u25C0\uFE0F", id: "\u25C0\uFE0F" } },
|
|
891
|
+
jump: { label: "\u{1F4C4}", emoji: { name: "\u{1F4C4}", id: "\u{1F4C4}" } },
|
|
892
|
+
next: { label: "\u25B6", emoji: { name: "\u25B6\uFE0F", id: "\u25B6\uFE0F" } },
|
|
893
|
+
last: { label: "\u25B6\u25B6", emoji: { name: "\u23ED\uFE0F", id: "\u23ED\uFE0F" } }
|
|
894
|
+
}
|
|
895
|
+
},
|
|
896
|
+
prompt: {
|
|
897
|
+
defaultTitle: "Are you sure?",
|
|
898
|
+
defaultDescription: "Make sure you know what you're doing!",
|
|
899
|
+
confirmLabel: "Confirm",
|
|
900
|
+
rejectLabel: "Cancel"
|
|
901
|
+
}
|
|
902
|
+
};
|
|
903
|
+
function defineGlobalToolsConfig(options) {
|
|
904
|
+
Object.assign(globalVimcordToolsConfig, _5.merge(globalVimcordToolsConfig, options));
|
|
905
|
+
}
|
|
906
|
+
function createToolsConfig(options) {
|
|
907
|
+
return _5.merge(globalVimcordToolsConfig, options);
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
// src/configs/app.config.ts
|
|
911
|
+
import _6 from "lodash";
|
|
912
|
+
var defaultConfig = {
|
|
913
|
+
devMode: process.argv.includes("--dev"),
|
|
914
|
+
name: "Discord Bot",
|
|
915
|
+
appVersion: "1.0.0",
|
|
916
|
+
verbose: false,
|
|
917
|
+
disableBanner: false
|
|
918
|
+
};
|
|
919
|
+
function createVimcordAppConfig(options = {}) {
|
|
920
|
+
return _6.merge(defaultConfig, options);
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
// src/configs/staff.config.ts
|
|
924
|
+
import _7 from "lodash";
|
|
925
|
+
var defaultConfig2 = {
|
|
926
|
+
ownerId: null,
|
|
927
|
+
superUsers: [],
|
|
928
|
+
superUserRoles: [],
|
|
929
|
+
bypassers: [],
|
|
930
|
+
bypassesGuildAdmin: {
|
|
931
|
+
allBotStaff: false,
|
|
932
|
+
botOwner: false,
|
|
933
|
+
superUsers: false,
|
|
934
|
+
bypassers: false
|
|
935
|
+
},
|
|
936
|
+
guild: {
|
|
937
|
+
id: null,
|
|
938
|
+
inviteUrl: null,
|
|
939
|
+
channels: {}
|
|
940
|
+
}
|
|
941
|
+
};
|
|
942
|
+
function createVimcordStaffConfig(options = {}) {
|
|
943
|
+
return _7.merge(defaultConfig2, options);
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
// src/configs/slashCommand.config.ts
|
|
947
|
+
import _8 from "lodash";
|
|
948
|
+
var defaultConfig3 = {
|
|
949
|
+
logExecution: true
|
|
950
|
+
};
|
|
951
|
+
function createVimcordSlashCommandConfig(options = {}) {
|
|
952
|
+
return _8.merge(defaultConfig3, options);
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
// src/configs/prefixCommand.config.ts
|
|
956
|
+
import _9 from "lodash";
|
|
957
|
+
var defaultConfig4 = {
|
|
958
|
+
enabled: true,
|
|
959
|
+
defaultPrefix: "!",
|
|
960
|
+
allowMentionAsPrefix: true,
|
|
961
|
+
allowCaseInsensitiveCommandNames: true,
|
|
962
|
+
logExecution: true
|
|
963
|
+
};
|
|
964
|
+
function createVimcordPrefixCommandConfig(options = {}) {
|
|
965
|
+
return _9.merge(defaultConfig4, options);
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
// src/configs/contextCommand.config.ts
|
|
969
|
+
import _10 from "lodash";
|
|
970
|
+
var defaultConfig5 = {
|
|
971
|
+
enabled: true,
|
|
972
|
+
logExecution: true
|
|
973
|
+
};
|
|
974
|
+
function createVimcordContextCommandConfig(options = {}) {
|
|
975
|
+
return _10.merge(defaultConfig5, options);
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
// src/tools/Logger.ts
|
|
979
|
+
import chalk from "chalk";
|
|
980
|
+
var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
|
|
981
|
+
LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
|
|
982
|
+
LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
|
|
983
|
+
LogLevel2[LogLevel2["SUCCESS"] = 2] = "SUCCESS";
|
|
984
|
+
LogLevel2[LogLevel2["WARN"] = 3] = "WARN";
|
|
985
|
+
LogLevel2[LogLevel2["ERROR"] = 4] = "ERROR";
|
|
986
|
+
return LogLevel2;
|
|
987
|
+
})(LogLevel || {});
|
|
988
|
+
var DEFAULT_COLORS = {
|
|
989
|
+
primary: "#5865F2",
|
|
990
|
+
success: "#57F287",
|
|
991
|
+
warn: "#FEE75C",
|
|
992
|
+
danger: "#ED4245",
|
|
993
|
+
muted: "#747F8D",
|
|
994
|
+
text: "#FFFFFF"
|
|
995
|
+
};
|
|
996
|
+
var Logger = class {
|
|
997
|
+
logPrefixEmoji;
|
|
998
|
+
logPrefix;
|
|
999
|
+
minLevel;
|
|
1000
|
+
showTimestamp;
|
|
1001
|
+
colorScheme;
|
|
1002
|
+
constructor(options) {
|
|
1003
|
+
const { prefixEmoji = null, prefix = null, minLevel = 0 /* DEBUG */, showTimestamp = true } = options || {};
|
|
1004
|
+
this.logPrefixEmoji = prefixEmoji;
|
|
1005
|
+
this.logPrefix = prefix;
|
|
1006
|
+
this.minLevel = minLevel;
|
|
1007
|
+
this.showTimestamp = showTimestamp;
|
|
1008
|
+
this.colorScheme = {
|
|
1009
|
+
...DEFAULT_COLORS,
|
|
1010
|
+
...options?.colors
|
|
1011
|
+
};
|
|
1012
|
+
}
|
|
1013
|
+
formatTimestamp() {
|
|
1014
|
+
if (!this.showTimestamp) return "";
|
|
1015
|
+
const now = /* @__PURE__ */ new Date();
|
|
1016
|
+
const time = now.toLocaleTimeString("en-US", {
|
|
1017
|
+
hour12: false,
|
|
1018
|
+
hour: "2-digit",
|
|
1019
|
+
minute: "2-digit",
|
|
1020
|
+
second: "2-digit"
|
|
1021
|
+
});
|
|
1022
|
+
return chalk.hex(this.colorScheme.muted)(`[${time}]`);
|
|
1023
|
+
}
|
|
1024
|
+
formatPrefix() {
|
|
1025
|
+
if (!this.logPrefix) return "";
|
|
1026
|
+
return chalk.bold.hex(this.colorScheme.primary)(
|
|
1027
|
+
`${this.logPrefixEmoji ? `${this.logPrefixEmoji} ` : ""}${this.logPrefix}`
|
|
1028
|
+
);
|
|
1029
|
+
}
|
|
1030
|
+
shouldLog(level) {
|
|
1031
|
+
return level >= this.minLevel;
|
|
1032
|
+
}
|
|
1033
|
+
get prefixEmoji() {
|
|
1034
|
+
return this.logPrefixEmoji;
|
|
1035
|
+
}
|
|
1036
|
+
get prefix() {
|
|
1037
|
+
return this.logPrefix;
|
|
1038
|
+
}
|
|
1039
|
+
get colors() {
|
|
1040
|
+
return this.colorScheme;
|
|
1041
|
+
}
|
|
1042
|
+
extend(extras) {
|
|
1043
|
+
for (const [key, fn] of Object.entries(extras)) {
|
|
1044
|
+
if (typeof fn === "function") {
|
|
1045
|
+
this[key] = function(...args) {
|
|
1046
|
+
return fn.call(this, ...args);
|
|
1047
|
+
};
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
return this;
|
|
1051
|
+
}
|
|
1052
|
+
setPrefix(prefix) {
|
|
1053
|
+
this.logPrefix = prefix;
|
|
1054
|
+
return this;
|
|
1055
|
+
}
|
|
1056
|
+
setPrefixEmoji(prefixEmoji) {
|
|
1057
|
+
this.logPrefixEmoji = prefixEmoji;
|
|
1058
|
+
return this;
|
|
1059
|
+
}
|
|
1060
|
+
setMinLevel(minLevel) {
|
|
1061
|
+
this.minLevel = minLevel;
|
|
1062
|
+
return this;
|
|
1063
|
+
}
|
|
1064
|
+
setShowTimestamp(show) {
|
|
1065
|
+
this.showTimestamp = show;
|
|
1066
|
+
return this;
|
|
1067
|
+
}
|
|
1068
|
+
setColors(colors) {
|
|
1069
|
+
this.colorScheme = {
|
|
1070
|
+
...DEFAULT_COLORS,
|
|
1071
|
+
...colors
|
|
1072
|
+
};
|
|
1073
|
+
return this;
|
|
1074
|
+
}
|
|
1075
|
+
log(message, ...args) {
|
|
1076
|
+
console.log(this.formatTimestamp(), this.formatPrefix(), message, ...args);
|
|
1077
|
+
}
|
|
1078
|
+
debug(message, ...args) {
|
|
1079
|
+
if (!this.shouldLog(0 /* DEBUG */)) return;
|
|
1080
|
+
console.log(
|
|
1081
|
+
this.formatTimestamp(),
|
|
1082
|
+
this.formatPrefix(),
|
|
1083
|
+
chalk.hex(this.colorScheme.muted)("DEBUG"),
|
|
1084
|
+
chalk.dim(message),
|
|
1085
|
+
...args
|
|
1086
|
+
);
|
|
1087
|
+
}
|
|
1088
|
+
info(message, ...args) {
|
|
1089
|
+
if (!this.shouldLog(1 /* INFO */)) return;
|
|
1090
|
+
console.log(this.formatTimestamp(), this.formatPrefix(), chalk.hex("#87CEEB")("INFO"), message, ...args);
|
|
1091
|
+
}
|
|
1092
|
+
success(message, ...args) {
|
|
1093
|
+
if (!this.shouldLog(2 /* SUCCESS */)) return;
|
|
1094
|
+
console.log(
|
|
1095
|
+
this.formatTimestamp(),
|
|
1096
|
+
this.formatPrefix(),
|
|
1097
|
+
chalk.bold.hex(this.colorScheme.success)("\u2713 SUCCESS"),
|
|
1098
|
+
chalk.hex(this.colorScheme.success)(message),
|
|
1099
|
+
...args
|
|
1100
|
+
);
|
|
1101
|
+
}
|
|
1102
|
+
warn(message, ...args) {
|
|
1103
|
+
if (!this.shouldLog(3 /* WARN */)) return;
|
|
1104
|
+
console.warn(
|
|
1105
|
+
this.formatTimestamp(),
|
|
1106
|
+
this.formatPrefix(),
|
|
1107
|
+
chalk.bold.hex(this.colorScheme.warn)("\u26A0 WARN"),
|
|
1108
|
+
chalk.hex(this.colorScheme.warn)(message),
|
|
1109
|
+
...args
|
|
1110
|
+
);
|
|
1111
|
+
}
|
|
1112
|
+
error(message, error, ...args) {
|
|
1113
|
+
if (!this.shouldLog(4 /* ERROR */)) return;
|
|
1114
|
+
console.error(
|
|
1115
|
+
this.formatTimestamp(),
|
|
1116
|
+
this.formatPrefix(),
|
|
1117
|
+
chalk.bold.hex(this.colorScheme.danger)("\u2715 ERROR"),
|
|
1118
|
+
chalk.hex(this.colorScheme.danger)(message),
|
|
1119
|
+
...args
|
|
1120
|
+
);
|
|
1121
|
+
if (error && error.stack) {
|
|
1122
|
+
console.error(chalk.dim(error.stack));
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
loader(message) {
|
|
1126
|
+
const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
1127
|
+
let i = 0;
|
|
1128
|
+
const interval = setInterval(() => {
|
|
1129
|
+
process.stdout.write(
|
|
1130
|
+
`\r${this.formatTimestamp()} ${this.formatPrefix()} ${chalk.hex(this.colorScheme.warn)(frames[i])} ${message}`
|
|
1131
|
+
);
|
|
1132
|
+
i = (i + 1) % frames.length;
|
|
1133
|
+
}, 100);
|
|
1134
|
+
return (newMessage) => {
|
|
1135
|
+
clearInterval(interval);
|
|
1136
|
+
process.stdout.write(
|
|
1137
|
+
`\r${this.formatTimestamp()} ${this.formatPrefix()} ${chalk.hex(this.colorScheme.success)("\u2713")} ${newMessage || message}
|
|
1138
|
+
`
|
|
1139
|
+
);
|
|
1140
|
+
};
|
|
1141
|
+
}
|
|
1142
|
+
table(title, data) {
|
|
1143
|
+
console.log(this.formatTimestamp(), this.formatPrefix(), chalk.bold(title));
|
|
1144
|
+
Object.entries(data).forEach(([key, value]) => {
|
|
1145
|
+
const formattedKey = chalk.hex(this.colorScheme.warn)(` ${key}`);
|
|
1146
|
+
const formattedValue = chalk.hex(this.colorScheme.muted)(value);
|
|
1147
|
+
console.log(`${formattedKey.padEnd(25)} ${formattedValue}`);
|
|
1148
|
+
});
|
|
1149
|
+
}
|
|
1150
|
+
section(title) {
|
|
1151
|
+
const line = "\u2500".repeat(Math.max(30, title.length + 4));
|
|
1152
|
+
console.log(chalk.hex(this.colorScheme.muted)(`
|
|
1153
|
+
\u250C\u2500${line}\u2500\u2510`));
|
|
1154
|
+
console.log(
|
|
1155
|
+
chalk.hex(this.colorScheme.muted)("\u2502 ") + chalk.bold.hex(this.colorScheme.text)(title.padEnd(line.length)) + chalk.hex(this.colorScheme.muted)(" \u2502")
|
|
1156
|
+
);
|
|
1157
|
+
console.log(chalk.hex(this.colorScheme.muted)(`\u2514\u2500${line}\u2500\u2518`));
|
|
1158
|
+
}
|
|
1159
|
+
};
|
|
1160
|
+
var logger = new Logger();
|
|
1161
|
+
|
|
1162
|
+
// src/tools/utils.ts
|
|
1163
|
+
function __zero(str) {
|
|
1164
|
+
return str?.length ? str : "0";
|
|
1165
|
+
}
|
|
1166
|
+
function isMentionOrSnowflake(str) {
|
|
1167
|
+
return str ? str.match(/<@[#&]?[\d]{6,}>/) || str.match(/\d{6,}/) ? true : false : false;
|
|
1168
|
+
}
|
|
1169
|
+
function cleanMention(str) {
|
|
1170
|
+
return str ? str.replaceAll(/[<@#&>]/g, "").trim() : void 0;
|
|
1171
|
+
}
|
|
1172
|
+
async function getMessageMention(message, content, type, index = 0, idOnly) {
|
|
1173
|
+
const args = content?.split(" ");
|
|
1174
|
+
const arg = isMentionOrSnowflake(args?.[index]) ? cleanMention(args?.[index]) : void 0;
|
|
1175
|
+
switch (type) {
|
|
1176
|
+
case "user":
|
|
1177
|
+
const userMention2 = message.mentions.users.at(index) || null;
|
|
1178
|
+
if (!userMention2 && arg) {
|
|
1179
|
+
return idOnly ? arg : await fetchUser(message.client, arg);
|
|
1180
|
+
} else {
|
|
1181
|
+
return idOnly ? userMention2?.id || null : userMention2;
|
|
1182
|
+
}
|
|
1183
|
+
case "member":
|
|
1184
|
+
if (!message.guild) return null;
|
|
1185
|
+
const member = await fetchMember(message.guild, message.mentions.users.at(index)?.id ?? arg);
|
|
1186
|
+
return idOnly ? member?.id || null : member;
|
|
1187
|
+
case "channel":
|
|
1188
|
+
const channelMention = message.mentions.channels.at(index) || null;
|
|
1189
|
+
if (!channelMention && arg) {
|
|
1190
|
+
return idOnly ? arg : message.guild ? await fetchChannel(message.guild, arg) : message.client.channels.cache.get(__zero(arg)) ?? message.client.channels.fetch(__zero(arg));
|
|
1191
|
+
} else {
|
|
1192
|
+
return idOnly ? channelMention?.id || null : channelMention;
|
|
1193
|
+
}
|
|
1194
|
+
case "role":
|
|
1195
|
+
const roleMention = message.mentions.roles.at(index) || null;
|
|
1196
|
+
if (!roleMention && arg) {
|
|
1197
|
+
return idOnly ? arg : message.guild ? await fetchRole(message.guild, arg) : null;
|
|
1198
|
+
} else {
|
|
1199
|
+
return idOnly ? roleMention?.id || null : roleMention;
|
|
1200
|
+
}
|
|
1201
|
+
default:
|
|
1202
|
+
return null;
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
function getFirstMentionId(options) {
|
|
1206
|
+
let mentionId = "";
|
|
1207
|
+
if (options.message) {
|
|
1208
|
+
switch (options.type) {
|
|
1209
|
+
case "user":
|
|
1210
|
+
mentionId = options.message.mentions.users.first()?.id || "";
|
|
1211
|
+
break;
|
|
1212
|
+
case "channel":
|
|
1213
|
+
mentionId = options.message.mentions.channels.first()?.id || "";
|
|
1214
|
+
break;
|
|
1215
|
+
case "role":
|
|
1216
|
+
mentionId = options.message.mentions.roles.first()?.id || "";
|
|
1217
|
+
break;
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
const firstArg = options.content?.split(" ")[0] || "";
|
|
1221
|
+
return mentionId || isMentionOrSnowflake(firstArg) ? cleanMention(firstArg) : "";
|
|
1222
|
+
}
|
|
1223
|
+
async function fetchUser(client, userId) {
|
|
1224
|
+
if (!userId) return null;
|
|
1225
|
+
return client.users.cache.get(__zero(userId)) || await client.users.fetch(__zero(userId)).catch(() => null);
|
|
1226
|
+
}
|
|
1227
|
+
async function fetchGuild(client, guildId) {
|
|
1228
|
+
if (!guildId) return null;
|
|
1229
|
+
return client.guilds.cache.get(__zero(guildId)) || await client.guilds.fetch(__zero(guildId)).catch(() => null);
|
|
1230
|
+
}
|
|
1231
|
+
async function fetchMember(guild, memberId) {
|
|
1232
|
+
if (!memberId) return null;
|
|
1233
|
+
return guild.members.cache.get(__zero(memberId)) || await guild.members.fetch(__zero(memberId)).catch(() => null);
|
|
1234
|
+
}
|
|
1235
|
+
async function fetchChannel(guild, channelId, type) {
|
|
1236
|
+
if (!channelId) return null;
|
|
1237
|
+
const channel = guild.channels.cache.get(__zero(channelId)) || await guild.channels.fetch(__zero(channelId)).catch(() => null);
|
|
1238
|
+
if (type && channel?.type !== type) return null;
|
|
1239
|
+
return channel;
|
|
1240
|
+
}
|
|
1241
|
+
async function fetchRole(guild, roleId) {
|
|
1242
|
+
if (!roleId) return null;
|
|
1243
|
+
return guild.roles.cache.get(__zero(roleId)) || await guild.roles.fetch(__zero(roleId)).catch(() => null) || null;
|
|
1244
|
+
}
|
|
1245
|
+
async function fetchMessage(channel, messageId) {
|
|
1246
|
+
if (!messageId) return null;
|
|
1247
|
+
return channel.messages.cache.get(__zero(messageId)) || await channel.messages.fetch(__zero(messageId)).catch(() => null) || null;
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
// src/types/status.ts
|
|
1251
|
+
import { ActivityType } from "discord.js";
|
|
1252
|
+
import _11 from "lodash";
|
|
1253
|
+
var StatusType = /* @__PURE__ */ ((StatusType2) => {
|
|
1254
|
+
StatusType2["DND"] = "dnd";
|
|
1255
|
+
StatusType2["Idle"] = "idle";
|
|
1256
|
+
StatusType2["Online"] = "online";
|
|
1257
|
+
StatusType2["Invisible"] = "invisible";
|
|
1258
|
+
return StatusType2;
|
|
1259
|
+
})(StatusType || {});
|
|
1260
|
+
var defaultPresence = {
|
|
1261
|
+
production: {
|
|
1262
|
+
interval: 6e4,
|
|
1263
|
+
randomize: false,
|
|
1264
|
+
activity: [
|
|
1265
|
+
{ status: "online" /* Online */, type: ActivityType.Custom, name: "Need help? Use /help or !help" },
|
|
1266
|
+
{ status: "online" /* Online */, type: ActivityType.Custom, name: "Join our community!" },
|
|
1267
|
+
{ status: "online" /* Online */, type: ActivityType.Watching, name: "\u2728 $GUILD_COUNT servers" }
|
|
1268
|
+
]
|
|
1269
|
+
},
|
|
1270
|
+
development: {
|
|
1271
|
+
activity: { status: "dnd" /* DND */, type: ActivityType.Custom, name: "In development!" }
|
|
1272
|
+
}
|
|
1273
|
+
};
|
|
1274
|
+
function createVimcordStatusConfig(options = {}) {
|
|
1275
|
+
return _11.merge(defaultPresence, options);
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
// src/utils/number.ts
|
|
1279
|
+
function formatThousands(num, sep = ",") {
|
|
1280
|
+
return `${num}`.replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, sep);
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
// src/utils/random.ts
|
|
1284
|
+
function pickRandom(arr, options) {
|
|
1285
|
+
const _rnd = () => {
|
|
1286
|
+
return arr[Math.floor(Math.random() * arr.length)];
|
|
1287
|
+
};
|
|
1288
|
+
let att = 0;
|
|
1289
|
+
let candidate = _rnd();
|
|
1290
|
+
if (options?.notEqualTo !== void 0 && arr.length > 1) {
|
|
1291
|
+
while (candidate === options.notEqualTo) {
|
|
1292
|
+
if (att < (options?.maxRerollAttempts ?? 100)) {
|
|
1293
|
+
throw new Error(`pickRandom reached max reroll attempts (${options?.maxRerollAttempts ?? 100})`);
|
|
1294
|
+
}
|
|
1295
|
+
candidate = _rnd();
|
|
1296
|
+
att++;
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
return options?.clone ? structuredClone(candidate) : candidate;
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
// src/modules/status.manager.ts
|
|
1303
|
+
import cron from "node-cron";
|
|
1304
|
+
import EventEmitter from "events";
|
|
1305
|
+
var VimcordStatusManager = class {
|
|
1306
|
+
client;
|
|
1307
|
+
logger;
|
|
1308
|
+
emitter = new EventEmitter();
|
|
1309
|
+
lastActivity = null;
|
|
1310
|
+
lastActivityIndex = 0;
|
|
1311
|
+
task = null;
|
|
1312
|
+
constructor(client) {
|
|
1313
|
+
this.client = client;
|
|
1314
|
+
this.logger = new Logger({ prefixEmoji: "\u{1F4AC}", prefix: `StatusManager (i${this.client.index})` });
|
|
1315
|
+
this.emitter.on("changed", (activity) => {
|
|
1316
|
+
if (this.client.config.app.verbose) {
|
|
1317
|
+
this.logger.debug(`Status changed to '${activity.name}'`);
|
|
1318
|
+
}
|
|
1319
|
+
});
|
|
1320
|
+
this.emitter.on("cleared", () => {
|
|
1321
|
+
if (this.client.config.app.verbose) {
|
|
1322
|
+
this.logger.debug("Status cleared");
|
|
1323
|
+
}
|
|
1324
|
+
});
|
|
1325
|
+
this.emitter.on("rotation", () => {
|
|
1326
|
+
if (this.client.config.app.verbose) {
|
|
1327
|
+
this.logger.debug("Status rotated");
|
|
1328
|
+
}
|
|
1329
|
+
});
|
|
1330
|
+
this.emitter.on("rotationStarted", () => {
|
|
1331
|
+
if (this.client.config.app.verbose) {
|
|
1332
|
+
this.logger.debug("Status rotation resumed \u25B6\uFE0F");
|
|
1333
|
+
}
|
|
1334
|
+
});
|
|
1335
|
+
this.emitter.on("rotationPaused", () => {
|
|
1336
|
+
if (this.client.config.app.verbose) {
|
|
1337
|
+
this.logger.debug("Status rotation paused \u23F8\uFE0F");
|
|
1338
|
+
}
|
|
1339
|
+
});
|
|
1340
|
+
}
|
|
1341
|
+
clearData() {
|
|
1342
|
+
this.task?.destroy();
|
|
1343
|
+
this.task = null;
|
|
1344
|
+
this.lastActivity = null;
|
|
1345
|
+
this.lastActivityIndex = 0;
|
|
1346
|
+
return this;
|
|
1347
|
+
}
|
|
1348
|
+
async getReadyClient() {
|
|
1349
|
+
const client = await this.client.whenReady();
|
|
1350
|
+
if (!client.user) throw new Error("Cannot manage the client's activity when its user is not hydrated");
|
|
1351
|
+
return client;
|
|
1352
|
+
}
|
|
1353
|
+
async formatActivityName(name) {
|
|
1354
|
+
name = name.replace("$USER_COUNT", formatThousands(this.client.users.cache.size)).replace("$GUILD_COUNT", formatThousands(this.client.guilds.cache.size)).replace(
|
|
1355
|
+
"$INVITE",
|
|
1356
|
+
this.client.config.staff.guild.inviteUrl ? this.client.config.staff.guild.inviteUrl : "<STAFF_INVITE_URL_NOT_SET>"
|
|
1357
|
+
);
|
|
1358
|
+
if (name.includes("$STAFF_GUILD_MEMBER_COUNT")) {
|
|
1359
|
+
await fetchGuild(this.client, this.client.config.staff.guild.id).then((guild) => {
|
|
1360
|
+
if (!guild) return name = name.replace("$STAFF_GUILD_MEMBER_COUNT", "<STAFF_GUILD_NOT_FOUND>");
|
|
1361
|
+
name = name.replace("$STAFF_GUILD_MEMBER_COUNT", formatThousands(guild.members.cache.size));
|
|
1362
|
+
}).catch((err) => this.logger.error("Failed to fetch the staff guild", err));
|
|
1363
|
+
}
|
|
1364
|
+
return name;
|
|
1365
|
+
}
|
|
1366
|
+
async setActivity(activity) {
|
|
1367
|
+
const client = await this.getReadyClient();
|
|
1368
|
+
activity.name = await this.formatActivityName(activity.name);
|
|
1369
|
+
client.user.setStatus(activity.status);
|
|
1370
|
+
client.user.setActivity({ name: activity.name, type: activity.type, url: activity.streamUrl });
|
|
1371
|
+
this.emitter.emit("changed", activity);
|
|
1372
|
+
}
|
|
1373
|
+
async statusRotationTask(clientStatus) {
|
|
1374
|
+
let activity;
|
|
1375
|
+
if (clientStatus.randomize) {
|
|
1376
|
+
activity = pickRandom(clientStatus.activity, {
|
|
1377
|
+
notEqualTo: this.lastActivity,
|
|
1378
|
+
clone: true
|
|
1379
|
+
});
|
|
1380
|
+
this.lastActivity = activity;
|
|
1381
|
+
} else {
|
|
1382
|
+
const activityIndex = (this.lastActivityIndex + 1) % clientStatus.activity.length;
|
|
1383
|
+
this.lastActivityIndex = activityIndex;
|
|
1384
|
+
activity = clientStatus.activity[activityIndex];
|
|
1385
|
+
}
|
|
1386
|
+
await this.setActivity(activity);
|
|
1387
|
+
this.emitter.emit("rotation", activity);
|
|
1388
|
+
}
|
|
1389
|
+
async scheduleStatusRotation(clientStatus) {
|
|
1390
|
+
if (!clientStatus.interval) throw new Error("Cannot create client activity interval without interval time");
|
|
1391
|
+
this.task?.destroy();
|
|
1392
|
+
this.task = null;
|
|
1393
|
+
await this.statusRotationTask(clientStatus);
|
|
1394
|
+
this.task = cron.createTask(
|
|
1395
|
+
`*/${clientStatus.interval} * * * * *`,
|
|
1396
|
+
async () => await this.statusRotationTask(clientStatus),
|
|
1397
|
+
{ noOverlap: true }
|
|
1398
|
+
);
|
|
1399
|
+
this.start();
|
|
1400
|
+
this.emitter.emit("rotationStarted", this.task);
|
|
1401
|
+
}
|
|
1402
|
+
start() {
|
|
1403
|
+
if (this.task) {
|
|
1404
|
+
this.task.start();
|
|
1405
|
+
this.emitter.emit("rotationStarted", this.task);
|
|
1406
|
+
if (this.client.config.app.verbose) {
|
|
1407
|
+
this.logger.debug("Status rotation started");
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
return this;
|
|
1411
|
+
}
|
|
1412
|
+
pause() {
|
|
1413
|
+
if (this.task) {
|
|
1414
|
+
this.task.stop();
|
|
1415
|
+
this.emitter.emit("rotationPaused", this.task);
|
|
1416
|
+
if (this.client.config.app.verbose) {
|
|
1417
|
+
this.logger.debug("Status rotation paused");
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
return this;
|
|
1421
|
+
}
|
|
1422
|
+
async set(status) {
|
|
1423
|
+
const statusConfig = createVimcordStatusConfig(status);
|
|
1424
|
+
let clientStatus;
|
|
1425
|
+
if (this.client.config.app.devMode) {
|
|
1426
|
+
clientStatus = statusConfig.development;
|
|
1427
|
+
} else {
|
|
1428
|
+
clientStatus = statusConfig.production;
|
|
1429
|
+
}
|
|
1430
|
+
if (!clientStatus.interval) {
|
|
1431
|
+
await this.setActivity(Array.isArray(clientStatus.activity) ? clientStatus.activity[0] : clientStatus.activity);
|
|
1432
|
+
return this;
|
|
1433
|
+
}
|
|
1434
|
+
if (clientStatus.interval) {
|
|
1435
|
+
await this.scheduleStatusRotation(clientStatus);
|
|
1436
|
+
}
|
|
1437
|
+
return this;
|
|
1438
|
+
}
|
|
1439
|
+
async destroy() {
|
|
1440
|
+
if (this.task) {
|
|
1441
|
+
this.task.destroy();
|
|
1442
|
+
this.task = null;
|
|
1443
|
+
this.emitter.emit("rotationDestroyed");
|
|
1444
|
+
if (this.client.config.app.verbose) {
|
|
1445
|
+
this.logger.debug("Status rotation destroyed");
|
|
1446
|
+
}
|
|
1447
|
+
await this.clear();
|
|
1448
|
+
}
|
|
1449
|
+
return this;
|
|
1450
|
+
}
|
|
1451
|
+
async clear() {
|
|
1452
|
+
const client = await this.getReadyClient();
|
|
1453
|
+
this.clearData();
|
|
1454
|
+
client.user.setActivity({ name: "" });
|
|
1455
|
+
this.emitter.emit("cleared");
|
|
1456
|
+
if (this.client.config.app.verbose) {
|
|
1457
|
+
this.logger.debug("Status cleared");
|
|
1458
|
+
}
|
|
1459
|
+
return this;
|
|
1460
|
+
}
|
|
1461
|
+
};
|
|
1462
|
+
|
|
1463
|
+
// src/modules/command.manager.ts
|
|
1464
|
+
import { REST, Routes } from "discord.js";
|
|
1465
|
+
var VimcordCommandManager = class {
|
|
1466
|
+
slash;
|
|
1467
|
+
prefix;
|
|
1468
|
+
context;
|
|
1469
|
+
constructor(client) {
|
|
1470
|
+
this.slash = new VimcordSlashCommandManager(client);
|
|
1471
|
+
this.prefix = new VimcordPrefixCommandManager(client);
|
|
1472
|
+
this.context = new VimcordContextCommandManager(client);
|
|
1473
|
+
}
|
|
1474
|
+
};
|
|
1475
|
+
var VimcordSlashCommandManager = class {
|
|
1476
|
+
constructor(client) {
|
|
1477
|
+
this.client = client;
|
|
1478
|
+
client.whenReady().then((c) => this.rest = new REST().setToken(c.token));
|
|
1479
|
+
}
|
|
1480
|
+
commands = /* @__PURE__ */ new Map();
|
|
1481
|
+
rest;
|
|
1482
|
+
get(name) {
|
|
1483
|
+
return this.commands.get(name);
|
|
1484
|
+
}
|
|
1485
|
+
getAll(options) {
|
|
1486
|
+
const matchedCommands = /* @__PURE__ */ new Map();
|
|
1487
|
+
for (const cmd of this.commands.values()) {
|
|
1488
|
+
let matched = null;
|
|
1489
|
+
if (options?.names || options?.fuzzyNames || options?.guilds) {
|
|
1490
|
+
const nameMatched = (
|
|
1491
|
+
// Exact name match
|
|
1492
|
+
options.names?.includes(cmd.builder.name) || // Fuzzy match
|
|
1493
|
+
options.fuzzyNames?.some((fuzzyCommand) => cmd.builder.name.includes(fuzzyCommand))
|
|
1494
|
+
);
|
|
1495
|
+
if ((options.names || options.fuzzyNames) && !nameMatched) continue;
|
|
1496
|
+
const guildMatched = cmd.deployment?.guilds?.some((guildId) => options.guilds?.includes(guildId));
|
|
1497
|
+
if (options.guilds && !guildMatched) continue;
|
|
1498
|
+
matched = cmd;
|
|
1499
|
+
} else {
|
|
1500
|
+
matched = cmd;
|
|
1501
|
+
}
|
|
1502
|
+
if (!matched) continue;
|
|
1503
|
+
if (options?.ignoreDeploymentOptions) {
|
|
1504
|
+
if (matchedCommands.has(matched.builder.name)) continue;
|
|
1505
|
+
matchedCommands.set(matched.builder.name, matched);
|
|
1506
|
+
continue;
|
|
1507
|
+
}
|
|
1508
|
+
const isProperEnvironment = matched?.deployment?.environments?.includes(
|
|
1509
|
+
this.client.config.app.devMode ? "development" : "production"
|
|
1510
|
+
);
|
|
1511
|
+
if ((isProperEnvironment ?? true) && ((options?.globalOnly && (matched?.deployment?.global ?? true)) ?? true)) {
|
|
1512
|
+
matchedCommands.set(matched.builder.name, matched);
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
return matchedCommands;
|
|
1516
|
+
}
|
|
1517
|
+
/** Import command modules that end with `.slash` */
|
|
1518
|
+
async importFrom(dir, replaceAll) {
|
|
1519
|
+
if (replaceAll) {
|
|
1520
|
+
this.commands.clear();
|
|
1521
|
+
}
|
|
1522
|
+
dir = Array.isArray(dir) ? dir : [dir];
|
|
1523
|
+
const commandModules = await Promise.all(
|
|
1524
|
+
dir.map((dir2) => importModulesFromDir(dir2, "slash"))
|
|
1525
|
+
);
|
|
1526
|
+
let importedCommands = 0;
|
|
1527
|
+
for (const command of commandModules.flat()) {
|
|
1528
|
+
const isProperEnvironment = command.module.default.deployment?.environments?.includes(
|
|
1529
|
+
this.client.config.app.devMode ? "development" : "production"
|
|
1530
|
+
);
|
|
1531
|
+
if (isProperEnvironment ?? true) {
|
|
1532
|
+
this.commands.set(command.module.default.builder.name, command.module.default);
|
|
1533
|
+
importedCommands++;
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
this.client.logger.moduleLoaded("Slash Commands", importedCommands);
|
|
1537
|
+
return this.commands;
|
|
1538
|
+
}
|
|
1539
|
+
async registerGuild(options) {
|
|
1540
|
+
const client = await this.client.whenReady();
|
|
1541
|
+
const commandsToRegister = Array.from(
|
|
1542
|
+
this.getAll({
|
|
1543
|
+
names: options?.commands,
|
|
1544
|
+
fuzzyNames: options?.fuzzyCommands
|
|
1545
|
+
}).values()
|
|
1546
|
+
).map((cmd) => cmd.builder.toJSON());
|
|
1547
|
+
if (!commandsToRegister.length) {
|
|
1548
|
+
console.log("[SlashCommandManager] No commands to register");
|
|
1549
|
+
return;
|
|
1550
|
+
}
|
|
1551
|
+
const guildIds = options?.guilds || client.guilds.cache.map((guild) => guild.id);
|
|
1552
|
+
console.log(
|
|
1553
|
+
`[SlashCommandManager] Registering ${commandsToRegister.length} app (/) ${commandsToRegister.length === 1 ? "command" : "commands"} for ${guildIds.length} ${guildIds.length === 1 ? "guild" : "guilds"}...`
|
|
1554
|
+
);
|
|
1555
|
+
await Promise.all(
|
|
1556
|
+
guildIds.map(
|
|
1557
|
+
(guildId) => this.rest.put(Routes.applicationGuildCommands(client.user.id, guildId), {
|
|
1558
|
+
body: commandsToRegister
|
|
1559
|
+
}).then(
|
|
1560
|
+
() => console.log(
|
|
1561
|
+
`[SlashCommandManager] \u2714 Set app (/) commands in guild: ${guildId} (${client.guilds.cache.find((g) => g.id === guildId)?.name || "n/a"})`
|
|
1562
|
+
)
|
|
1563
|
+
).catch(
|
|
1564
|
+
(err) => console.log(
|
|
1565
|
+
`[SlashCommandManager] \u2716 Failed to set app (/) commands in guild: ${guildId} (${client.guilds.cache.find((g) => g.id === guildId)?.name || "n/a"})`,
|
|
1566
|
+
err
|
|
1567
|
+
)
|
|
1568
|
+
)
|
|
1569
|
+
)
|
|
1570
|
+
);
|
|
1571
|
+
console.log(
|
|
1572
|
+
`[SlashCommandManager] \u2714 Finished registering app (/) commands for ${guildIds.length} ${guildIds.length === 1 ? "guild" : "guilds"}`
|
|
1573
|
+
);
|
|
1574
|
+
}
|
|
1575
|
+
async unregisterGuild(options) {
|
|
1576
|
+
const client = await this.client.whenReady();
|
|
1577
|
+
const guildIds = options?.guilds || client.guilds.cache.map((guild) => guild.id);
|
|
1578
|
+
console.log(
|
|
1579
|
+
`[SlashCommandManager] Unregistering app (/) commands from ${guildIds.length} ${guildIds.length === 1 ? "guild" : "guilds"}...`
|
|
1580
|
+
);
|
|
1581
|
+
await Promise.all(
|
|
1582
|
+
guildIds.map(
|
|
1583
|
+
(guildId) => this.rest.put(Routes.applicationGuildCommands(client.user.id, guildId), {
|
|
1584
|
+
body: []
|
|
1585
|
+
}).then(
|
|
1586
|
+
() => console.log(
|
|
1587
|
+
`[SlashCommandManager] \u2714 Removed app (/) commands in guild: ${guildId} (${client.guilds.cache.find((g) => g.id === guildId)?.name || "n/a"})`
|
|
1588
|
+
)
|
|
1589
|
+
).catch(
|
|
1590
|
+
(err) => console.log(
|
|
1591
|
+
`[SlashCommandManager] \u2716 Failed to remove app (/) commands in guild: ${guildId} (${client.guilds.cache.find((g) => g.id === guildId)?.name || "n/a"})`,
|
|
1592
|
+
err
|
|
1593
|
+
)
|
|
1594
|
+
)
|
|
1595
|
+
)
|
|
1596
|
+
);
|
|
1597
|
+
console.log(
|
|
1598
|
+
`[SlashCommandManager] \u2714 Finished unregistering app (/) commands for ${guildIds.length} ${guildIds.length === 1 ? "guild" : "guilds"}`
|
|
1599
|
+
);
|
|
1600
|
+
}
|
|
1601
|
+
async registerGlobal(options) {
|
|
1602
|
+
const client = await this.client.whenReady();
|
|
1603
|
+
const commandsToRegister = Array.from(
|
|
1604
|
+
this.getAll({
|
|
1605
|
+
names: options?.commands,
|
|
1606
|
+
fuzzyNames: options?.fuzzyCommands,
|
|
1607
|
+
globalOnly: true
|
|
1608
|
+
}).values()
|
|
1609
|
+
).map((cmd) => cmd.builder.toJSON());
|
|
1610
|
+
if (!commandsToRegister.length) {
|
|
1611
|
+
console.log("[SlashCommandManager] No commands to register");
|
|
1612
|
+
return;
|
|
1613
|
+
}
|
|
1614
|
+
console.log(
|
|
1615
|
+
`[SlashCommandManager] Registering ${commandsToRegister.length} app (/) ${commandsToRegister.length === 1 ? "command" : "commands"} globally...`
|
|
1616
|
+
);
|
|
1617
|
+
try {
|
|
1618
|
+
await this.rest.put(Routes.applicationCommands(client.user.id), { body: commandsToRegister });
|
|
1619
|
+
console.log("[SlashCommandManager] \u2714 Registered app (/) commands globally");
|
|
1620
|
+
} catch (err) {
|
|
1621
|
+
console.log("[SlashCommandManager] \u2716 Failed to register app (/) commands globally", err);
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
async unregisterGlobal() {
|
|
1625
|
+
const client = await this.client.whenReady();
|
|
1626
|
+
console.log("[SlashCommandManager] Unregistering app (/) commands globally...");
|
|
1627
|
+
try {
|
|
1628
|
+
await this.rest.put(Routes.applicationCommands(client.user.id), { body: [] });
|
|
1629
|
+
console.log("[SlashCommandManager] \u2714 Removed app (/) commands globally");
|
|
1630
|
+
} catch (err) {
|
|
1631
|
+
console.log("[SlashCommandManager] \u2716 Failed to remove app (/) commands globally", err);
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
};
|
|
1635
|
+
var VimcordPrefixCommandManager = class {
|
|
1636
|
+
constructor(client) {
|
|
1637
|
+
this.client = client;
|
|
1638
|
+
}
|
|
1639
|
+
commands = /* @__PURE__ */ new Map();
|
|
1640
|
+
get(name) {
|
|
1641
|
+
if (this.client.config.prefixCommands.allowCaseInsensitiveCommandNames) {
|
|
1642
|
+
name = name.toLowerCase();
|
|
1643
|
+
return Array.from(this.commands.values()).find((cmd) => cmd.name.toLowerCase() === name);
|
|
1644
|
+
}
|
|
1645
|
+
return this.commands.get(name);
|
|
1646
|
+
}
|
|
1647
|
+
getByAlias(alias) {
|
|
1648
|
+
if (this.client.config.prefixCommands.allowCaseInsensitiveCommandNames) {
|
|
1649
|
+
alias = alias.toLowerCase();
|
|
1650
|
+
return Array.from(this.commands.values()).find((cmd) => cmd.aliases?.includes(alias));
|
|
1651
|
+
}
|
|
1652
|
+
return Array.from(this.commands.values()).find((cmd) => cmd.aliases?.includes(alias));
|
|
1653
|
+
}
|
|
1654
|
+
/** Import command modules that end with `.prefix` */
|
|
1655
|
+
async importFrom(dir, replaceAll) {
|
|
1656
|
+
if (replaceAll) {
|
|
1657
|
+
this.commands.clear();
|
|
1658
|
+
}
|
|
1659
|
+
dir = Array.isArray(dir) ? dir : [dir];
|
|
1660
|
+
const commandModules = await Promise.all(
|
|
1661
|
+
dir.map((dir2) => importModulesFromDir(dir2, "prefix"))
|
|
1662
|
+
);
|
|
1663
|
+
let importedCommands = 0;
|
|
1664
|
+
for (const command of commandModules.flat()) {
|
|
1665
|
+
this.commands.set(command.module.default.name, command.module.default);
|
|
1666
|
+
importedCommands++;
|
|
1667
|
+
}
|
|
1668
|
+
this.client.logger.moduleLoaded("Prefix Commands", importedCommands);
|
|
1669
|
+
return this.commands;
|
|
1670
|
+
}
|
|
1671
|
+
};
|
|
1672
|
+
var VimcordContextCommandManager = class {
|
|
1673
|
+
constructor(client) {
|
|
1674
|
+
this.client = client;
|
|
1675
|
+
}
|
|
1676
|
+
commands = /* @__PURE__ */ new Map();
|
|
1677
|
+
/** Import command modules that end with `.ctx` */
|
|
1678
|
+
async importFrom(dir, replaceAll) {
|
|
1679
|
+
if (replaceAll) {
|
|
1680
|
+
this.commands.clear();
|
|
1681
|
+
}
|
|
1682
|
+
dir = Array.isArray(dir) ? dir : [dir];
|
|
1683
|
+
const commandModules = await Promise.all(
|
|
1684
|
+
dir.map((dir2) => importModulesFromDir(dir2, "ctx"))
|
|
1685
|
+
);
|
|
1686
|
+
let importedCommands = 0;
|
|
1687
|
+
for (const command of commandModules.flat()) {
|
|
1688
|
+
this.commands.set(command.module.default.builder.name, command.module.default);
|
|
1689
|
+
importedCommands++;
|
|
1690
|
+
}
|
|
1691
|
+
this.client.logger.moduleLoaded("Context Commands", importedCommands);
|
|
1692
|
+
return this.commands;
|
|
1693
|
+
}
|
|
1694
|
+
};
|
|
1695
|
+
|
|
1696
|
+
// src/modules/event.manager.ts
|
|
1697
|
+
import { Events } from "discord.js";
|
|
1698
|
+
var VimcordEventManager = class {
|
|
1699
|
+
client;
|
|
1700
|
+
events = /* @__PURE__ */ new Map();
|
|
1701
|
+
logger;
|
|
1702
|
+
constructor(client) {
|
|
1703
|
+
this.client = client;
|
|
1704
|
+
this.logger = new Logger({ prefixEmoji: "\u{1F4CB}", prefix: `EventManager (i${this.client.index})` });
|
|
1705
|
+
for (const event of Object.values(Events)) {
|
|
1706
|
+
client.on(
|
|
1707
|
+
event.toString(),
|
|
1708
|
+
async (...args) => this.executeEvents.apply(this, [event, ...args])
|
|
1709
|
+
);
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
register(...events) {
|
|
1713
|
+
for (const event of events) {
|
|
1714
|
+
this.events.set(event.name, event);
|
|
1715
|
+
if (this.client.config.app.verbose) {
|
|
1716
|
+
this.logger.debug(`'${event.name}' registered for EventType '${event.event}'`);
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
unregister(...names) {
|
|
1721
|
+
for (const name of names) {
|
|
1722
|
+
const event = this.events.get(name);
|
|
1723
|
+
if (!event) continue;
|
|
1724
|
+
this.events.delete(name);
|
|
1725
|
+
if (this.client.config.app.verbose) {
|
|
1726
|
+
this.logger.debug(`'${event.name}' unregistered for EventType '${event.event}'`);
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
clear() {
|
|
1731
|
+
this.events.forEach((e) => this.unregister(e.name));
|
|
1732
|
+
this.events.clear();
|
|
1733
|
+
}
|
|
1734
|
+
get(name) {
|
|
1735
|
+
return this.events.get(name);
|
|
1736
|
+
}
|
|
1737
|
+
getByTag(tag) {
|
|
1738
|
+
return Array.from(this.events.values()).filter((event) => event.metadata?.tags?.includes(tag));
|
|
1739
|
+
}
|
|
1740
|
+
getByCategory(category) {
|
|
1741
|
+
return Array.from(this.events.values()).filter((event) => event.metadata?.category?.includes(category));
|
|
1742
|
+
}
|
|
1743
|
+
getByEvent(eventType) {
|
|
1744
|
+
return Array.from(this.events.values()).filter((event) => event.event === eventType);
|
|
1745
|
+
}
|
|
1746
|
+
async executeEvents(eventType, ...args) {
|
|
1747
|
+
const events = this.getByEvent(eventType);
|
|
1748
|
+
if (!events.length) return;
|
|
1749
|
+
const sortedEvents = events.sort((a, b) => b.priority - a.priority);
|
|
1750
|
+
await Promise.all(
|
|
1751
|
+
sortedEvents.map(async (event) => {
|
|
1752
|
+
try {
|
|
1753
|
+
await event.execute?.(this.client, ...args);
|
|
1754
|
+
if (event.once) {
|
|
1755
|
+
this.unregister(event.name);
|
|
1756
|
+
}
|
|
1757
|
+
} catch (err) {
|
|
1758
|
+
this.logger.error(`'${event.name}' failed to execute`, err);
|
|
1759
|
+
}
|
|
1760
|
+
})
|
|
1761
|
+
);
|
|
1762
|
+
}
|
|
1763
|
+
/** Import event modules that end with `.event` */
|
|
1764
|
+
async importFrom(dir, replaceAll) {
|
|
1765
|
+
dir = Array.isArray(dir) ? dir : [dir];
|
|
1766
|
+
const eventModules = await Promise.all(
|
|
1767
|
+
dir.map((dir2) => importModulesFromDir(dir2, "event"))
|
|
1768
|
+
);
|
|
1769
|
+
if (replaceAll) {
|
|
1770
|
+
this.clear();
|
|
1771
|
+
}
|
|
1772
|
+
let importedEvents = 0;
|
|
1773
|
+
let ignoredEvents = 0;
|
|
1774
|
+
for (const event of eventModules.flat()) {
|
|
1775
|
+
if (!event.module.default.enabled) {
|
|
1776
|
+
ignoredEvents++;
|
|
1777
|
+
} else {
|
|
1778
|
+
importedEvents++;
|
|
1779
|
+
}
|
|
1780
|
+
this.register(event.module.default);
|
|
1781
|
+
}
|
|
1782
|
+
this.client.logger.moduleLoaded("Event Handlers", importedEvents, ignoredEvents);
|
|
1783
|
+
return this.events;
|
|
1784
|
+
}
|
|
1785
|
+
};
|
|
1786
|
+
|
|
1787
|
+
// src/utils/sendCommandErrorEmbed.ts
|
|
1788
|
+
import {
|
|
1789
|
+
ActionRowBuilder as ActionRowBuilder2,
|
|
1790
|
+
AttachmentBuilder,
|
|
1791
|
+
ButtonBuilder,
|
|
1792
|
+
ButtonStyle,
|
|
1793
|
+
ComponentType,
|
|
1794
|
+
Message as Message3
|
|
1795
|
+
} from "discord.js";
|
|
1796
|
+
|
|
1797
|
+
// src/tools/BetterEmbed.ts
|
|
1798
|
+
import {
|
|
1799
|
+
EmbedBuilder,
|
|
1800
|
+
GuildMember as GuildMember3,
|
|
1801
|
+
User as User3
|
|
1802
|
+
} from "discord.js";
|
|
1803
|
+
|
|
1804
|
+
// src/tools/dynaSend.ts
|
|
1805
|
+
import {
|
|
1806
|
+
BaseChannel,
|
|
1807
|
+
BaseInteraction as BaseInteraction2,
|
|
1808
|
+
GuildMember as GuildMember2,
|
|
1809
|
+
InteractionCallbackResponse,
|
|
1810
|
+
Message,
|
|
1811
|
+
User as User2
|
|
1812
|
+
} from "discord.js";
|
|
1813
|
+
|
|
1814
|
+
// src/tools/types.ts
|
|
1815
|
+
var SendMethod = /* @__PURE__ */ ((SendMethod2) => {
|
|
1816
|
+
SendMethod2[SendMethod2["Reply"] = 0] = "Reply";
|
|
1817
|
+
SendMethod2[SendMethod2["EditReply"] = 1] = "EditReply";
|
|
1818
|
+
SendMethod2[SendMethod2["FollowUp"] = 2] = "FollowUp";
|
|
1819
|
+
SendMethod2[SendMethod2["Channel"] = 3] = "Channel";
|
|
1820
|
+
SendMethod2[SendMethod2["MessageReply"] = 4] = "MessageReply";
|
|
1821
|
+
SendMethod2[SendMethod2["MessageEdit"] = 5] = "MessageEdit";
|
|
1822
|
+
SendMethod2[SendMethod2["User"] = 6] = "User";
|
|
1823
|
+
return SendMethod2;
|
|
1824
|
+
})(SendMethod || {});
|
|
1825
|
+
|
|
1826
|
+
// src/tools/dynaSend.ts
|
|
1827
|
+
var DynaSend = class {
|
|
1828
|
+
static forceArray(value) {
|
|
1829
|
+
return Array.isArray(value) ? value : [value];
|
|
1830
|
+
}
|
|
1831
|
+
static isInteractionCallback(obj) {
|
|
1832
|
+
return obj instanceof InteractionCallbackResponse;
|
|
1833
|
+
}
|
|
1834
|
+
static filterFlags(flags, excludeFlags) {
|
|
1835
|
+
if (!flags) return void 0;
|
|
1836
|
+
const flagArray = this.forceArray(flags);
|
|
1837
|
+
return flagArray.filter((flag) => !excludeFlags.includes(flag));
|
|
1838
|
+
}
|
|
1839
|
+
static detectSendMethod(handler) {
|
|
1840
|
+
if (handler instanceof BaseInteraction2) {
|
|
1841
|
+
return handler.replied || handler.deferred ? 1 /* EditReply */ : 0 /* Reply */;
|
|
1842
|
+
}
|
|
1843
|
+
if (handler instanceof BaseChannel) return 3 /* Channel */;
|
|
1844
|
+
if (handler instanceof Message) return 4 /* MessageReply */;
|
|
1845
|
+
if (handler instanceof GuildMember2 || handler instanceof User2) return 6 /* User */;
|
|
1846
|
+
throw new Error("[DynaSend] Unable to determine send method for handler type");
|
|
1847
|
+
}
|
|
1848
|
+
static validateSendMethod(handler, method) {
|
|
1849
|
+
const interactionMethods = [0 /* Reply */, 1 /* EditReply */, 2 /* FollowUp */];
|
|
1850
|
+
if (interactionMethods.includes(method) && !(handler instanceof BaseInteraction2)) {
|
|
1851
|
+
throw new TypeError(`[DynaSend] SendMethod '${SendMethod[method]}' requires BaseInteraction handler`);
|
|
1852
|
+
}
|
|
1853
|
+
if (method === 3 /* Channel */ && !(handler instanceof BaseChannel)) {
|
|
1854
|
+
throw new TypeError(`[DynaSend] SendMethod '${SendMethod[method]}' requires BaseChannel handler`);
|
|
1855
|
+
}
|
|
1856
|
+
if ([4 /* MessageReply */, 5 /* MessageEdit */].includes(method) && !(handler instanceof Message)) {
|
|
1857
|
+
throw new TypeError(`[DynaSend] SendMethod '${SendMethod[method]}' requires Message handler`);
|
|
1858
|
+
}
|
|
1859
|
+
if (method === 6 /* User */ && !(handler instanceof GuildMember2 || handler instanceof User2)) {
|
|
1860
|
+
throw new TypeError(`[DynaSend] SendMethod '${SendMethod[method]}' requires User or GuildMember handler`);
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1863
|
+
static createMessageData(options, method) {
|
|
1864
|
+
const baseData = {
|
|
1865
|
+
content: options.content,
|
|
1866
|
+
embeds: options.embeds,
|
|
1867
|
+
components: options.components,
|
|
1868
|
+
files: options.files,
|
|
1869
|
+
allowedMentions: options.allowedMentions,
|
|
1870
|
+
tts: options.tts
|
|
1871
|
+
};
|
|
1872
|
+
switch (method) {
|
|
1873
|
+
case 0 /* Reply */:
|
|
1874
|
+
return {
|
|
1875
|
+
...baseData,
|
|
1876
|
+
flags: options.flags,
|
|
1877
|
+
withResponse: options.withResponse,
|
|
1878
|
+
poll: options.poll
|
|
1879
|
+
};
|
|
1880
|
+
case 1 /* EditReply */:
|
|
1881
|
+
return {
|
|
1882
|
+
...baseData,
|
|
1883
|
+
flags: this.filterFlags(options.flags, ["Ephemeral", "SuppressNotifications"]),
|
|
1884
|
+
withResponse: options.withResponse,
|
|
1885
|
+
poll: options.poll
|
|
1886
|
+
};
|
|
1887
|
+
case 2 /* FollowUp */:
|
|
1888
|
+
return {
|
|
1889
|
+
...baseData,
|
|
1890
|
+
flags: options.flags,
|
|
1891
|
+
withResponse: options.withResponse,
|
|
1892
|
+
poll: options.poll
|
|
1893
|
+
};
|
|
1894
|
+
case 3 /* Channel */:
|
|
1895
|
+
return {
|
|
1896
|
+
...baseData,
|
|
1897
|
+
flags: this.filterFlags(options.flags, ["Ephemeral"]),
|
|
1898
|
+
poll: options.poll,
|
|
1899
|
+
stickers: options.stickers,
|
|
1900
|
+
reply: options.reply
|
|
1901
|
+
};
|
|
1902
|
+
case 4 /* MessageReply */:
|
|
1903
|
+
return {
|
|
1904
|
+
...baseData,
|
|
1905
|
+
flags: this.filterFlags(options.flags, ["Ephemeral"]),
|
|
1906
|
+
poll: options.poll,
|
|
1907
|
+
stickers: options.stickers
|
|
1908
|
+
};
|
|
1909
|
+
case 5 /* MessageEdit */:
|
|
1910
|
+
return {
|
|
1911
|
+
...baseData,
|
|
1912
|
+
flags: this.filterFlags(options.flags, ["Ephemeral", "SuppressNotifications"])
|
|
1913
|
+
};
|
|
1914
|
+
case 6 /* User */:
|
|
1915
|
+
return {
|
|
1916
|
+
...baseData,
|
|
1917
|
+
flags: this.filterFlags(options.flags, ["Ephemeral"]),
|
|
1918
|
+
poll: options.poll,
|
|
1919
|
+
forward: options.forward,
|
|
1920
|
+
stickers: options.stickers
|
|
1921
|
+
};
|
|
1922
|
+
default:
|
|
1923
|
+
return baseData;
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
static async executeSend(handler, method, data) {
|
|
1927
|
+
try {
|
|
1928
|
+
switch (method) {
|
|
1929
|
+
case 0 /* Reply */: {
|
|
1930
|
+
const response = await handler.reply(data);
|
|
1931
|
+
return this.isInteractionCallback(response) ? response.resource?.message ?? null : null;
|
|
1932
|
+
}
|
|
1933
|
+
case 1 /* EditReply */:
|
|
1934
|
+
return await handler.editReply(data);
|
|
1935
|
+
case 2 /* FollowUp */:
|
|
1936
|
+
return await handler.followUp(data);
|
|
1937
|
+
case 3 /* Channel */:
|
|
1938
|
+
return await handler.send(data);
|
|
1939
|
+
case 4 /* MessageReply */:
|
|
1940
|
+
return await handler.reply(data);
|
|
1941
|
+
case 5 /* MessageEdit */: {
|
|
1942
|
+
const message = handler;
|
|
1943
|
+
if (!message.editable) {
|
|
1944
|
+
console.warn("[DynaSend] Message is not editable");
|
|
1945
|
+
return null;
|
|
1946
|
+
}
|
|
1947
|
+
return await message.edit(data);
|
|
1948
|
+
}
|
|
1949
|
+
case 6 /* User */:
|
|
1950
|
+
return await handler.send(data);
|
|
1951
|
+
default:
|
|
1952
|
+
throw new Error(`[DynaSend] Unknown send method '${method}'`);
|
|
1953
|
+
}
|
|
1954
|
+
} catch (error) {
|
|
1955
|
+
console.error(`[DynaSend] Error with method '${SendMethod[method]}':`, error);
|
|
1956
|
+
return null;
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
static scheduleDelete(message, delay) {
|
|
1960
|
+
if (delay < 1e3) {
|
|
1961
|
+
console.warn(`[DynaSend] Delete delay is less than 1 second (${delay}ms). Is this intentional?`);
|
|
1962
|
+
}
|
|
1963
|
+
setTimeout(async () => {
|
|
1964
|
+
try {
|
|
1965
|
+
if (message.deletable) {
|
|
1966
|
+
await message.delete();
|
|
1967
|
+
}
|
|
1968
|
+
} catch (error) {
|
|
1969
|
+
console.error("[DynaSend] Error deleting message:", error);
|
|
1970
|
+
}
|
|
1971
|
+
}, delay);
|
|
1972
|
+
}
|
|
1973
|
+
static async send(handler, options) {
|
|
1974
|
+
const sendMethod = options.sendMethod ?? this.detectSendMethod(handler);
|
|
1975
|
+
this.validateSendMethod(handler, sendMethod);
|
|
1976
|
+
const messageData = this.createMessageData(options, sendMethod);
|
|
1977
|
+
const message = await this.executeSend(handler, sendMethod, messageData);
|
|
1978
|
+
if (options.deleteAfter && message) {
|
|
1979
|
+
this.scheduleDelete(message, options.deleteAfter);
|
|
1980
|
+
}
|
|
1981
|
+
return message;
|
|
1982
|
+
}
|
|
1983
|
+
};
|
|
1984
|
+
async function dynaSend(handler, options) {
|
|
1985
|
+
return DynaSend.send(handler, options);
|
|
1986
|
+
}
|
|
1987
|
+
|
|
1988
|
+
// src/tools/BetterEmbed.ts
|
|
1989
|
+
var BetterEmbed = class _BetterEmbed {
|
|
1990
|
+
embed = new EmbedBuilder();
|
|
1991
|
+
data;
|
|
1992
|
+
config;
|
|
1993
|
+
/** A powerful wrapper for `EmbedBuilder` that introduces useful features
|
|
1994
|
+
*
|
|
1995
|
+
* Auto-shorthand context formatting (_ACF_) is enabled by default
|
|
1996
|
+
*
|
|
1997
|
+
* All functions utilize _ACF_ unless `BetterEmbed.acf` is set to `false`
|
|
1998
|
+
*
|
|
1999
|
+
* ___Use a blackslash___ `\` ___to escape any context___
|
|
2000
|
+
*
|
|
2001
|
+
* \- - - Author Context - - -
|
|
2002
|
+
* - __`$USER`__: _author's mention (@xsqu1znt)_
|
|
2003
|
+
* - __`$USER_NAME`__: _author's username_
|
|
2004
|
+
* - __`$DISPLAY_NAME`__: _author's display name (requires `GuildMember` context)_
|
|
2005
|
+
* - __`$USER_AVATAR`__: _author's avatar_
|
|
2006
|
+
*
|
|
2007
|
+
* \- - - Client Context - - -
|
|
2008
|
+
*
|
|
2009
|
+
* - __`$BOT_AVATAR`__: _bot's avatar_
|
|
2010
|
+
*
|
|
2011
|
+
* \- - - Shorthand Context - - -
|
|
2012
|
+
* - __`$YEAR`__: _YYYY_
|
|
2013
|
+
* - __`$MONTH`__: _MM_
|
|
2014
|
+
* - __`$DAY`__: _DD_
|
|
2015
|
+
* - __`$year`__: _YY_
|
|
2016
|
+
* - __`$month`__: _M or MM_
|
|
2017
|
+
* - __`$day`__: _D or DD_ */
|
|
2018
|
+
constructor(data = {}) {
|
|
2019
|
+
this.config = data.config || globalVimcordToolsConfig;
|
|
2020
|
+
this.data = {
|
|
2021
|
+
context: data.context || null,
|
|
2022
|
+
author: data.author || null,
|
|
2023
|
+
title: data.title || null,
|
|
2024
|
+
thumbnailUrl: data.thumbnailUrl || null,
|
|
2025
|
+
description: data.description || null,
|
|
2026
|
+
imageUrl: data.imageUrl || null,
|
|
2027
|
+
footer: data.footer || null,
|
|
2028
|
+
fields: data.fields || [],
|
|
2029
|
+
color: data.color ?? (this.config.devMode ? this.config.embedColorDev : this.config.embedColor),
|
|
2030
|
+
timestamp: data.timestamp || null,
|
|
2031
|
+
acf: data.acf ?? true
|
|
2032
|
+
};
|
|
2033
|
+
this.build();
|
|
2034
|
+
}
|
|
2035
|
+
build() {
|
|
2036
|
+
this.normalizeData();
|
|
2037
|
+
this.applyContextFormatting();
|
|
2038
|
+
this.configureEmbed();
|
|
2039
|
+
}
|
|
2040
|
+
normalizeData() {
|
|
2041
|
+
if (typeof this.data.author === "string") {
|
|
2042
|
+
this.data.author = { text: this.data.author };
|
|
2043
|
+
}
|
|
2044
|
+
if (typeof this.data.title === "string") {
|
|
2045
|
+
this.data.title = { text: this.data.title };
|
|
2046
|
+
}
|
|
2047
|
+
if (typeof this.data.footer === "string") {
|
|
2048
|
+
this.data.footer = { text: this.data.footer };
|
|
2049
|
+
}
|
|
2050
|
+
if (this.data.timestamp === true) {
|
|
2051
|
+
this.data.timestamp = Date.now();
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
2054
|
+
getContextUser() {
|
|
2055
|
+
const context = this.data.context;
|
|
2056
|
+
if (!context) return null;
|
|
2057
|
+
return context.user || context.interaction?.member || context.interaction?.user || context.message?.member || context.message?.author || null;
|
|
2058
|
+
}
|
|
2059
|
+
getContextClient() {
|
|
2060
|
+
const context = this.data.context;
|
|
2061
|
+
if (!context) return null;
|
|
2062
|
+
return context.client || context.interaction?.client || context.message?.client || null;
|
|
2063
|
+
}
|
|
2064
|
+
applyContextFormatting(str) {
|
|
2065
|
+
if (!this.data.acf) return;
|
|
2066
|
+
const user = this.getContextUser();
|
|
2067
|
+
const guildMember = user instanceof GuildMember3 ? user : null;
|
|
2068
|
+
const actualUser = guildMember?.user || (user instanceof User3 ? user : null);
|
|
2069
|
+
const client = this.getContextClient();
|
|
2070
|
+
const formatString = (str2) => {
|
|
2071
|
+
if (!str2 || !str2.includes("$")) return str2;
|
|
2072
|
+
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>");
|
|
2073
|
+
};
|
|
2074
|
+
if (str) {
|
|
2075
|
+
return formatString(str);
|
|
2076
|
+
}
|
|
2077
|
+
if (this.data.author && typeof this.data.author === "object") {
|
|
2078
|
+
this.data.author.text = formatString(this.data.author.text);
|
|
2079
|
+
if (this.data.author.icon === true && actualUser) {
|
|
2080
|
+
this.data.author.icon = actualUser.avatarURL();
|
|
2081
|
+
} else if (typeof this.data.author.icon === "string") {
|
|
2082
|
+
this.data.author.icon = formatString(this.data.author.icon);
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
if (this.data.title && typeof this.data.title === "object") {
|
|
2086
|
+
this.data.title.text = formatString(this.data.title.text);
|
|
2087
|
+
}
|
|
2088
|
+
if (this.data.description) {
|
|
2089
|
+
this.data.description = formatString(
|
|
2090
|
+
Array.isArray(this.data.description) ? this.data.description.join("\n") : this.data.description
|
|
2091
|
+
);
|
|
2092
|
+
}
|
|
2093
|
+
if (this.data.footer && typeof this.data.footer === "object") {
|
|
2094
|
+
this.data.footer.text = formatString(this.data.footer.text);
|
|
2095
|
+
}
|
|
2096
|
+
if (this.data.thumbnailUrl) {
|
|
2097
|
+
this.data.thumbnailUrl = formatString(this.data.thumbnailUrl);
|
|
2098
|
+
}
|
|
2099
|
+
if (this.data.imageUrl) {
|
|
2100
|
+
this.data.imageUrl = formatString(this.data.imageUrl);
|
|
2101
|
+
}
|
|
2102
|
+
this.data.fields = this.data.fields.map((field) => ({
|
|
2103
|
+
...field,
|
|
2104
|
+
name: formatString(field.name),
|
|
2105
|
+
value: formatString(field.value)
|
|
2106
|
+
}));
|
|
2107
|
+
}
|
|
2108
|
+
configureEmbed() {
|
|
2109
|
+
if (this.data.author && typeof this.data.author === "object" && this.data.author.text) {
|
|
2110
|
+
try {
|
|
2111
|
+
this.embed.setAuthor({
|
|
2112
|
+
name: this.data.author.text,
|
|
2113
|
+
iconURL: typeof this.data.author.icon === "string" ? this.data.author.icon : void 0,
|
|
2114
|
+
url: this.data.author.hyperlink || void 0
|
|
2115
|
+
});
|
|
2116
|
+
} catch (error) {
|
|
2117
|
+
console.error("[BetterEmbed] Invalid author configuration:", error);
|
|
2118
|
+
}
|
|
2119
|
+
}
|
|
2120
|
+
if (this.data.title && typeof this.data.title === "object" && this.data.title.text) {
|
|
2121
|
+
try {
|
|
2122
|
+
this.embed.setTitle(this.data.title.text);
|
|
2123
|
+
if (this.data.title.hyperlink) {
|
|
2124
|
+
this.embed.setURL(this.data.title.hyperlink);
|
|
2125
|
+
}
|
|
2126
|
+
} catch (error) {
|
|
2127
|
+
console.error("[BetterEmbed] Invalid title configuration:", error);
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
if (this.data.description) {
|
|
2131
|
+
this.embed.setDescription(
|
|
2132
|
+
Array.isArray(this.data.description) ? this.data.description.join("\n") : this.data.description
|
|
2133
|
+
);
|
|
2134
|
+
}
|
|
2135
|
+
if (this.data.thumbnailUrl) {
|
|
2136
|
+
try {
|
|
2137
|
+
this.embed.setThumbnail(this.data.thumbnailUrl);
|
|
2138
|
+
} catch (error) {
|
|
2139
|
+
console.error("[BetterEmbed] Invalid thumbnail URL:", error);
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
if (this.data.imageUrl) {
|
|
2143
|
+
try {
|
|
2144
|
+
this.embed.setImage(this.data.imageUrl);
|
|
2145
|
+
} catch (error) {
|
|
2146
|
+
console.error("[BetterEmbed] Invalid image URL:", error);
|
|
2147
|
+
}
|
|
2148
|
+
}
|
|
2149
|
+
if (this.data.footer && typeof this.data.footer === "object" && this.data.footer.text) {
|
|
2150
|
+
try {
|
|
2151
|
+
this.embed.setFooter({
|
|
2152
|
+
text: this.data.footer.text,
|
|
2153
|
+
iconURL: typeof this.data.footer.icon === "string" ? this.data.footer.icon : void 0
|
|
2154
|
+
});
|
|
2155
|
+
} catch (error) {
|
|
2156
|
+
console.error("[BetterEmbed] Invalid footer configuration:", error);
|
|
2157
|
+
}
|
|
2158
|
+
}
|
|
2159
|
+
if (this.data.color) {
|
|
2160
|
+
try {
|
|
2161
|
+
const color = Array.isArray(this.data.color) ? this.data.color[Math.floor(Math.random() * this.data.color.length)] ?? null : this.data.color;
|
|
2162
|
+
this.embed.setColor(color);
|
|
2163
|
+
} catch (error) {
|
|
2164
|
+
console.error("[BetterEmbed] Invalid color:", error);
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2167
|
+
if (this.data.timestamp && this.data.timestamp !== true) {
|
|
2168
|
+
try {
|
|
2169
|
+
this.embed.setTimestamp(this.data.timestamp);
|
|
2170
|
+
} catch (error) {
|
|
2171
|
+
console.error("[BetterEmbed] Invalid timestamp:", error);
|
|
2172
|
+
}
|
|
2173
|
+
}
|
|
2174
|
+
if (this.data.fields.length > 0) {
|
|
2175
|
+
const validFields = this.data.fields.slice(0, 25);
|
|
2176
|
+
if (this.data.fields.length > 25) {
|
|
2177
|
+
console.warn("[BetterEmbed] Only first 25 fields will be used (Discord limit)");
|
|
2178
|
+
}
|
|
2179
|
+
this.embed.setFields(validFields);
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
setAuthor(author) {
|
|
2183
|
+
this.data.author = author;
|
|
2184
|
+
this.build();
|
|
2185
|
+
return this;
|
|
2186
|
+
}
|
|
2187
|
+
setTitle(title) {
|
|
2188
|
+
this.data.title = title;
|
|
2189
|
+
this.build();
|
|
2190
|
+
return this;
|
|
2191
|
+
}
|
|
2192
|
+
setDescription(description) {
|
|
2193
|
+
this.data.description = description;
|
|
2194
|
+
this.build();
|
|
2195
|
+
return this;
|
|
2196
|
+
}
|
|
2197
|
+
setThumbnail(url) {
|
|
2198
|
+
this.data.thumbnailUrl = url;
|
|
2199
|
+
this.build();
|
|
2200
|
+
return this;
|
|
2201
|
+
}
|
|
2202
|
+
setImage(url) {
|
|
2203
|
+
this.data.imageUrl = url;
|
|
2204
|
+
this.build();
|
|
2205
|
+
return this;
|
|
2206
|
+
}
|
|
2207
|
+
setFooter(footer) {
|
|
2208
|
+
this.data.footer = footer;
|
|
2209
|
+
this.build();
|
|
2210
|
+
return this;
|
|
2211
|
+
}
|
|
2212
|
+
setColor(color) {
|
|
2213
|
+
this.data.color = color;
|
|
2214
|
+
this.build();
|
|
2215
|
+
return this;
|
|
2216
|
+
}
|
|
2217
|
+
setTimestamp(timestamp) {
|
|
2218
|
+
this.data.timestamp = timestamp;
|
|
2219
|
+
this.build();
|
|
2220
|
+
return this;
|
|
2221
|
+
}
|
|
2222
|
+
addFields(fields) {
|
|
2223
|
+
this.data.fields = [...this.data.fields, ...fields];
|
|
2224
|
+
this.build();
|
|
2225
|
+
return this;
|
|
2226
|
+
}
|
|
2227
|
+
setFields(fields) {
|
|
2228
|
+
this.data.fields = fields;
|
|
2229
|
+
this.build();
|
|
2230
|
+
return this;
|
|
2231
|
+
}
|
|
2232
|
+
spliceFields(index, deleteCount, ...fields) {
|
|
2233
|
+
this.data.fields.splice(index, deleteCount, ...fields);
|
|
2234
|
+
this.build();
|
|
2235
|
+
return this;
|
|
2236
|
+
}
|
|
2237
|
+
clone(overrides = {}) {
|
|
2238
|
+
return new _BetterEmbed({ ...this.data, ...overrides });
|
|
2239
|
+
}
|
|
2240
|
+
toJSON() {
|
|
2241
|
+
return this.embed.toJSON();
|
|
2242
|
+
}
|
|
2243
|
+
async send(handler, options = {}, overrides) {
|
|
2244
|
+
this.build();
|
|
2245
|
+
if (options.content && this.data.acf) {
|
|
2246
|
+
options.content = this.applyContextFormatting(options.content);
|
|
2247
|
+
}
|
|
2248
|
+
return await dynaSend(handler, {
|
|
2249
|
+
...options,
|
|
2250
|
+
embeds: [
|
|
2251
|
+
overrides ? this.clone(overrides) : this,
|
|
2252
|
+
...Array.isArray(options?.embeds) ? options?.embeds : options?.embeds ? [options?.embeds] : []
|
|
2253
|
+
]
|
|
2254
|
+
});
|
|
2255
|
+
}
|
|
2256
|
+
};
|
|
2257
|
+
|
|
2258
|
+
// src/utils/sendCommandErrorEmbed.ts
|
|
2259
|
+
async function sendCommandErrorEmbed(client, error, guild, messageOrInteraction) {
|
|
2260
|
+
if (!client.features.enableCommandErrorMessage) return null;
|
|
2261
|
+
const config = typeof client.features.enableCommandErrorMessage !== "boolean" ? client.features.enableCommandErrorMessage : void 0;
|
|
2262
|
+
const buttons = {
|
|
2263
|
+
supportServer: new ButtonBuilder({
|
|
2264
|
+
url: config?.inviteUrl || client.config.staff.guild.inviteUrl || "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
|
|
2265
|
+
// may or may not be a rickroll
|
|
2266
|
+
label: config?.inviteButtonLabel || "Support Support",
|
|
2267
|
+
style: ButtonStyle.Link
|
|
2268
|
+
}),
|
|
2269
|
+
details: new ButtonBuilder({
|
|
2270
|
+
customId: "btn_details",
|
|
2271
|
+
label: config?.detailButtonLabel || "Details",
|
|
2272
|
+
style: ButtonStyle.Secondary
|
|
2273
|
+
})
|
|
2274
|
+
};
|
|
2275
|
+
const actionRow = new ActionRowBuilder2({
|
|
2276
|
+
components: config?.inviteUrl && guild?.id !== (config.inviteUrl || client.config.staff.guild.id) ? [buttons.supportServer, buttons.details] : [buttons.details]
|
|
2277
|
+
});
|
|
2278
|
+
const embed_error = config?.embed?.(new BetterEmbed(), error, guild) || new BetterEmbed({
|
|
2279
|
+
color: "Red",
|
|
2280
|
+
title: "Something went wrong",
|
|
2281
|
+
description: "If you keep encountering this error, please report it"
|
|
2282
|
+
});
|
|
2283
|
+
const msg = await embed_error.send(messageOrInteraction, {
|
|
2284
|
+
components: [actionRow],
|
|
2285
|
+
flags: config?.ephemeral ? "Ephemeral" : void 0,
|
|
2286
|
+
deleteAfter: config?.deleteAfter
|
|
2287
|
+
});
|
|
2288
|
+
if (!msg) return null;
|
|
2289
|
+
const collector = msg.createMessageComponentCollector({
|
|
2290
|
+
componentType: ComponentType.Button,
|
|
2291
|
+
idle: config?.detailButtonIdleTimeout ?? 3e4,
|
|
2292
|
+
filter: (i) => i.customId === "btn_details"
|
|
2293
|
+
});
|
|
2294
|
+
collector.on("collect", (i) => {
|
|
2295
|
+
const attachment = new AttachmentBuilder(Buffer.from(`${error.message}
|
|
2296
|
+
|
|
2297
|
+
${error.stack}`), {
|
|
2298
|
+
name: "error.txt"
|
|
2299
|
+
});
|
|
2300
|
+
i.reply({ files: [attachment], flags: "Ephemeral" });
|
|
2301
|
+
});
|
|
2302
|
+
collector.on("end", () => {
|
|
2303
|
+
buttons.details.setDisabled(true);
|
|
2304
|
+
embed_error.send(messageOrInteraction, {
|
|
2305
|
+
sendMethod: messageOrInteraction instanceof Message3 ? 5 /* MessageEdit */ : void 0,
|
|
2306
|
+
components: [actionRow]
|
|
2307
|
+
});
|
|
2308
|
+
});
|
|
2309
|
+
return msg;
|
|
2310
|
+
}
|
|
2311
|
+
|
|
2312
|
+
// src/utils/async.ts
|
|
2313
|
+
async function retryExponentialBackoff(fn, maxRetries = 3, retryDelay = 1e3) {
|
|
2314
|
+
let attempts = 0;
|
|
2315
|
+
while (true) {
|
|
2316
|
+
try {
|
|
2317
|
+
return await fn(attempts);
|
|
2318
|
+
} catch (error) {
|
|
2319
|
+
if (attempts >= maxRetries) throw error;
|
|
2320
|
+
await new Promise((resolve) => setTimeout(resolve, Math.pow(1.75, attempts) * retryDelay + Math.random() * 500));
|
|
2321
|
+
attempts++;
|
|
2322
|
+
}
|
|
2323
|
+
}
|
|
2324
|
+
}
|
|
2325
|
+
|
|
2326
|
+
// package.json
|
|
2327
|
+
var version = "1.0.0";
|
|
2328
|
+
|
|
2329
|
+
// src/client.ts
|
|
2330
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
2331
|
+
import chalk2 from "chalk";
|
|
2332
|
+
var clientInstances = [];
|
|
2333
|
+
var Vimcord = class _Vimcord extends Client2 {
|
|
2334
|
+
uuid = randomUUID3();
|
|
2335
|
+
index = clientInstances.length;
|
|
2336
|
+
clientOptions;
|
|
2337
|
+
features;
|
|
2338
|
+
config;
|
|
2339
|
+
status;
|
|
2340
|
+
events;
|
|
2341
|
+
commands;
|
|
2342
|
+
database;
|
|
2343
|
+
// Configure custom logger
|
|
2344
|
+
logger = new Logger({ prefixEmoji: "\u26A1", prefix: `vimcord (i${this.index})` }).extend({
|
|
2345
|
+
clientBanner(client) {
|
|
2346
|
+
if (client.config.app.disableBanner) return;
|
|
2347
|
+
const border = "\u2550".repeat(50);
|
|
2348
|
+
console.log(chalk2.hex(this.colors.primary)(`
|
|
2349
|
+
\u2554${border}\u2557`));
|
|
2350
|
+
console.log(
|
|
2351
|
+
chalk2.hex(this.colors.primary)("\u2551") + chalk2.bold.hex(this.colors.text)(
|
|
2352
|
+
` \u{1F680} ${client.config.app.name} v${client.config.app.appVersion}`.padEnd(
|
|
2353
|
+
50 - (client.config.app.devMode ? 12 : 0)
|
|
2354
|
+
)
|
|
2355
|
+
) + chalk2.hex(this.colors.primary)(
|
|
2356
|
+
`${client.config.app.devMode ? chalk2.hex(this.colors.warn)("devMode \u26A0\uFE0F ") : ""}\u2551`
|
|
2357
|
+
)
|
|
2358
|
+
);
|
|
2359
|
+
console.log(chalk2.hex(this.colors.primary)(`\u2551${"".padEnd(50)}\u2551`));
|
|
2360
|
+
console.log(
|
|
2361
|
+
chalk2.hex(this.colors.primary)("\u2551") + chalk2.hex(this.colors.muted)(
|
|
2362
|
+
` # Powered by Vimcord v${version}`.padEnd(50 - 3 - `${client.index}`.length)
|
|
2363
|
+
) + chalk2.hex(this.colors.primary)(`${chalk2.hex(this.colors.muted)(`i${client.index}`)} \u2551`)
|
|
2364
|
+
);
|
|
2365
|
+
console.log(chalk2.hex(this.colors.primary)(`\u255A${border}\u255D
|
|
2366
|
+
`));
|
|
2367
|
+
},
|
|
2368
|
+
clientReady(clientTag, guildCount) {
|
|
2369
|
+
console.log(
|
|
2370
|
+
this.formatTimestamp(),
|
|
2371
|
+
this.formatPrefix(),
|
|
2372
|
+
chalk2.hex(this.colors.success)("\u{1F916} READY"),
|
|
2373
|
+
chalk2.white(`Connected as ${chalk2.bold.hex(this.colors.primary)(clientTag)}`),
|
|
2374
|
+
chalk2.hex(this.colors.muted)(`\u2022 ${guildCount} guilds`)
|
|
2375
|
+
);
|
|
2376
|
+
},
|
|
2377
|
+
moduleLoaded(moduleName, count, ignoredCount) {
|
|
2378
|
+
const countText = count ? chalk2.hex(this.colors.muted)(`(${count} items)`) : "";
|
|
2379
|
+
console.log(
|
|
2380
|
+
this.formatTimestamp(),
|
|
2381
|
+
this.formatPrefix(),
|
|
2382
|
+
chalk2.hex("#9B59B6")("\u{1F4E6} MODULE"),
|
|
2383
|
+
chalk2.hex(this.colors.warn)(`${moduleName} loaded`),
|
|
2384
|
+
ignoredCount ? chalk2.hex(this.colors.muted)(`(${ignoredCount} ignored)`) : "",
|
|
2385
|
+
countText
|
|
2386
|
+
);
|
|
2387
|
+
},
|
|
2388
|
+
commandExecuted(commandName, username, guildName) {
|
|
2389
|
+
const location = guildName ? `in ${chalk2.hex(this.colors.muted)(guildName)}` : "in DMs";
|
|
2390
|
+
console.log(
|
|
2391
|
+
this.formatTimestamp(),
|
|
2392
|
+
this.formatPrefix(),
|
|
2393
|
+
chalk2.hex("#87CEEB")("\u{1F4DD} COMMAND"),
|
|
2394
|
+
chalk2.hex(this.colors.warn)(`/${commandName}`),
|
|
2395
|
+
chalk2.white(`used by ${chalk2.bold(username)}`),
|
|
2396
|
+
chalk2.hex(this.colors.muted)(location)
|
|
2397
|
+
);
|
|
2398
|
+
},
|
|
2399
|
+
database(action, details) {
|
|
2400
|
+
console.log(
|
|
2401
|
+
this.formatTimestamp(),
|
|
2402
|
+
this.formatPrefix(),
|
|
2403
|
+
chalk2.hex("#FF6B9D")("\u{1F5C4}\uFE0F DATABASE"),
|
|
2404
|
+
chalk2.white(action),
|
|
2405
|
+
details ? chalk2.hex(this.colors.muted)(details) : ""
|
|
2406
|
+
);
|
|
2407
|
+
}
|
|
2408
|
+
});
|
|
2409
|
+
clientStartingPromise = null;
|
|
2410
|
+
constructor(options, features = {}, config = {}) {
|
|
2411
|
+
super(options);
|
|
2412
|
+
this.clientOptions = options;
|
|
2413
|
+
this.features = features;
|
|
2414
|
+
this.config = {
|
|
2415
|
+
app: createVimcordAppConfig(config.app),
|
|
2416
|
+
staff: createVimcordStaffConfig(config.staff),
|
|
2417
|
+
slashCommands: createVimcordSlashCommandConfig(config.slashCommands),
|
|
2418
|
+
prefixCommands: createVimcordPrefixCommandConfig(config.prefixCommands),
|
|
2419
|
+
contextCommands: createVimcordContextCommandConfig(config.contextCommands)
|
|
2420
|
+
};
|
|
2421
|
+
this.status = new VimcordStatusManager(this);
|
|
2422
|
+
this.events = new VimcordEventManager(this);
|
|
2423
|
+
this.commands = new VimcordCommandManager(this);
|
|
2424
|
+
if (this.features.useEnv) {
|
|
2425
|
+
if (typeof this.features.useEnv === "object") {
|
|
2426
|
+
dotEnv.config({ quiet: true, ...this.features.useEnv });
|
|
2427
|
+
} else {
|
|
2428
|
+
dotEnv.config({ quiet: true });
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2431
|
+
if (this.features.useGlobalErrorHandlers) {
|
|
2432
|
+
process.on("uncaughtException", (err) => this.logger.error("Uncaught Exception", err));
|
|
2433
|
+
process.on("unhandledRejection", (err) => this.logger.error("Unhandled Rejection", err));
|
|
2434
|
+
process.on("exit", (code) => this.logger.debug(`Process exited with code ${code}`));
|
|
2435
|
+
this.on("error", (err) => this.logger.error("Client Error", err));
|
|
2436
|
+
this.on("shardError", (err) => this.logger.error("Client Shard Error", err));
|
|
2437
|
+
}
|
|
2438
|
+
this.logger.clientBanner(this);
|
|
2439
|
+
this.once("clientReady", (client) => {
|
|
2440
|
+
this.logger.clientReady(client.user.tag, client.guilds.cache.size);
|
|
2441
|
+
});
|
|
2442
|
+
clientInstances.push(this);
|
|
2443
|
+
}
|
|
2444
|
+
/** Returns the options, features, and config of this client */
|
|
2445
|
+
toJSON() {
|
|
2446
|
+
return {
|
|
2447
|
+
options: this.clientOptions,
|
|
2448
|
+
features: this.features,
|
|
2449
|
+
config: this.config
|
|
2450
|
+
};
|
|
2451
|
+
}
|
|
2452
|
+
/** Make a clone of this client */
|
|
2453
|
+
clone() {
|
|
2454
|
+
const { options, features, config } = this.toJSON();
|
|
2455
|
+
return new _Vimcord(options, features, config);
|
|
2456
|
+
}
|
|
2457
|
+
configureApp(options = {}) {
|
|
2458
|
+
this.config.app = createVimcordAppConfig(options);
|
|
2459
|
+
if (this.features.hookToolsDevMode) {
|
|
2460
|
+
globalVimcordToolsConfig.devMode = this.config.app.devMode;
|
|
2461
|
+
}
|
|
2462
|
+
return this;
|
|
2463
|
+
}
|
|
2464
|
+
configureStaff(options = {}) {
|
|
2465
|
+
this.config.staff = createVimcordStaffConfig(options);
|
|
2466
|
+
return this;
|
|
2467
|
+
}
|
|
2468
|
+
configureSlashCommands(options) {
|
|
2469
|
+
this.config.slashCommands = createVimcordSlashCommandConfig(options);
|
|
2470
|
+
return this;
|
|
2471
|
+
}
|
|
2472
|
+
configurePrefixCommands(options) {
|
|
2473
|
+
this.config.prefixCommands = createVimcordPrefixCommandConfig(options);
|
|
2474
|
+
return this;
|
|
2475
|
+
}
|
|
2476
|
+
configureContextCommands(options) {
|
|
2477
|
+
this.config.contextCommands = createVimcordContextCommandConfig(options);
|
|
2478
|
+
return this;
|
|
2479
|
+
}
|
|
2480
|
+
async addEventModules(dir, replaceAll) {
|
|
2481
|
+
await this.events.importFrom(dir, replaceAll);
|
|
2482
|
+
return this;
|
|
2483
|
+
}
|
|
2484
|
+
async addSlashCommandModules(dir, replaceAll) {
|
|
2485
|
+
await this.commands.slash.importFrom(dir, replaceAll);
|
|
2486
|
+
return this;
|
|
2487
|
+
}
|
|
2488
|
+
async addPrefixCommandModules(dir, replaceAll) {
|
|
2489
|
+
await this.commands.prefix.importFrom(dir, replaceAll);
|
|
2490
|
+
return this;
|
|
2491
|
+
}
|
|
2492
|
+
async addContextCommandModules(dir, replaceAll) {
|
|
2493
|
+
await this.commands.context.importFrom(dir, replaceAll);
|
|
2494
|
+
return this;
|
|
2495
|
+
}
|
|
2496
|
+
async useDatabase(database) {
|
|
2497
|
+
this.database = database;
|
|
2498
|
+
this.logger.database("Using", database.name);
|
|
2499
|
+
return this.database.connect();
|
|
2500
|
+
}
|
|
2501
|
+
async whenReady() {
|
|
2502
|
+
if (this.isReady()) return this;
|
|
2503
|
+
return new Promise((resolve, reject) => {
|
|
2504
|
+
const timeout = setTimeout(() => reject(new Error("Client is not ready")), 45e3);
|
|
2505
|
+
this.once("clientReady", () => {
|
|
2506
|
+
clearTimeout(timeout);
|
|
2507
|
+
resolve(this);
|
|
2508
|
+
});
|
|
2509
|
+
});
|
|
2510
|
+
}
|
|
2511
|
+
async build() {
|
|
2512
|
+
this.configureApp(this.config.app);
|
|
2513
|
+
this.configureStaff(this.config.staff);
|
|
2514
|
+
this.configureSlashCommands(this.config.slashCommands);
|
|
2515
|
+
this.configurePrefixCommands(this.config.prefixCommands);
|
|
2516
|
+
this.configureContextCommands(this.config.contextCommands);
|
|
2517
|
+
if (this.features.importModules) {
|
|
2518
|
+
const importModules = this.features.importModules;
|
|
2519
|
+
await Promise.all([
|
|
2520
|
+
importModules.events && this.addEventModules(importModules.events),
|
|
2521
|
+
importModules.slashCommands && this.addSlashCommandModules(importModules.slashCommands),
|
|
2522
|
+
importModules.prefixCommands && this.addPrefixCommandModules(importModules.prefixCommands),
|
|
2523
|
+
importModules.contextCommands && this.addContextCommandModules(importModules.contextCommands)
|
|
2524
|
+
]);
|
|
2525
|
+
}
|
|
2526
|
+
if (this.features.useDefaultSlashCommandHandler) {
|
|
2527
|
+
this.events.register(defaultSlashCommandHandler);
|
|
2528
|
+
}
|
|
2529
|
+
if (this.features.useDefaultPrefixCommandHandler) {
|
|
2530
|
+
this.events.register(defaultPrefixCommandHandler);
|
|
2531
|
+
}
|
|
2532
|
+
if (this.features.useDefaultContextCommandHandler) {
|
|
2533
|
+
this.events.register(defaultContextCommandHandler);
|
|
2534
|
+
}
|
|
2535
|
+
return this;
|
|
2536
|
+
}
|
|
2537
|
+
async start(tokenOrPreHook, preHook) {
|
|
2538
|
+
if (this.clientStartingPromise) return this.clientStartingPromise;
|
|
2539
|
+
const main = async () => {
|
|
2540
|
+
let token = typeof tokenOrPreHook === "string" ? tokenOrPreHook : void 0;
|
|
2541
|
+
token ??= this.config.app.devMode ? process.env.TOKEN_DEV : process.env.TOKEN;
|
|
2542
|
+
if (!token) {
|
|
2543
|
+
throw new Error(
|
|
2544
|
+
`TOKEN Missing: ${this.config.app.devMode ? "devMode is enabled, but TOKEN_DEV is not set" : "TOKEN not set"}`
|
|
2545
|
+
);
|
|
2546
|
+
}
|
|
2547
|
+
await this.build();
|
|
2548
|
+
try {
|
|
2549
|
+
if (typeof tokenOrPreHook === "function") {
|
|
2550
|
+
await tokenOrPreHook(this);
|
|
2551
|
+
} else {
|
|
2552
|
+
await preHook?.(this);
|
|
2553
|
+
}
|
|
2554
|
+
const stopLoader = this.logger.loader("Connecting to Discord...");
|
|
2555
|
+
const loginResult = await retryExponentialBackoff(
|
|
2556
|
+
() => super.login(token),
|
|
2557
|
+
this.features.loginAttempts ?? 3,
|
|
2558
|
+
1e3
|
|
2559
|
+
);
|
|
2560
|
+
stopLoader("Connected to Discord ");
|
|
2561
|
+
this.config.app.verbose && this.logger.debug("\u23F3 Waiting for ready...");
|
|
2562
|
+
return loginResult;
|
|
2563
|
+
} catch (err) {
|
|
2564
|
+
this.logger.error(
|
|
2565
|
+
`Failed to log into Discord after ${this.features.loginAttempts} attempt(s))`,
|
|
2566
|
+
err
|
|
2567
|
+
);
|
|
2568
|
+
return null;
|
|
2569
|
+
} finally {
|
|
2570
|
+
this.clientStartingPromise = null;
|
|
2571
|
+
}
|
|
2572
|
+
};
|
|
2573
|
+
this.clientStartingPromise = main();
|
|
2574
|
+
return this.clientStartingPromise;
|
|
2575
|
+
}
|
|
2576
|
+
async kill() {
|
|
2577
|
+
await super.destroy();
|
|
2578
|
+
const idx = clientInstances.indexOf(this);
|
|
2579
|
+
if (idx >= 0) clientInstances.splice(idx, 1);
|
|
2580
|
+
this.logger.debug("\u{1F6AA} Logged out of Discord");
|
|
2581
|
+
}
|
|
2582
|
+
/** Shortcut for {@link fetchUser tools.fetchUser} */
|
|
2583
|
+
async fetchUser(id) {
|
|
2584
|
+
const client = await this.whenReady();
|
|
2585
|
+
return fetchUser(client, id);
|
|
2586
|
+
}
|
|
2587
|
+
/** Shortcut for {@link fetchGuild tools.fetchGuild} */
|
|
2588
|
+
async fetchGuild(id) {
|
|
2589
|
+
const client = await this.whenReady();
|
|
2590
|
+
return fetchGuild(client, id);
|
|
2591
|
+
}
|
|
2592
|
+
};
|
|
2593
|
+
var defaultSlashCommandHandler = new EventBuilder({
|
|
2594
|
+
event: "interactionCreate",
|
|
2595
|
+
name: "SlashCommandHandler",
|
|
2596
|
+
async execute(client, interaction) {
|
|
2597
|
+
if (!interaction.isChatInputCommand()) return;
|
|
2598
|
+
const command = client.commands.slash.get(interaction.commandName);
|
|
2599
|
+
if (!command) {
|
|
2600
|
+
return interaction.reply({
|
|
2601
|
+
content: `**/\`${interaction.commandName}\`** is not a command`,
|
|
2602
|
+
flags: "Ephemeral"
|
|
2603
|
+
});
|
|
2604
|
+
}
|
|
2605
|
+
if (command.deferReply && !interaction.replied && !interaction.deferred) {
|
|
2606
|
+
await interaction.deferReply(typeof command.deferReply === "object" ? command.deferReply : void 0);
|
|
2607
|
+
}
|
|
2608
|
+
try {
|
|
2609
|
+
return command.executeCommand(client, interaction);
|
|
2610
|
+
} catch (err) {
|
|
2611
|
+
sendCommandErrorEmbed(client, err, interaction.guild, interaction);
|
|
2612
|
+
throw err;
|
|
2613
|
+
}
|
|
2614
|
+
}
|
|
2615
|
+
});
|
|
2616
|
+
var defaultPrefixCommandHandler = new EventBuilder({
|
|
2617
|
+
event: "messageCreate",
|
|
2618
|
+
name: "PrefixCommandHandler",
|
|
2619
|
+
async execute(client, message) {
|
|
2620
|
+
if (message.author.bot) return;
|
|
2621
|
+
let prefixUsed;
|
|
2622
|
+
if (!message.content.startsWith(client.config.prefixCommands.defaultPrefix)) {
|
|
2623
|
+
if (client.config.prefixCommands.allowMentionAsPrefix) {
|
|
2624
|
+
if (!message.content.startsWith(`${userMention(client.user.id)} `)) {
|
|
2625
|
+
return;
|
|
2626
|
+
} else {
|
|
2627
|
+
prefixUsed = `${userMention(client.user.id)} `;
|
|
2628
|
+
}
|
|
2629
|
+
} else {
|
|
2630
|
+
return;
|
|
2631
|
+
}
|
|
2632
|
+
} else {
|
|
2633
|
+
prefixUsed = client.config.prefixCommands.defaultPrefix;
|
|
2634
|
+
}
|
|
2635
|
+
message.content = message.content.slice(prefixUsed.length);
|
|
2636
|
+
let commandName = message.content.split(" ")[0];
|
|
2637
|
+
if (!commandName) {
|
|
2638
|
+
return;
|
|
2639
|
+
}
|
|
2640
|
+
const command = client.commands.prefix.get(commandName) || client.commands.prefix.getByAlias(commandName);
|
|
2641
|
+
if (!command) {
|
|
2642
|
+
return;
|
|
2643
|
+
}
|
|
2644
|
+
message.content = message.content.slice(commandName.length + 1);
|
|
2645
|
+
try {
|
|
2646
|
+
return command.executeCommand(client, message);
|
|
2647
|
+
} catch (err) {
|
|
2648
|
+
sendCommandErrorEmbed(client, err, message.guild, message);
|
|
2649
|
+
throw err;
|
|
2650
|
+
}
|
|
2651
|
+
}
|
|
2652
|
+
});
|
|
2653
|
+
var defaultContextCommandHandler = new EventBuilder({
|
|
2654
|
+
event: "interactionCreate",
|
|
2655
|
+
name: "ContextCommandHandler",
|
|
2656
|
+
execute(client, interaction) {
|
|
2657
|
+
if (!interaction.isContextMenuCommand()) return;
|
|
2658
|
+
interaction.reply({ content: "This handler is not yet implemented" });
|
|
2659
|
+
}
|
|
2660
|
+
});
|
|
2661
|
+
|
|
2662
|
+
// src/modules/db/mongo/mongo.ts
|
|
2663
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
2664
|
+
import EventEmitter2 from "events";
|
|
2665
|
+
import mongoose from "mongoose";
|
|
2666
|
+
try {
|
|
2667
|
+
import("mongoose");
|
|
2668
|
+
} catch {
|
|
2669
|
+
throw new Error("MongoDatabase requires the mongoose package, install it with `npm install mongoose`");
|
|
2670
|
+
}
|
|
2671
|
+
var globalInstanceEmitter = new EventEmitter2();
|
|
2672
|
+
var instances = [];
|
|
2673
|
+
async function useMongoDatabase(index) {
|
|
2674
|
+
const instance = instances.at(index ?? 0);
|
|
2675
|
+
if (!instance) {
|
|
2676
|
+
return new Promise((resolve, reject) => {
|
|
2677
|
+
const timeout = setTimeout(() => reject("useMongoDatabase timed out"), 45e3);
|
|
2678
|
+
globalInstanceEmitter.once("connected", (mdb) => {
|
|
2679
|
+
clearTimeout(timeout);
|
|
2680
|
+
resolve(mdb);
|
|
2681
|
+
});
|
|
2682
|
+
});
|
|
2683
|
+
}
|
|
2684
|
+
return instance;
|
|
2685
|
+
}
|
|
2686
|
+
var MongoDatabase = class {
|
|
2687
|
+
constructor(client, options) {
|
|
2688
|
+
this.client = client;
|
|
2689
|
+
this.mongoose = new mongoose.Mongoose(options);
|
|
2690
|
+
this.index = instances.length - 1;
|
|
2691
|
+
instances.push(this);
|
|
2692
|
+
globalInstanceEmitter.emit("created", this);
|
|
2693
|
+
}
|
|
2694
|
+
name = "MongoDatabase";
|
|
2695
|
+
uuid = randomUUID4();
|
|
2696
|
+
index;
|
|
2697
|
+
uri;
|
|
2698
|
+
mongoose;
|
|
2699
|
+
eventEmitter = new EventEmitter2();
|
|
2700
|
+
isReady = false;
|
|
2701
|
+
isConnecting = false;
|
|
2702
|
+
async waitForReady() {
|
|
2703
|
+
if (!this.isReady && this.isConnecting) {
|
|
2704
|
+
return new Promise((resolve) => this.eventEmitter.once("ready", () => resolve(this.isReady)));
|
|
2705
|
+
}
|
|
2706
|
+
return this.isReady;
|
|
2707
|
+
}
|
|
2708
|
+
async connect(uri, connectionOptions, options) {
|
|
2709
|
+
if (!this.isReady && this.isConnecting) {
|
|
2710
|
+
return new Promise((resolve) => this.eventEmitter.once("ready", () => resolve(true)));
|
|
2711
|
+
}
|
|
2712
|
+
if (this.mongoose.connection?.readyState === 1) {
|
|
2713
|
+
return true;
|
|
2714
|
+
}
|
|
2715
|
+
uri ??= this.uri || this.client.config.app.devMode ? process.env.MONGO_URI_DEV : process.env.MONGO_URI;
|
|
2716
|
+
options = { ...options, maxRetries: options?.maxRetries ?? 3 };
|
|
2717
|
+
if (!uri) {
|
|
2718
|
+
throw new Error(
|
|
2719
|
+
`MONGO_URI Missing: ${this.client.config.app.devMode ? "DEV MODE is enabled, but MONGO_URI_DEV is not set" : "MONGO_URI not set"}`
|
|
2720
|
+
);
|
|
2721
|
+
}
|
|
2722
|
+
this.uri = uri;
|
|
2723
|
+
this.isReady = false;
|
|
2724
|
+
this.isConnecting = true;
|
|
2725
|
+
try {
|
|
2726
|
+
const stopLoader = this.client.logger.loader("Connecting to MongoDB...");
|
|
2727
|
+
await retryExponentialBackoff(
|
|
2728
|
+
(attempt) => {
|
|
2729
|
+
return this.mongoose.connect(uri, {
|
|
2730
|
+
serverSelectionTimeoutMS: 3e4,
|
|
2731
|
+
socketTimeoutMS: 45e3,
|
|
2732
|
+
connectTimeoutMS: 3e4,
|
|
2733
|
+
maxPoolSize: 10,
|
|
2734
|
+
minPoolSize: 5,
|
|
2735
|
+
bufferCommands: false,
|
|
2736
|
+
...connectionOptions
|
|
2737
|
+
});
|
|
2738
|
+
},
|
|
2739
|
+
options.maxRetries,
|
|
2740
|
+
1e3
|
|
2741
|
+
);
|
|
2742
|
+
this.isReady = true;
|
|
2743
|
+
this.eventEmitter.emit("ready");
|
|
2744
|
+
globalInstanceEmitter.emit("connected", this);
|
|
2745
|
+
stopLoader("Connected to MongoDB ");
|
|
2746
|
+
} catch (err) {
|
|
2747
|
+
this.client.logger.error(`Failed to connect to MongoDB after ${options.maxRetries} attempt(s)`, err);
|
|
2748
|
+
}
|
|
2749
|
+
this.isConnecting = false;
|
|
2750
|
+
return true;
|
|
2751
|
+
}
|
|
2752
|
+
};
|
|
2753
|
+
|
|
2754
|
+
// src/modules/db/mongo/mongoSchema.builder.ts
|
|
2755
|
+
import {
|
|
2756
|
+
Schema
|
|
2757
|
+
} from "mongoose";
|
|
2758
|
+
import { randomBytes } from "crypto";
|
|
2759
|
+
import EventEmitter3 from "events";
|
|
2760
|
+
try {
|
|
2761
|
+
import("mongoose");
|
|
2762
|
+
} catch {
|
|
2763
|
+
throw new Error("MongoSchemaBuilder requires the mongoose package, install it with `npm install mongoose`");
|
|
2764
|
+
}
|
|
2765
|
+
function createMongoSchema(collection, definition, options) {
|
|
2766
|
+
return new MongoSchemaBuilder(collection, definition, options);
|
|
2767
|
+
}
|
|
2768
|
+
var MongoSchemaBuilder = class {
|
|
2769
|
+
schema;
|
|
2770
|
+
model;
|
|
2771
|
+
database = null;
|
|
2772
|
+
connectionIndex = 0;
|
|
2773
|
+
isReady = false;
|
|
2774
|
+
isInitializing = false;
|
|
2775
|
+
eventEmitter = new EventEmitter3();
|
|
2776
|
+
logger;
|
|
2777
|
+
constructor(collection, definition, options) {
|
|
2778
|
+
this.connectionIndex = options?.connectionIndex ?? this.connectionIndex;
|
|
2779
|
+
this.logger = new Logger({
|
|
2780
|
+
prefixEmoji: "\u{1F96D}",
|
|
2781
|
+
prefix: `MongoSchema (c${this.connectionIndex}) [${collection}]`,
|
|
2782
|
+
colors: { primary: "#F29B58" }
|
|
2783
|
+
});
|
|
2784
|
+
this.eventEmitter.once("initialized", () => {
|
|
2785
|
+
if (this.database) {
|
|
2786
|
+
this.schema = new Schema(definition, { versionKey: false });
|
|
2787
|
+
this.model = this.database.mongoose.model(collection, this.schema);
|
|
2788
|
+
this.eventEmitter.emit("ready", true);
|
|
2789
|
+
} else {
|
|
2790
|
+
this.eventEmitter.emit("error", new Error(`MongoDatabase (c${this.connectionIndex}) not found`));
|
|
2791
|
+
}
|
|
2792
|
+
});
|
|
2793
|
+
this.eventEmitter.on("ready", (ready) => {
|
|
2794
|
+
this.isReady = ready;
|
|
2795
|
+
if (this.database?.client.config.app.verbose) {
|
|
2796
|
+
this.logger.debug(`Loaded! | ${this.database?.client.config.app.name}`);
|
|
2797
|
+
}
|
|
2798
|
+
});
|
|
2799
|
+
this.eventEmitter.on("error", (error) => this.logger.error("Error:", error));
|
|
2800
|
+
this.init().catch((error) => {
|
|
2801
|
+
this.eventEmitter.emit("error", error);
|
|
2802
|
+
});
|
|
2803
|
+
}
|
|
2804
|
+
async init() {
|
|
2805
|
+
if (this.isInitializing) return;
|
|
2806
|
+
this.isInitializing = true;
|
|
2807
|
+
try {
|
|
2808
|
+
const database = await useMongoDatabase(this.connectionIndex);
|
|
2809
|
+
if (!database) {
|
|
2810
|
+
throw new Error("Could not use MongoDatabase");
|
|
2811
|
+
}
|
|
2812
|
+
this.database = database;
|
|
2813
|
+
this.eventEmitter.emit("initialized");
|
|
2814
|
+
} catch (err) {
|
|
2815
|
+
this.eventEmitter.emit("error", err);
|
|
2816
|
+
} finally {
|
|
2817
|
+
this.isInitializing = false;
|
|
2818
|
+
}
|
|
2819
|
+
}
|
|
2820
|
+
extend(extras) {
|
|
2821
|
+
for (const [key, fn] of Object.entries(extras)) {
|
|
2822
|
+
if (typeof fn === "function") {
|
|
2823
|
+
this[key] = function(...args) {
|
|
2824
|
+
return fn.call(this, ...args);
|
|
2825
|
+
};
|
|
2826
|
+
}
|
|
2827
|
+
}
|
|
2828
|
+
return this;
|
|
2829
|
+
}
|
|
2830
|
+
on(event, listener) {
|
|
2831
|
+
this.eventEmitter.on(event, listener);
|
|
2832
|
+
return this;
|
|
2833
|
+
}
|
|
2834
|
+
once(event, listener) {
|
|
2835
|
+
this.eventEmitter.once(event, listener);
|
|
2836
|
+
return this;
|
|
2837
|
+
}
|
|
2838
|
+
off(event, listener) {
|
|
2839
|
+
this.eventEmitter.off(event, listener);
|
|
2840
|
+
return this;
|
|
2841
|
+
}
|
|
2842
|
+
async execute(fn) {
|
|
2843
|
+
try {
|
|
2844
|
+
if (!this.isReady) {
|
|
2845
|
+
await new Promise((resolve, reject) => {
|
|
2846
|
+
const timeout = setTimeout(() => reject("execution wait for ready timed out"), 45e3);
|
|
2847
|
+
this.eventEmitter.once("ready", () => {
|
|
2848
|
+
clearTimeout(timeout);
|
|
2849
|
+
resolve();
|
|
2850
|
+
});
|
|
2851
|
+
this.eventEmitter.once("error", (error) => {
|
|
2852
|
+
clearTimeout(timeout);
|
|
2853
|
+
reject(error);
|
|
2854
|
+
});
|
|
2855
|
+
});
|
|
2856
|
+
}
|
|
2857
|
+
if (!this.database) {
|
|
2858
|
+
throw new Error("MongoDB connection not found");
|
|
2859
|
+
}
|
|
2860
|
+
return await retryExponentialBackoff(async () => await fn());
|
|
2861
|
+
} catch (err) {
|
|
2862
|
+
this.eventEmitter.emit("error", err);
|
|
2863
|
+
}
|
|
2864
|
+
}
|
|
2865
|
+
async createHexId(bytes, path2, maxRetries = 10) {
|
|
2866
|
+
return await this.execute(async () => {
|
|
2867
|
+
const createHex = () => Buffer.from(randomBytes(bytes)).toString("hex");
|
|
2868
|
+
let id = createHex();
|
|
2869
|
+
let tries = 0;
|
|
2870
|
+
while (await this.model.exists({ [path2]: id })) {
|
|
2871
|
+
if (tries >= maxRetries) throw Error(`Failed to generate a unique hex ID after ${tries} attempt(s)`);
|
|
2872
|
+
id = createHex();
|
|
2873
|
+
tries++;
|
|
2874
|
+
}
|
|
2875
|
+
return id;
|
|
2876
|
+
});
|
|
2877
|
+
}
|
|
2878
|
+
async count(filter) {
|
|
2879
|
+
return await this.execute(async () => {
|
|
2880
|
+
return this.model.countDocuments(filter);
|
|
2881
|
+
});
|
|
2882
|
+
}
|
|
2883
|
+
async exists(filter) {
|
|
2884
|
+
return await this.execute(async () => {
|
|
2885
|
+
return await this.model.exists(filter) ? true : false;
|
|
2886
|
+
});
|
|
2887
|
+
}
|
|
2888
|
+
async create(query) {
|
|
2889
|
+
return await this.execute(async () => {
|
|
2890
|
+
return this.model.create(query);
|
|
2891
|
+
});
|
|
2892
|
+
}
|
|
2893
|
+
async upsert(filter, query, options) {
|
|
2894
|
+
return await this.execute(async () => {
|
|
2895
|
+
return this.model.findOneAndUpdate(filter, query, { ...options, upsert: true, new: true });
|
|
2896
|
+
});
|
|
2897
|
+
}
|
|
2898
|
+
async delete(filter) {
|
|
2899
|
+
return await this.execute(async () => {
|
|
2900
|
+
return this.model.deleteOne(filter);
|
|
2901
|
+
});
|
|
2902
|
+
}
|
|
2903
|
+
async deleteAll(filter) {
|
|
2904
|
+
return await this.execute(async () => {
|
|
2905
|
+
return this.model.deleteMany(filter);
|
|
2906
|
+
});
|
|
2907
|
+
}
|
|
2908
|
+
async distinct(key, filter) {
|
|
2909
|
+
return await this.execute(async () => {
|
|
2910
|
+
return this.model.distinct(key, filter);
|
|
2911
|
+
});
|
|
2912
|
+
}
|
|
2913
|
+
async fetch(filter, projection, options) {
|
|
2914
|
+
return await this.execute(async () => {
|
|
2915
|
+
return this.model.findOne(filter, projection, { ...options, lean: options?.lean ?? true });
|
|
2916
|
+
});
|
|
2917
|
+
}
|
|
2918
|
+
async fetchAll(filter, projection, options) {
|
|
2919
|
+
return await this.execute(async () => {
|
|
2920
|
+
return this.model.find(filter, projection, { ...options, lean: options?.lean ?? true });
|
|
2921
|
+
}) || [];
|
|
2922
|
+
}
|
|
2923
|
+
async update(filter, update, options) {
|
|
2924
|
+
return await this.execute(async () => {
|
|
2925
|
+
return this.model.findOneAndUpdate(filter, update, { ...options, lean: options?.lean ?? true });
|
|
2926
|
+
});
|
|
2927
|
+
}
|
|
2928
|
+
async updateAll(filter, update, options) {
|
|
2929
|
+
return await this.execute(async () => {
|
|
2930
|
+
return this.model.updateMany(filter, update, options);
|
|
2931
|
+
});
|
|
2932
|
+
}
|
|
2933
|
+
async aggregate(pipeline, options) {
|
|
2934
|
+
return await this.execute(async () => {
|
|
2935
|
+
const result = await this.model.aggregate(pipeline, options);
|
|
2936
|
+
return result?.length ? result : [];
|
|
2937
|
+
});
|
|
2938
|
+
}
|
|
2939
|
+
};
|
|
2940
|
+
|
|
2941
|
+
// src/tools/BetterContainer.ts
|
|
2942
|
+
import {
|
|
2943
|
+
ButtonBuilder as ButtonBuilder2,
|
|
2944
|
+
ContainerBuilder as ContainerBuilder2,
|
|
2945
|
+
ThumbnailBuilder
|
|
2946
|
+
} from "discord.js";
|
|
2947
|
+
var BetterContainer = class {
|
|
2948
|
+
container = new ContainerBuilder2();
|
|
2949
|
+
data;
|
|
2950
|
+
config;
|
|
2951
|
+
constructor(data = {}) {
|
|
2952
|
+
this.config = data.config || globalVimcordToolsConfig;
|
|
2953
|
+
this.data = {
|
|
2954
|
+
color: data.color ?? (this.config.devMode ? this.config.embedColorDev : this.config.embedColor),
|
|
2955
|
+
...data
|
|
2956
|
+
};
|
|
2957
|
+
this.build();
|
|
2958
|
+
}
|
|
2959
|
+
configure() {
|
|
2960
|
+
if (this.data.color) {
|
|
2961
|
+
try {
|
|
2962
|
+
const color = Array.isArray(this.data.color) ? this.data.color[Math.floor(Math.random() * this.data.color.length)] ?? null : this.data.color;
|
|
2963
|
+
if (color) {
|
|
2964
|
+
this.container.setAccentColor(parseInt(color.replace("#", ""), 16));
|
|
2965
|
+
} else {
|
|
2966
|
+
this.container.clearAccentColor();
|
|
2967
|
+
}
|
|
2968
|
+
} catch (error) {
|
|
2969
|
+
console.error("[BetterContainer] Invalid color:", error);
|
|
2970
|
+
}
|
|
2971
|
+
}
|
|
2972
|
+
}
|
|
2973
|
+
build() {
|
|
2974
|
+
this.configure();
|
|
2975
|
+
}
|
|
2976
|
+
addSeparator(options) {
|
|
2977
|
+
this.container.addSeparatorComponents((sb) => {
|
|
2978
|
+
if (options?.divider !== void 0) sb.setDivider(options.divider);
|
|
2979
|
+
if (options?.spacing !== void 0) sb.setSpacing(options.spacing);
|
|
2980
|
+
return sb;
|
|
2981
|
+
});
|
|
2982
|
+
return this;
|
|
2983
|
+
}
|
|
2984
|
+
addText(text) {
|
|
2985
|
+
this.container.addTextDisplayComponents((tdb) => tdb.setContent(Array.isArray(text) ? text.join("\n") : text));
|
|
2986
|
+
return this;
|
|
2987
|
+
}
|
|
2988
|
+
addMedia(...media) {
|
|
2989
|
+
this.container.addMediaGalleryComponents((mb) => {
|
|
2990
|
+
for (const m of media) {
|
|
2991
|
+
const urls = Array.isArray(m.url) ? m.url : [m.url];
|
|
2992
|
+
for (const u of urls) {
|
|
2993
|
+
mb.addItems((mgb) => {
|
|
2994
|
+
mgb.setURL(u);
|
|
2995
|
+
if (m.spoiler) mgb.setSpoiler(true);
|
|
2996
|
+
if (m.description) mgb.setDescription(m.description);
|
|
2997
|
+
return mgb;
|
|
2998
|
+
});
|
|
2999
|
+
}
|
|
3000
|
+
}
|
|
3001
|
+
return mb;
|
|
3002
|
+
});
|
|
3003
|
+
return this;
|
|
3004
|
+
}
|
|
3005
|
+
addSection(data) {
|
|
3006
|
+
this.container.addSectionComponents((sb) => {
|
|
3007
|
+
if (data.text) {
|
|
3008
|
+
sb.addTextDisplayComponents(
|
|
3009
|
+
(tdb) => tdb.setContent(Array.isArray(data.text) ? data.text.join("\n") : data.text)
|
|
3010
|
+
);
|
|
3011
|
+
}
|
|
3012
|
+
if (data.thumbnail) sb.setThumbnailAccessory(new ThumbnailBuilder(data.thumbnail));
|
|
3013
|
+
if (data.button) sb.setButtonAccessory(new ButtonBuilder2(data.button));
|
|
3014
|
+
return sb;
|
|
3015
|
+
});
|
|
3016
|
+
return this;
|
|
3017
|
+
}
|
|
3018
|
+
addActionRow(...components) {
|
|
3019
|
+
this.container.addActionRowComponents(...components);
|
|
3020
|
+
return this;
|
|
3021
|
+
}
|
|
3022
|
+
toJSON() {
|
|
3023
|
+
return this.container.toJSON();
|
|
3024
|
+
}
|
|
3025
|
+
async send(handler, options = {}) {
|
|
3026
|
+
this.build();
|
|
3027
|
+
return await dynaSend(handler, {
|
|
3028
|
+
...options,
|
|
3029
|
+
withResponse: true,
|
|
3030
|
+
components: [this.container],
|
|
3031
|
+
flags: Array.isArray(options.flags) ? [...options.flags, "IsComponentsV2"] : options.flags ? [options.flags, "IsComponentsV2"] : "IsComponentsV2"
|
|
3032
|
+
});
|
|
3033
|
+
}
|
|
3034
|
+
};
|
|
3035
|
+
|
|
3036
|
+
// src/tools/BetterModal.ts
|
|
3037
|
+
import {
|
|
3038
|
+
ChannelSelectMenuBuilder,
|
|
3039
|
+
FileUploadBuilder,
|
|
3040
|
+
LabelBuilder,
|
|
3041
|
+
MentionableSelectMenuBuilder,
|
|
3042
|
+
ModalBuilder,
|
|
3043
|
+
RoleSelectMenuBuilder,
|
|
3044
|
+
StringSelectMenuBuilder,
|
|
3045
|
+
TextInputBuilder,
|
|
3046
|
+
TextInputStyle,
|
|
3047
|
+
UserSelectMenuBuilder
|
|
3048
|
+
} from "discord.js";
|
|
3049
|
+
function randomCharString(length) {
|
|
3050
|
+
const chars = "ABCDEFGHJKLMOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
3051
|
+
return Array.from({ length }, () => chars.charAt(Math.floor(Math.random() * chars.length))).join("");
|
|
3052
|
+
}
|
|
3053
|
+
var BetterModal = class {
|
|
3054
|
+
id;
|
|
3055
|
+
options;
|
|
3056
|
+
modal;
|
|
3057
|
+
components = /* @__PURE__ */ new Map();
|
|
3058
|
+
config;
|
|
3059
|
+
constructor(options = {}) {
|
|
3060
|
+
this.id = options.id || this.createModalId();
|
|
3061
|
+
this.options = options;
|
|
3062
|
+
this.modal = new ModalBuilder().setCustomId(this.id);
|
|
3063
|
+
this.config = options.config || globalVimcordToolsConfig;
|
|
3064
|
+
if (options.title) {
|
|
3065
|
+
this.setTitle(options.title);
|
|
3066
|
+
}
|
|
3067
|
+
if (options.components?.length) {
|
|
3068
|
+
this.addComponents(...options.components);
|
|
3069
|
+
}
|
|
3070
|
+
}
|
|
3071
|
+
createModalId() {
|
|
3072
|
+
return `modal:${randomCharString(10)}-${Date.now()}`;
|
|
3073
|
+
}
|
|
3074
|
+
createComponentId() {
|
|
3075
|
+
return `modal-component:${randomCharString(4)}-${Date.now().toString().slice(-4)}`;
|
|
3076
|
+
}
|
|
3077
|
+
validateComponentLength() {
|
|
3078
|
+
if (this.components.size >= 5) throw new Error("Modal can only have 5 components");
|
|
3079
|
+
}
|
|
3080
|
+
toJSON() {
|
|
3081
|
+
return this.modal.toJSON();
|
|
3082
|
+
}
|
|
3083
|
+
build() {
|
|
3084
|
+
this.modal.setLabelComponents(Array.from(this.components.values()));
|
|
3085
|
+
return this.modal;
|
|
3086
|
+
}
|
|
3087
|
+
setTitle(title) {
|
|
3088
|
+
this.modal.setTitle(title);
|
|
3089
|
+
return this;
|
|
3090
|
+
}
|
|
3091
|
+
addComponents(...components) {
|
|
3092
|
+
for (const component of components) {
|
|
3093
|
+
if ("textInput" in component) {
|
|
3094
|
+
this.addTextInput(component.textInput);
|
|
3095
|
+
} else if ("stringSelect" in component) {
|
|
3096
|
+
this.addStringSelect(component.stringSelect);
|
|
3097
|
+
} else if ("channelSelect" in component) {
|
|
3098
|
+
this.addChannelSelect(component.channelSelect);
|
|
3099
|
+
} else if ("userSelect" in component) {
|
|
3100
|
+
this.addUserSelect(component.userSelect);
|
|
3101
|
+
} else if ("roleSelect" in component) {
|
|
3102
|
+
this.addRoleSelect(component.roleSelect);
|
|
3103
|
+
} else if ("mentionableSelect" in component) {
|
|
3104
|
+
this.addMentionableSelect(component.mentionableSelect);
|
|
3105
|
+
} else if ("fileUpload" in component) {
|
|
3106
|
+
this.addFileUpload(component.fileUpload);
|
|
3107
|
+
}
|
|
3108
|
+
}
|
|
3109
|
+
return this;
|
|
3110
|
+
}
|
|
3111
|
+
setComponents(...components) {
|
|
3112
|
+
this.modal.spliceLabelComponents(0, this.modal.components.length);
|
|
3113
|
+
this.addComponents(...components);
|
|
3114
|
+
return this;
|
|
3115
|
+
}
|
|
3116
|
+
addTextInput(data) {
|
|
3117
|
+
this.validateComponentLength();
|
|
3118
|
+
let { label, description, custom_id, ...rest } = data;
|
|
3119
|
+
custom_id ||= this.createComponentId();
|
|
3120
|
+
const textInputComponent = new TextInputBuilder(rest).setCustomId(custom_id);
|
|
3121
|
+
if (!rest.style) textInputComponent.setStyle(TextInputStyle.Short);
|
|
3122
|
+
const labelComponent = new LabelBuilder().setLabel(label).setTextInputComponent(textInputComponent);
|
|
3123
|
+
if (description) labelComponent.setDescription(description);
|
|
3124
|
+
this.components.set(custom_id, labelComponent);
|
|
3125
|
+
return this;
|
|
3126
|
+
}
|
|
3127
|
+
addStringSelect(data) {
|
|
3128
|
+
this.validateComponentLength();
|
|
3129
|
+
let { label, description, custom_id, ...rest } = data;
|
|
3130
|
+
custom_id ||= this.createComponentId();
|
|
3131
|
+
const stringSelectComponent = new StringSelectMenuBuilder(rest).setCustomId(custom_id);
|
|
3132
|
+
const labelComponent = new LabelBuilder().setLabel(label).setStringSelectMenuComponent(stringSelectComponent);
|
|
3133
|
+
if (description) labelComponent.setDescription(description);
|
|
3134
|
+
this.components.set(custom_id, labelComponent);
|
|
3135
|
+
return this;
|
|
3136
|
+
}
|
|
3137
|
+
addChannelSelect(data) {
|
|
3138
|
+
this.validateComponentLength();
|
|
3139
|
+
let { label, description, custom_id, ...rest } = data;
|
|
3140
|
+
custom_id ||= this.createComponentId();
|
|
3141
|
+
const channelSelectComponent = new ChannelSelectMenuBuilder(rest).setCustomId(custom_id);
|
|
3142
|
+
const labelComponent = new LabelBuilder().setLabel(label).setChannelSelectMenuComponent(channelSelectComponent);
|
|
3143
|
+
if (description) labelComponent.setDescription(description);
|
|
3144
|
+
this.components.set(custom_id, labelComponent);
|
|
3145
|
+
return this;
|
|
3146
|
+
}
|
|
3147
|
+
addUserSelect(data) {
|
|
3148
|
+
this.validateComponentLength();
|
|
3149
|
+
let { label, description, custom_id, ...rest } = data;
|
|
3150
|
+
custom_id ||= this.createComponentId();
|
|
3151
|
+
const userSelectComponent = new UserSelectMenuBuilder(rest).setCustomId(custom_id);
|
|
3152
|
+
const labelComponent = new LabelBuilder().setLabel(label).setUserSelectMenuComponent(userSelectComponent);
|
|
3153
|
+
if (description) labelComponent.setDescription(description);
|
|
3154
|
+
this.components.set(custom_id, labelComponent);
|
|
3155
|
+
return this;
|
|
3156
|
+
}
|
|
3157
|
+
addRoleSelect(data) {
|
|
3158
|
+
this.validateComponentLength();
|
|
3159
|
+
let { label, description, custom_id, ...rest } = data;
|
|
3160
|
+
custom_id ||= this.createComponentId();
|
|
3161
|
+
const roleSelectComponent = new RoleSelectMenuBuilder(rest).setCustomId(custom_id);
|
|
3162
|
+
const labelComponent = new LabelBuilder().setLabel(label).setRoleSelectMenuComponent(roleSelectComponent);
|
|
3163
|
+
if (description) labelComponent.setDescription(description);
|
|
3164
|
+
this.components.set(custom_id, labelComponent);
|
|
3165
|
+
return this;
|
|
3166
|
+
}
|
|
3167
|
+
addMentionableSelect(data) {
|
|
3168
|
+
this.validateComponentLength();
|
|
3169
|
+
let { label, description, custom_id, ...rest } = data;
|
|
3170
|
+
custom_id ||= this.createComponentId();
|
|
3171
|
+
const mentionableSelectComponent = new MentionableSelectMenuBuilder(rest).setCustomId(custom_id);
|
|
3172
|
+
const labelComponent = new LabelBuilder().setLabel(label).setMentionableSelectMenuComponent(mentionableSelectComponent);
|
|
3173
|
+
if (description) labelComponent.setDescription(description);
|
|
3174
|
+
this.components.set(custom_id, labelComponent);
|
|
3175
|
+
return this;
|
|
3176
|
+
}
|
|
3177
|
+
addFileUpload(data) {
|
|
3178
|
+
this.validateComponentLength();
|
|
3179
|
+
let { label, description, custom_id, ...rest } = data;
|
|
3180
|
+
custom_id ||= this.createComponentId();
|
|
3181
|
+
const fileUploadComponent = new FileUploadBuilder(rest).setCustomId(custom_id);
|
|
3182
|
+
const labelComponent = new LabelBuilder().setLabel(label).setFileUploadComponent(fileUploadComponent);
|
|
3183
|
+
if (description) labelComponent.setDescription(description);
|
|
3184
|
+
this.components.set(custom_id, labelComponent);
|
|
3185
|
+
return this;
|
|
3186
|
+
}
|
|
3187
|
+
/** Show the modal via interaction */
|
|
3188
|
+
async show(interaction) {
|
|
3189
|
+
if (!("showModal" in interaction)) throw new Error("Interaction does not support showing modals");
|
|
3190
|
+
if (!this.modal.data.title) throw new Error("Modal must have a title");
|
|
3191
|
+
this.build();
|
|
3192
|
+
await interaction.showModal(this.modal).catch((err) => {
|
|
3193
|
+
console.error("Modal failed to send", err);
|
|
3194
|
+
});
|
|
3195
|
+
}
|
|
3196
|
+
/** Waits for the modal to be submitted and returns the component data
|
|
3197
|
+
* @param interaction The interaction used to show the modal
|
|
3198
|
+
* @param options Options */
|
|
3199
|
+
async awaitSubmit(interaction, options) {
|
|
3200
|
+
if (!("showModal" in interaction)) throw new Error("Interaction does not support showing modals");
|
|
3201
|
+
try {
|
|
3202
|
+
const modalSubmit = await interaction.awaitModalSubmit({
|
|
3203
|
+
time: this.config.timeouts.modalSubmit,
|
|
3204
|
+
...options,
|
|
3205
|
+
filter: (i) => i.customId === this.id
|
|
3206
|
+
});
|
|
3207
|
+
const fields = {};
|
|
3208
|
+
const values = [];
|
|
3209
|
+
for (const [customId] of this.components) {
|
|
3210
|
+
let value = null;
|
|
3211
|
+
try {
|
|
3212
|
+
value = modalSubmit.fields.getTextInputValue(customId);
|
|
3213
|
+
} catch {
|
|
3214
|
+
try {
|
|
3215
|
+
const field = modalSubmit.fields.fields.get(customId);
|
|
3216
|
+
if (field && "values" in field) {
|
|
3217
|
+
value = field.values;
|
|
3218
|
+
}
|
|
3219
|
+
} catch {
|
|
3220
|
+
}
|
|
3221
|
+
}
|
|
3222
|
+
fields[customId] = value;
|
|
3223
|
+
values.push(value);
|
|
3224
|
+
}
|
|
3225
|
+
return { fields, values, interaction: modalSubmit };
|
|
3226
|
+
} catch (error) {
|
|
3227
|
+
return null;
|
|
3228
|
+
}
|
|
3229
|
+
}
|
|
3230
|
+
async showAndAwait(interaction, options) {
|
|
3231
|
+
if (!("showModal" in interaction)) throw new Error("Interaction does not support showing modals");
|
|
3232
|
+
await this.show(interaction);
|
|
3233
|
+
return this.awaitSubmit(interaction, options);
|
|
3234
|
+
}
|
|
3235
|
+
};
|
|
3236
|
+
|
|
3237
|
+
// src/tools/Paginator.ts
|
|
3238
|
+
import {
|
|
3239
|
+
ActionRowBuilder as ActionRowBuilder4,
|
|
3240
|
+
ButtonBuilder as ButtonBuilder3,
|
|
3241
|
+
ButtonStyle as ButtonStyle2,
|
|
3242
|
+
ComponentType as ComponentType2,
|
|
3243
|
+
ContainerBuilder as ContainerBuilder3,
|
|
3244
|
+
EmbedBuilder as EmbedBuilder2,
|
|
3245
|
+
StringSelectMenuBuilder as StringSelectMenuBuilder2
|
|
3246
|
+
} from "discord.js";
|
|
3247
|
+
import EventEmitter4 from "events";
|
|
3248
|
+
var PaginationType = /* @__PURE__ */ ((PaginationType2) => {
|
|
3249
|
+
PaginationType2[PaginationType2["Short"] = 0] = "Short";
|
|
3250
|
+
PaginationType2[PaginationType2["ShortJump"] = 1] = "ShortJump";
|
|
3251
|
+
PaginationType2[PaginationType2["Long"] = 2] = "Long";
|
|
3252
|
+
PaginationType2[PaginationType2["LongJump"] = 3] = "LongJump";
|
|
3253
|
+
return PaginationType2;
|
|
3254
|
+
})(PaginationType || {});
|
|
3255
|
+
var PaginationTimeoutType = /* @__PURE__ */ ((PaginationTimeoutType2) => {
|
|
3256
|
+
PaginationTimeoutType2[PaginationTimeoutType2["DisableComponents"] = 0] = "DisableComponents";
|
|
3257
|
+
PaginationTimeoutType2[PaginationTimeoutType2["ClearComponents"] = 1] = "ClearComponents";
|
|
3258
|
+
PaginationTimeoutType2[PaginationTimeoutType2["DeleteMessage"] = 2] = "DeleteMessage";
|
|
3259
|
+
return PaginationTimeoutType2;
|
|
3260
|
+
})(PaginationTimeoutType || {});
|
|
3261
|
+
function wrapPositive(num, max) {
|
|
3262
|
+
return (num % (max + 1) + (max + 1)) % (max + 1);
|
|
3263
|
+
}
|
|
3264
|
+
function createNavButton(id, config) {
|
|
3265
|
+
const data = config.paginator.buttons[id];
|
|
3266
|
+
const btn = new ButtonBuilder3({ customId: `btn_${id}`, style: ButtonStyle2.Secondary });
|
|
3267
|
+
if (data.label) {
|
|
3268
|
+
btn.setLabel(data.label);
|
|
3269
|
+
} else {
|
|
3270
|
+
btn.setEmoji(data.emoji.name);
|
|
3271
|
+
}
|
|
3272
|
+
return btn;
|
|
3273
|
+
}
|
|
3274
|
+
function isEmbed(item) {
|
|
3275
|
+
return item instanceof EmbedBuilder2 || item instanceof BetterEmbed;
|
|
3276
|
+
}
|
|
3277
|
+
function resolvePages(pages) {
|
|
3278
|
+
if (Array.isArray(pages)) {
|
|
3279
|
+
if (pages.length === 0) return [];
|
|
3280
|
+
if (Array.isArray(pages[0])) return pages;
|
|
3281
|
+
return pages;
|
|
3282
|
+
}
|
|
3283
|
+
return [pages];
|
|
3284
|
+
}
|
|
3285
|
+
var Paginator = class {
|
|
3286
|
+
chapters = [];
|
|
3287
|
+
options;
|
|
3288
|
+
config;
|
|
3289
|
+
data;
|
|
3290
|
+
events;
|
|
3291
|
+
eventEmitter = new EventEmitter4();
|
|
3292
|
+
constructor(options = {}) {
|
|
3293
|
+
this.config = options.config || globalVimcordToolsConfig;
|
|
3294
|
+
this.options = {
|
|
3295
|
+
type: options.type ?? 0 /* Short */,
|
|
3296
|
+
participants: options.participants ?? [],
|
|
3297
|
+
pages: options.pages ?? [],
|
|
3298
|
+
useReactions: options.useReactions ?? false,
|
|
3299
|
+
dynamic: options.dynamic ?? false,
|
|
3300
|
+
timeout: options.timeout ?? this.config.timeouts.pagination,
|
|
3301
|
+
onTimeout: options.onTimeout ?? 1 /* ClearComponents */
|
|
3302
|
+
};
|
|
3303
|
+
this.data = {
|
|
3304
|
+
message: null,
|
|
3305
|
+
messageActionRows: [],
|
|
3306
|
+
messageSendOptions: void 0,
|
|
3307
|
+
extraButtons: [],
|
|
3308
|
+
page: { current: null, index: { chapter: 0, nested: 0 } },
|
|
3309
|
+
navigation: { reactions: [], isRequired: false, isLong: false, canJump: false },
|
|
3310
|
+
collectors: { component: null, reaction: null },
|
|
3311
|
+
components: {
|
|
3312
|
+
chapterSelect: new StringSelectMenuBuilder2({ customId: "ssm_chapterSelect" }),
|
|
3313
|
+
navigation: {
|
|
3314
|
+
first: createNavButton("first", this.config),
|
|
3315
|
+
back: createNavButton("back", this.config),
|
|
3316
|
+
jump: createNavButton("jump", this.config),
|
|
3317
|
+
next: createNavButton("next", this.config),
|
|
3318
|
+
last: createNavButton("last", this.config)
|
|
3319
|
+
},
|
|
3320
|
+
actionRows: {
|
|
3321
|
+
chapterSelect: new ActionRowBuilder4(),
|
|
3322
|
+
navigation: new ActionRowBuilder4()
|
|
3323
|
+
}
|
|
3324
|
+
}
|
|
3325
|
+
};
|
|
3326
|
+
this.data.components.actionRows.chapterSelect.setComponents(this.data.components.chapterSelect);
|
|
3327
|
+
this.events = {
|
|
3328
|
+
beforeChapterChange: [],
|
|
3329
|
+
chapterChange: [],
|
|
3330
|
+
beforePageChange: [],
|
|
3331
|
+
pageChange: [],
|
|
3332
|
+
first: [],
|
|
3333
|
+
back: [],
|
|
3334
|
+
jump: [],
|
|
3335
|
+
next: [],
|
|
3336
|
+
last: [],
|
|
3337
|
+
collect: [],
|
|
3338
|
+
react: [],
|
|
3339
|
+
timeout: []
|
|
3340
|
+
};
|
|
3341
|
+
if (this.options.pages.length) {
|
|
3342
|
+
this.addChapter(this.options.pages, { label: "Default" });
|
|
3343
|
+
}
|
|
3344
|
+
for (const [key, val] of Object.entries(this.config.paginator.buttons)) {
|
|
3345
|
+
if (!val.emoji.id) throw new Error(`[Paginator] Button '${key}.id' is not defined`);
|
|
3346
|
+
if (!val.emoji.name) throw new Error(`[Paginator] Button '${key}.name' is not defined`);
|
|
3347
|
+
}
|
|
3348
|
+
}
|
|
3349
|
+
async build() {
|
|
3350
|
+
await this.setPage();
|
|
3351
|
+
this.data.components.actionRows.navigation.setComponents([]);
|
|
3352
|
+
this.data.navigation.reactions = [];
|
|
3353
|
+
this.data.messageActionRows = [];
|
|
3354
|
+
if (this.data.navigation.isRequired) {
|
|
3355
|
+
let navTypes = [];
|
|
3356
|
+
if (this.options.dynamic) {
|
|
3357
|
+
const isLong = this.data.navigation.isLong;
|
|
3358
|
+
const isJump = this.options.type === 1 /* ShortJump */ || this.options.type === 3 /* LongJump */;
|
|
3359
|
+
if (isLong) {
|
|
3360
|
+
this.options.type = isJump ? 3 /* LongJump */ : 2 /* Long */;
|
|
3361
|
+
} else {
|
|
3362
|
+
this.options.type = isJump ? 1 /* ShortJump */ : 0 /* Short */;
|
|
3363
|
+
}
|
|
3364
|
+
}
|
|
3365
|
+
switch (this.options.type) {
|
|
3366
|
+
case 0 /* Short */:
|
|
3367
|
+
navTypes = ["back", "next"];
|
|
3368
|
+
break;
|
|
3369
|
+
case 1 /* ShortJump */:
|
|
3370
|
+
navTypes = ["back", "jump", "next"];
|
|
3371
|
+
break;
|
|
3372
|
+
case 2 /* Long */:
|
|
3373
|
+
navTypes = ["first", "back", "next", "last"];
|
|
3374
|
+
break;
|
|
3375
|
+
case 3 /* LongJump */:
|
|
3376
|
+
navTypes = ["first", "back", "jump", "next", "last"];
|
|
3377
|
+
break;
|
|
3378
|
+
}
|
|
3379
|
+
if (this.options.useReactions) {
|
|
3380
|
+
this.data.navigation.reactions = navTypes.map((type) => {
|
|
3381
|
+
const data = this.config.paginator.buttons[type];
|
|
3382
|
+
return { name: data.emoji.name, id: data.emoji.id };
|
|
3383
|
+
});
|
|
3384
|
+
} else {
|
|
3385
|
+
this.data.components.actionRows.navigation.setComponents(
|
|
3386
|
+
navTypes.map((type) => this.data.components.navigation[type])
|
|
3387
|
+
);
|
|
3388
|
+
}
|
|
3389
|
+
}
|
|
3390
|
+
if (this.chapters.length > 1) {
|
|
3391
|
+
this.data.messageActionRows.push(this.data.components.actionRows.chapterSelect);
|
|
3392
|
+
}
|
|
3393
|
+
if (this.data.navigation.isRequired && !this.options.useReactions || this.data.extraButtons.length) {
|
|
3394
|
+
for (const btn of this.data.extraButtons) {
|
|
3395
|
+
this.data.components.actionRows.navigation.components.splice(btn.index, 0, btn.component);
|
|
3396
|
+
}
|
|
3397
|
+
this.data.messageActionRows.push(this.data.components.actionRows.navigation);
|
|
3398
|
+
}
|
|
3399
|
+
}
|
|
3400
|
+
buildSendOptions(options = {}) {
|
|
3401
|
+
const sendOptions = {
|
|
3402
|
+
content: "",
|
|
3403
|
+
embeds: [],
|
|
3404
|
+
components: [],
|
|
3405
|
+
flags: [],
|
|
3406
|
+
...options,
|
|
3407
|
+
withResponse: true
|
|
3408
|
+
};
|
|
3409
|
+
const page = this.data.page.current;
|
|
3410
|
+
if (Array.isArray(page)) {
|
|
3411
|
+
sendOptions.embeds.push(...page);
|
|
3412
|
+
} else if (typeof page === "string") {
|
|
3413
|
+
sendOptions.content = page;
|
|
3414
|
+
} else if (isEmbed(page)) {
|
|
3415
|
+
sendOptions.embeds.push(page);
|
|
3416
|
+
} else if (page instanceof ContainerBuilder3 || page instanceof BetterContainer) {
|
|
3417
|
+
sendOptions.components.push(page);
|
|
3418
|
+
if (!sendOptions.flags.includes("IsComponentsV2")) {
|
|
3419
|
+
sendOptions.flags.push("IsComponentsV2");
|
|
3420
|
+
}
|
|
3421
|
+
}
|
|
3422
|
+
sendOptions.components.push(...this.data.messageActionRows);
|
|
3423
|
+
return sendOptions;
|
|
3424
|
+
}
|
|
3425
|
+
async handlePostTimeout() {
|
|
3426
|
+
if (!this.data.message) return;
|
|
3427
|
+
this.callEventStack("timeout", this.data.message);
|
|
3428
|
+
this.data.collectors.component?.stop();
|
|
3429
|
+
this.data.collectors.reaction?.stop();
|
|
3430
|
+
switch (this.options.onTimeout) {
|
|
3431
|
+
case 0 /* DisableComponents */:
|
|
3432
|
+
if (!this.data.message.editable) break;
|
|
3433
|
+
const disabledNavComponents = this.data.components.actionRows.navigation.components.map((component) => {
|
|
3434
|
+
if ("setDisabled" in component) {
|
|
3435
|
+
return component.setDisabled(true);
|
|
3436
|
+
}
|
|
3437
|
+
return component;
|
|
3438
|
+
});
|
|
3439
|
+
const disabledNavRow = ActionRowBuilder4.from(this.data.components.actionRows.navigation).setComponents(
|
|
3440
|
+
disabledNavComponents
|
|
3441
|
+
);
|
|
3442
|
+
const newComponents = [];
|
|
3443
|
+
const currentPage = this.data.page.current;
|
|
3444
|
+
if (currentPage instanceof ContainerBuilder3 || currentPage instanceof BetterContainer) {
|
|
3445
|
+
newComponents.push(currentPage);
|
|
3446
|
+
}
|
|
3447
|
+
if (this.chapters.length > 1) {
|
|
3448
|
+
const disabledSelect = StringSelectMenuBuilder2.from(this.data.components.chapterSelect).setDisabled(
|
|
3449
|
+
true
|
|
3450
|
+
);
|
|
3451
|
+
newComponents.push(
|
|
3452
|
+
ActionRowBuilder4.from(this.data.components.actionRows.chapterSelect).setComponents(disabledSelect)
|
|
3453
|
+
);
|
|
3454
|
+
}
|
|
3455
|
+
if (disabledNavRow.components.length > 0) {
|
|
3456
|
+
newComponents.push(disabledNavRow);
|
|
3457
|
+
}
|
|
3458
|
+
await this.data.message.edit({ components: newComponents }).catch(Boolean);
|
|
3459
|
+
if (this.options.useReactions) {
|
|
3460
|
+
await this.nav_removeFromMessage();
|
|
3461
|
+
}
|
|
3462
|
+
break;
|
|
3463
|
+
case 1 /* ClearComponents */:
|
|
3464
|
+
if (this.data.message.editable) {
|
|
3465
|
+
await this.nav_removeFromMessage();
|
|
3466
|
+
}
|
|
3467
|
+
break;
|
|
3468
|
+
case 2 /* DeleteMessage */:
|
|
3469
|
+
await this.data.message.delete().catch(Boolean);
|
|
3470
|
+
break;
|
|
3471
|
+
}
|
|
3472
|
+
}
|
|
3473
|
+
async nav_removeFromMessage() {
|
|
3474
|
+
if (!this.data.message?.editable) return;
|
|
3475
|
+
if (this.options.useReactions) {
|
|
3476
|
+
await this.data.message.reactions.removeAll().catch(Boolean);
|
|
3477
|
+
} else {
|
|
3478
|
+
const newComponents = this.data.message.components.filter((c) => c.type !== ComponentType2.Container);
|
|
3479
|
+
await this.data.message.edit({ components: newComponents }).catch(Boolean);
|
|
3480
|
+
}
|
|
3481
|
+
}
|
|
3482
|
+
async nav_addReactions() {
|
|
3483
|
+
if (!this.data.message || !this.options.useReactions || !this.data.navigation.reactions.length) return;
|
|
3484
|
+
for (const r of this.data.navigation.reactions) {
|
|
3485
|
+
await this.data.message.react(r.id).catch(Boolean);
|
|
3486
|
+
}
|
|
3487
|
+
}
|
|
3488
|
+
async collect_components() {
|
|
3489
|
+
if (!this.data.message || !this.data.messageActionRows.length) return;
|
|
3490
|
+
if (this.data.collectors.component) {
|
|
3491
|
+
this.data.collectors.component.resetTimer();
|
|
3492
|
+
return;
|
|
3493
|
+
}
|
|
3494
|
+
const participantIds = this.options.participants.map((p) => typeof p === "string" ? p : p.id);
|
|
3495
|
+
const collector = this.data.message.createMessageComponentCollector({
|
|
3496
|
+
filter: async (i) => {
|
|
3497
|
+
if (!participantIds.length) return true;
|
|
3498
|
+
if (participantIds.includes(i.user.id)) {
|
|
3499
|
+
return true;
|
|
3500
|
+
} else {
|
|
3501
|
+
await i.reply({ content: this.config.paginator.notAParticipantMessage, flags: "Ephemeral" });
|
|
3502
|
+
return false;
|
|
3503
|
+
}
|
|
3504
|
+
},
|
|
3505
|
+
...this.options.timeout ? { idle: this.options.timeout } : {}
|
|
3506
|
+
});
|
|
3507
|
+
this.data.collectors.component = collector;
|
|
3508
|
+
collector.on("collect", async (i) => {
|
|
3509
|
+
if (!i.isStringSelectMenu() && !i.isButton()) return;
|
|
3510
|
+
collector.resetTimer();
|
|
3511
|
+
this.callEventStack("collect", i, this.data.page.current, this.data.page.index);
|
|
3512
|
+
try {
|
|
3513
|
+
if (i.customId === "btn_jump") {
|
|
3514
|
+
this.callEventStack("jump", this.data.page.current, this.data.page.index);
|
|
3515
|
+
await i.reply({ content: "Jump not implemented yet.", flags: "Ephemeral" });
|
|
3516
|
+
return;
|
|
3517
|
+
}
|
|
3518
|
+
await i.deferUpdate().catch(Boolean);
|
|
3519
|
+
switch (i.customId) {
|
|
3520
|
+
case "ssm_chapterSelect":
|
|
3521
|
+
const chapterIndex = this.chapters.findIndex(
|
|
3522
|
+
(c) => c.id === i.values[0]
|
|
3523
|
+
);
|
|
3524
|
+
await this.setPage(chapterIndex, 0);
|
|
3525
|
+
break;
|
|
3526
|
+
case "btn_first":
|
|
3527
|
+
this.callEventStack("first", this.data.page.current, this.data.page.index);
|
|
3528
|
+
await this.setPage(this.data.page.index.chapter, 0);
|
|
3529
|
+
break;
|
|
3530
|
+
case "btn_back":
|
|
3531
|
+
this.callEventStack("back", this.data.page.current, this.data.page.index);
|
|
3532
|
+
await this.setPage(this.data.page.index.chapter, this.data.page.index.nested - 1);
|
|
3533
|
+
break;
|
|
3534
|
+
case "btn_next":
|
|
3535
|
+
this.callEventStack("next", this.data.page.current, this.data.page.index);
|
|
3536
|
+
await this.setPage(this.data.page.index.chapter, this.data.page.index.nested + 1);
|
|
3537
|
+
break;
|
|
3538
|
+
case "btn_last":
|
|
3539
|
+
this.callEventStack("last", this.data.page.current, this.data.page.index);
|
|
3540
|
+
await this.setPage(
|
|
3541
|
+
this.data.page.index.chapter,
|
|
3542
|
+
this.chapters[this.data.page.index.chapter].pages.length - 1
|
|
3543
|
+
);
|
|
3544
|
+
break;
|
|
3545
|
+
}
|
|
3546
|
+
await this.refresh();
|
|
3547
|
+
} catch (err) {
|
|
3548
|
+
console.error("[Paginator] Component navigation error", err);
|
|
3549
|
+
}
|
|
3550
|
+
});
|
|
3551
|
+
collector.on("end", async () => {
|
|
3552
|
+
this.data.collectors.component = null;
|
|
3553
|
+
this.handlePostTimeout();
|
|
3554
|
+
});
|
|
3555
|
+
}
|
|
3556
|
+
async callEventStack(event, ...args) {
|
|
3557
|
+
if (!this.events[event].length) return;
|
|
3558
|
+
const listeners = [...this.events[event]];
|
|
3559
|
+
for (const _event of listeners) {
|
|
3560
|
+
await _event.listener(...args);
|
|
3561
|
+
if (_event.once) {
|
|
3562
|
+
const originalIndex = this.events[event].indexOf(_event);
|
|
3563
|
+
if (originalIndex > -1) this.events[event].splice(originalIndex, 1);
|
|
3564
|
+
}
|
|
3565
|
+
}
|
|
3566
|
+
}
|
|
3567
|
+
on(event, listener, once = false) {
|
|
3568
|
+
this.events[event].push({ listener, once });
|
|
3569
|
+
return this;
|
|
3570
|
+
}
|
|
3571
|
+
/** Adds a chapter to the paginator.
|
|
3572
|
+
* @param pages The pages for this chapter.
|
|
3573
|
+
* Note: `[Embed1, Embed2]` = 2 Pages. `[[Embed1, Embed2]]` = 1 Page (Group of 2).
|
|
3574
|
+
* @param data Metadata for the chapter select menu. */
|
|
3575
|
+
addChapter(pages, data) {
|
|
3576
|
+
if (data.default === void 0 && !this.chapters.length) {
|
|
3577
|
+
data.default = true;
|
|
3578
|
+
}
|
|
3579
|
+
if (data.default) {
|
|
3580
|
+
this.data.components.chapterSelect.options.forEach((opt) => opt.setDefault(false));
|
|
3581
|
+
}
|
|
3582
|
+
if (!data.value) {
|
|
3583
|
+
data.value = `ssm_c:${this.chapters.length}`;
|
|
3584
|
+
}
|
|
3585
|
+
const normalizedPages = resolvePages(pages);
|
|
3586
|
+
this.chapters.push({ id: data.value, pages: normalizedPages });
|
|
3587
|
+
this.data.components.chapterSelect.addOptions(data);
|
|
3588
|
+
return this;
|
|
3589
|
+
}
|
|
3590
|
+
spliceChapters(index, deleteCount) {
|
|
3591
|
+
this.chapters.splice(index, deleteCount);
|
|
3592
|
+
this.data.components.chapterSelect.spliceOptions(index, deleteCount);
|
|
3593
|
+
return this;
|
|
3594
|
+
}
|
|
3595
|
+
hydrateChapter(index, pages, set) {
|
|
3596
|
+
if (!this.chapters[index]) {
|
|
3597
|
+
throw new Error(`[Paginator] Could not find chapter at index ${index}`);
|
|
3598
|
+
}
|
|
3599
|
+
const normalizedPages = resolvePages(pages);
|
|
3600
|
+
if (set) {
|
|
3601
|
+
this.chapters[index].pages = normalizedPages;
|
|
3602
|
+
} else {
|
|
3603
|
+
this.chapters[index].pages.push(...normalizedPages);
|
|
3604
|
+
}
|
|
3605
|
+
return this;
|
|
3606
|
+
}
|
|
3607
|
+
setPaginationType(type) {
|
|
3608
|
+
this.options.type = type;
|
|
3609
|
+
return this;
|
|
3610
|
+
}
|
|
3611
|
+
insertButtonAt(index, component) {
|
|
3612
|
+
if (this.data.components.actionRows.navigation.components.length >= 5) {
|
|
3613
|
+
throw new Error("[Paginator] You cannot have more than 5 components in 1 action row. Use a separate ActionRow");
|
|
3614
|
+
}
|
|
3615
|
+
this.data.extraButtons.push({ index, component });
|
|
3616
|
+
return this;
|
|
3617
|
+
}
|
|
3618
|
+
removeButtonAt(...index) {
|
|
3619
|
+
index.forEach((i) => this.data.extraButtons.splice(i, 1));
|
|
3620
|
+
return this;
|
|
3621
|
+
}
|
|
3622
|
+
async setPage(chapterIndex = this.data.page.index.chapter, nestedIndex = this.data.page.index.nested) {
|
|
3623
|
+
const _oldChapterIndex = this.data.page.index.chapter;
|
|
3624
|
+
this.data.page.index.chapter = wrapPositive(chapterIndex, this.chapters.length - 1);
|
|
3625
|
+
const currentChapter = this.chapters[this.data.page.index.chapter];
|
|
3626
|
+
if (!currentChapter) {
|
|
3627
|
+
throw new Error(`[Paginator] Could not find chapter at index ${this.data.page.index.chapter}`);
|
|
3628
|
+
}
|
|
3629
|
+
await this.callEventStack("beforeChapterChange", this.data.page.index.chapter);
|
|
3630
|
+
await this.callEventStack("beforePageChange", nestedIndex);
|
|
3631
|
+
const _oldNestedIndex = this.data.page.index.nested;
|
|
3632
|
+
this.data.page.index.nested = this.data.page.index.chapter !== _oldChapterIndex ? 0 : wrapPositive(nestedIndex, currentChapter.pages.length - 1);
|
|
3633
|
+
const currentPage = currentChapter.pages[this.data.page.index.nested];
|
|
3634
|
+
if (!currentPage) {
|
|
3635
|
+
throw new Error(`[Paginator] Could not find page at index ${this.data.page.index.nested}`);
|
|
3636
|
+
}
|
|
3637
|
+
this.data.page.current = currentPage;
|
|
3638
|
+
this.data.components.chapterSelect.options.forEach((opt) => opt.setDefault(false));
|
|
3639
|
+
this.data.components.chapterSelect.options.at(this.data.page.index.chapter)?.setDefault(true);
|
|
3640
|
+
const { jumpableThreshold, longThreshold } = this.config.paginator;
|
|
3641
|
+
this.data.navigation.isRequired = currentChapter.pages.length >= 2;
|
|
3642
|
+
this.data.navigation.canJump = currentChapter.pages.length >= jumpableThreshold;
|
|
3643
|
+
this.data.navigation.isLong = currentChapter.pages.length >= longThreshold;
|
|
3644
|
+
if (this.data.page.index.chapter !== _oldChapterIndex) {
|
|
3645
|
+
this.callEventStack(
|
|
3646
|
+
"chapterChange",
|
|
3647
|
+
this.data.components.chapterSelect.options.at(this.data.page.index.chapter),
|
|
3648
|
+
this.data.page.current,
|
|
3649
|
+
this.data.page.index
|
|
3650
|
+
);
|
|
3651
|
+
this.callEventStack("pageChange", this.data.page.current, this.data.page.index);
|
|
3652
|
+
} else if (this.data.page.index.nested !== _oldNestedIndex) {
|
|
3653
|
+
this.callEventStack("pageChange", this.data.page.current, this.data.page.index);
|
|
3654
|
+
}
|
|
3655
|
+
}
|
|
3656
|
+
async refresh() {
|
|
3657
|
+
if (!this.data.message) {
|
|
3658
|
+
throw new Error("[Paginator] Cannot refresh, message not sent");
|
|
3659
|
+
}
|
|
3660
|
+
if (!this.data.message.editable) {
|
|
3661
|
+
throw new Error("[Paginator] Cannot refresh, message is not editable");
|
|
3662
|
+
}
|
|
3663
|
+
await this.build();
|
|
3664
|
+
this.data.message = await dynaSend(this.data.message, {
|
|
3665
|
+
sendMethod: 5 /* MessageEdit */,
|
|
3666
|
+
...this.buildSendOptions(this.data.messageSendOptions)
|
|
3667
|
+
});
|
|
3668
|
+
return this.data.message;
|
|
3669
|
+
}
|
|
3670
|
+
async send(handler, options) {
|
|
3671
|
+
this.data.messageSendOptions = options;
|
|
3672
|
+
await this.build();
|
|
3673
|
+
this.data.message = await dynaSend(handler, this.buildSendOptions(options));
|
|
3674
|
+
if (!this.data.message) return null;
|
|
3675
|
+
this.collect_components();
|
|
3676
|
+
return this.data.message;
|
|
3677
|
+
}
|
|
3678
|
+
};
|
|
3679
|
+
|
|
3680
|
+
// src/tools/Prompt.ts
|
|
3681
|
+
import {
|
|
3682
|
+
ActionRowBuilder as ActionRowBuilder5,
|
|
3683
|
+
ButtonBuilder as ButtonBuilder4,
|
|
3684
|
+
ButtonStyle as ButtonStyle3,
|
|
3685
|
+
ComponentType as ComponentType3
|
|
3686
|
+
} from "discord.js";
|
|
3687
|
+
var PromptResolveType = /* @__PURE__ */ ((PromptResolveType2) => {
|
|
3688
|
+
PromptResolveType2[PromptResolveType2["DisableComponents"] = 0] = "DisableComponents";
|
|
3689
|
+
PromptResolveType2[PromptResolveType2["ClearComponents"] = 1] = "ClearComponents";
|
|
3690
|
+
PromptResolveType2[PromptResolveType2["DeleteOnConfirm"] = 3] = "DeleteOnConfirm";
|
|
3691
|
+
PromptResolveType2[PromptResolveType2["DeleteOnReject"] = 4] = "DeleteOnReject";
|
|
3692
|
+
return PromptResolveType2;
|
|
3693
|
+
})(PromptResolveType || {});
|
|
3694
|
+
var Prompt = class {
|
|
3695
|
+
participants;
|
|
3696
|
+
content;
|
|
3697
|
+
embed;
|
|
3698
|
+
container;
|
|
3699
|
+
buttons;
|
|
3700
|
+
customButtons;
|
|
3701
|
+
onResolve;
|
|
3702
|
+
timeout;
|
|
3703
|
+
config;
|
|
3704
|
+
message = null;
|
|
3705
|
+
constructor(options = {}) {
|
|
3706
|
+
this.config = options.config ?? globalVimcordToolsConfig;
|
|
3707
|
+
this.participants = options.participants ?? [];
|
|
3708
|
+
this.timeout = options.timeout ?? this.config.timeouts.prompt;
|
|
3709
|
+
this.content = options.content;
|
|
3710
|
+
this.embed = options.embed ?? this.createDefaultForm();
|
|
3711
|
+
this.container = options?.container;
|
|
3712
|
+
this.buttons = this.createButtons(options.buttons);
|
|
3713
|
+
this.customButtons = this.createCustomButtons(options.customButtons);
|
|
3714
|
+
this.onResolve = options.onResolve ?? [3 /* DeleteOnConfirm */, 4 /* DeleteOnReject */];
|
|
3715
|
+
}
|
|
3716
|
+
createDefaultForm() {
|
|
3717
|
+
return new BetterEmbed({
|
|
3718
|
+
title: this.config.prompt.defaultTitle,
|
|
3719
|
+
description: this.config.prompt.defaultDescription
|
|
3720
|
+
});
|
|
3721
|
+
}
|
|
3722
|
+
createButtons(buttonOptions) {
|
|
3723
|
+
const confirm = this.buildButton(
|
|
3724
|
+
buttonOptions?.confirm,
|
|
3725
|
+
"btn_confirm",
|
|
3726
|
+
this.config.prompt.confirmLabel,
|
|
3727
|
+
ButtonStyle3.Success
|
|
3728
|
+
);
|
|
3729
|
+
const reject = this.buildButton(
|
|
3730
|
+
buttonOptions?.reject,
|
|
3731
|
+
"btn_reject",
|
|
3732
|
+
this.config.prompt.rejectLabel,
|
|
3733
|
+
ButtonStyle3.Danger
|
|
3734
|
+
);
|
|
3735
|
+
return { confirm, reject };
|
|
3736
|
+
}
|
|
3737
|
+
createCustomButtons(customOptions) {
|
|
3738
|
+
const map = /* @__PURE__ */ new Map();
|
|
3739
|
+
if (!customOptions) return map;
|
|
3740
|
+
for (const [customId, { builder, handler, index = 2 }] of Object.entries(customOptions)) {
|
|
3741
|
+
const button = this.buildButton(builder, customId, customId, ButtonStyle3.Primary);
|
|
3742
|
+
map.set(customId, { button, handler, index });
|
|
3743
|
+
}
|
|
3744
|
+
return map;
|
|
3745
|
+
}
|
|
3746
|
+
buildButton(option, customId, defaultLabel, defaultStyle) {
|
|
3747
|
+
if (typeof option === "function") {
|
|
3748
|
+
return option(new ButtonBuilder4());
|
|
3749
|
+
}
|
|
3750
|
+
if (option instanceof ButtonBuilder4) {
|
|
3751
|
+
return option;
|
|
3752
|
+
}
|
|
3753
|
+
return new ButtonBuilder4({
|
|
3754
|
+
customId,
|
|
3755
|
+
label: defaultLabel,
|
|
3756
|
+
style: defaultStyle,
|
|
3757
|
+
...option
|
|
3758
|
+
});
|
|
3759
|
+
}
|
|
3760
|
+
buildActionRow(disable = {}) {
|
|
3761
|
+
const confirmBtn = disable.confirm ? new ButtonBuilder4(this.buttons.confirm.data).setDisabled(true) : this.buttons.confirm;
|
|
3762
|
+
const rejectBtn = disable.reject ? new ButtonBuilder4(this.buttons.reject.data).setDisabled(true) : this.buttons.reject;
|
|
3763
|
+
const buttons = [];
|
|
3764
|
+
const customButtonsArray = Array.from(this.customButtons.entries()).map(([customId, data]) => ({
|
|
3765
|
+
customId,
|
|
3766
|
+
button: disable[customId] ? new ButtonBuilder4(data.button.data).setDisabled(true) : data.button,
|
|
3767
|
+
index: data.index
|
|
3768
|
+
}));
|
|
3769
|
+
customButtonsArray.sort((a, b) => a.index - b.index);
|
|
3770
|
+
for (const custom of customButtonsArray) {
|
|
3771
|
+
if (custom.index === 0) {
|
|
3772
|
+
buttons.push(custom.button);
|
|
3773
|
+
}
|
|
3774
|
+
}
|
|
3775
|
+
buttons.push(confirmBtn);
|
|
3776
|
+
for (const custom of customButtonsArray) {
|
|
3777
|
+
if (custom.index === 1) {
|
|
3778
|
+
buttons.push(custom.button);
|
|
3779
|
+
}
|
|
3780
|
+
}
|
|
3781
|
+
buttons.push(rejectBtn);
|
|
3782
|
+
for (const custom of customButtonsArray) {
|
|
3783
|
+
if (custom.index >= 2) {
|
|
3784
|
+
buttons.push(custom.button);
|
|
3785
|
+
}
|
|
3786
|
+
}
|
|
3787
|
+
return new ActionRowBuilder5({ components: buttons });
|
|
3788
|
+
}
|
|
3789
|
+
buildSendOptions(options) {
|
|
3790
|
+
const sendData = { ...options };
|
|
3791
|
+
if (this.container) {
|
|
3792
|
+
sendData.components = Array.isArray(sendData.components) ? [...sendData.components, this.container] : [this.container];
|
|
3793
|
+
const existingFlags = sendData.flags ? Array.isArray(sendData.flags) ? sendData.flags : [sendData.flags] : [];
|
|
3794
|
+
if (!existingFlags.includes("IsComponentsV2")) {
|
|
3795
|
+
sendData.flags = [...existingFlags, "IsComponentsV2"];
|
|
3796
|
+
} else {
|
|
3797
|
+
sendData.flags = existingFlags;
|
|
3798
|
+
}
|
|
3799
|
+
} else {
|
|
3800
|
+
sendData.embeds = Array.isArray(sendData.embeds) ? [this.embed, ...sendData.embeds] : [this.embed];
|
|
3801
|
+
}
|
|
3802
|
+
if (this.content) {
|
|
3803
|
+
sendData.content = this.content;
|
|
3804
|
+
}
|
|
3805
|
+
sendData.components = Array.isArray(sendData.components) ? [...sendData.components, this.buildActionRow()] : [this.buildActionRow()];
|
|
3806
|
+
return sendData;
|
|
3807
|
+
}
|
|
3808
|
+
isParticipant(userId) {
|
|
3809
|
+
if (this.participants.length === 0) return true;
|
|
3810
|
+
return this.participants.some((p) => {
|
|
3811
|
+
if (typeof p === "string") return p === userId;
|
|
3812
|
+
if (typeof p === "object" && "id" in p) return p.id === userId;
|
|
3813
|
+
return false;
|
|
3814
|
+
});
|
|
3815
|
+
}
|
|
3816
|
+
async handleResolve(confirmed) {
|
|
3817
|
+
if (!this.message) return;
|
|
3818
|
+
const shouldDelete = confirmed === true && this.onResolve.includes(3 /* DeleteOnConfirm */) || confirmed === false && this.onResolve.includes(4 /* DeleteOnReject */);
|
|
3819
|
+
if (shouldDelete) {
|
|
3820
|
+
await this.message.delete().catch(Boolean);
|
|
3821
|
+
return;
|
|
3822
|
+
}
|
|
3823
|
+
if (this.onResolve.includes(1 /* ClearComponents */)) {
|
|
3824
|
+
await this.message.edit({ components: [] }).catch(Boolean);
|
|
3825
|
+
} else if (this.onResolve.includes(0 /* DisableComponents */)) {
|
|
3826
|
+
const disableAll = { confirm: true, reject: true };
|
|
3827
|
+
for (const customId of this.customButtons.keys()) {
|
|
3828
|
+
disableAll[customId] = true;
|
|
3829
|
+
}
|
|
3830
|
+
await this.message.edit({ components: [this.buildActionRow(disableAll)] }).catch(Boolean);
|
|
3831
|
+
}
|
|
3832
|
+
}
|
|
3833
|
+
async send(handler, options) {
|
|
3834
|
+
const sendData = this.buildSendOptions(options);
|
|
3835
|
+
this.message = await dynaSend(handler, sendData);
|
|
3836
|
+
return this.message;
|
|
3837
|
+
}
|
|
3838
|
+
async awaitResponse() {
|
|
3839
|
+
if (!this.message) {
|
|
3840
|
+
throw new Error("Prompt must be sent before awaiting response");
|
|
3841
|
+
}
|
|
3842
|
+
const validCustomIds = /* @__PURE__ */ new Set(["btn_confirm", "btn_reject", ...this.customButtons.keys()]);
|
|
3843
|
+
try {
|
|
3844
|
+
const interaction = await this.message.awaitMessageComponent({
|
|
3845
|
+
componentType: ComponentType3.Button,
|
|
3846
|
+
filter: (i) => validCustomIds.has(i.customId) && this.isParticipant(i.user.id),
|
|
3847
|
+
time: this.timeout
|
|
3848
|
+
});
|
|
3849
|
+
await interaction.deferUpdate().catch(Boolean);
|
|
3850
|
+
let confirmed = null;
|
|
3851
|
+
if (interaction.customId === "btn_confirm") {
|
|
3852
|
+
confirmed = true;
|
|
3853
|
+
} else if (interaction.customId === "btn_reject") {
|
|
3854
|
+
confirmed = false;
|
|
3855
|
+
}
|
|
3856
|
+
const customButton = this.customButtons.get(interaction.customId);
|
|
3857
|
+
if (customButton?.handler) {
|
|
3858
|
+
await customButton.handler(interaction);
|
|
3859
|
+
}
|
|
3860
|
+
await this.handleResolve(confirmed);
|
|
3861
|
+
return {
|
|
3862
|
+
message: this.message,
|
|
3863
|
+
confirmed,
|
|
3864
|
+
customId: interaction.customId,
|
|
3865
|
+
timedOut: false
|
|
3866
|
+
};
|
|
3867
|
+
} catch (error) {
|
|
3868
|
+
await this.handleResolve(false);
|
|
3869
|
+
return {
|
|
3870
|
+
message: this.message,
|
|
3871
|
+
confirmed: null,
|
|
3872
|
+
customId: null,
|
|
3873
|
+
timedOut: true
|
|
3874
|
+
};
|
|
3875
|
+
}
|
|
3876
|
+
}
|
|
3877
|
+
};
|
|
3878
|
+
async function prompt(handler, options, sendOptions) {
|
|
3879
|
+
const p = new Prompt(options);
|
|
3880
|
+
await p.send(handler, sendOptions);
|
|
3881
|
+
return await p.awaitResponse();
|
|
3882
|
+
}
|
|
3883
|
+
|
|
3884
|
+
// src/utils/VimcordCLI.ts
|
|
3885
|
+
import { createInterface } from "readline";
|
|
3886
|
+
import jsTools2 from "jstools";
|
|
3887
|
+
var VimcordCLI = class {
|
|
3888
|
+
rl;
|
|
3889
|
+
options;
|
|
3890
|
+
commands = /* @__PURE__ */ new Map();
|
|
3891
|
+
logger = new Logger({ prefixEmoji: "\u{1F680}", prefix: "CLI", showTimestamp: false });
|
|
3892
|
+
constructor(options) {
|
|
3893
|
+
this.options = options;
|
|
3894
|
+
this.rl = createInterface({
|
|
3895
|
+
input: process.stdin,
|
|
3896
|
+
output: process.stdout,
|
|
3897
|
+
terminal: false
|
|
3898
|
+
});
|
|
3899
|
+
this.rl.on("line", (line) => {
|
|
3900
|
+
const { isCommand, commandName, content, args } = this.parseLine(line);
|
|
3901
|
+
if (!isCommand) return;
|
|
3902
|
+
const command = this.commands.get(commandName);
|
|
3903
|
+
if (!command) {
|
|
3904
|
+
const nearestMatches = Array.from(this.commands.keys()).filter(
|
|
3905
|
+
(cmd) => cmd.toLowerCase().includes(commandName.toLowerCase())
|
|
3906
|
+
);
|
|
3907
|
+
return this.logger.error(
|
|
3908
|
+
`Unknown command '${commandName}'${nearestMatches.length ? `. Did you mean ${nearestMatches.length > 1 ? `[${nearestMatches.map((m) => `'${this.options.prefix}${m}'`).join(", ")}]` : `'${this.options.prefix}${nearestMatches[0]}'`}?` : ""}`
|
|
3909
|
+
);
|
|
3910
|
+
}
|
|
3911
|
+
command.fn(args, content);
|
|
3912
|
+
});
|
|
3913
|
+
}
|
|
3914
|
+
parseLine(line) {
|
|
3915
|
+
if (line.startsWith(this.options.prefix)) {
|
|
3916
|
+
line = line.slice(this.options.prefix.length);
|
|
3917
|
+
} else {
|
|
3918
|
+
return { isCommand: false };
|
|
3919
|
+
}
|
|
3920
|
+
const args = line.split(" ").map((s) => s.trim());
|
|
3921
|
+
const commandName = args.shift();
|
|
3922
|
+
return { isCommand: true, commandName, content: args.join(" "), args };
|
|
3923
|
+
}
|
|
3924
|
+
getClientInstance(line) {
|
|
3925
|
+
const clientIndex = jsTools2.getFlagSubstring(line, "--client", 1) || jsTools2.getFlagSubstring(line, "-c", 1);
|
|
3926
|
+
if (clientIndex) {
|
|
3927
|
+
const idx = Number(clientIndex);
|
|
3928
|
+
if (isNaN(idx)) {
|
|
3929
|
+
CLI.logger.error(`'${clientIndex}' is not a valid number`);
|
|
3930
|
+
return void 0;
|
|
3931
|
+
}
|
|
3932
|
+
const client = clientInstances[idx];
|
|
3933
|
+
if (!client) {
|
|
3934
|
+
CLI.logger.error("Client instance not found");
|
|
3935
|
+
return void 0;
|
|
3936
|
+
}
|
|
3937
|
+
return client;
|
|
3938
|
+
} else {
|
|
3939
|
+
const client = clientInstances[0];
|
|
3940
|
+
if (!client) {
|
|
3941
|
+
CLI.logger.error("Client instance not found");
|
|
3942
|
+
return void 0;
|
|
3943
|
+
}
|
|
3944
|
+
return client;
|
|
3945
|
+
}
|
|
3946
|
+
}
|
|
3947
|
+
addCommand(commandName, description, fn) {
|
|
3948
|
+
this.commands.set(commandName, { description, fn });
|
|
3949
|
+
}
|
|
3950
|
+
removeCommand(commandName) {
|
|
3951
|
+
if (!this.commands.has(commandName)) return false;
|
|
3952
|
+
this.commands.delete(commandName);
|
|
3953
|
+
return true;
|
|
3954
|
+
}
|
|
3955
|
+
};
|
|
3956
|
+
var initCalled = false;
|
|
3957
|
+
var CLI = new VimcordCLI({ prefix: "/" });
|
|
3958
|
+
CLI.addCommand("help", "View information about a command, or the available CLI options", (args) => {
|
|
3959
|
+
const prefix = CLI.options.prefix;
|
|
3960
|
+
const helpList = {};
|
|
3961
|
+
for (const cmd of CLI.commands.entries()) {
|
|
3962
|
+
const commandName = cmd[0];
|
|
3963
|
+
const commandDescription = cmd[1].description;
|
|
3964
|
+
helpList[`${prefix}${commandName}`] = `~ ${commandDescription}`;
|
|
3965
|
+
}
|
|
3966
|
+
CLI.logger.table("(help)", helpList);
|
|
3967
|
+
});
|
|
3968
|
+
CLI.addCommand("register", "Register app commands globally, or per guild", async (args, content) => {
|
|
3969
|
+
const client = CLI.getClientInstance(content);
|
|
3970
|
+
if (!client) return;
|
|
3971
|
+
const mode = args[0]?.toLowerCase() || "";
|
|
3972
|
+
if (!["guild", "global"].includes(mode)) {
|
|
3973
|
+
return CLI.logger.error(`'${mode}' is not a valid option. Your options are [guild|global]`);
|
|
3974
|
+
}
|
|
3975
|
+
let guildIds = (jsTools2.getFlagSubstring(content, "--guilds", 1) || jsTools2.getFlagSubstring(content, "-g", 1) || "").replaceAll(/["']/g, "").split(" ").filter(Boolean).map((s) => s.replaceAll(",", "").trim());
|
|
3976
|
+
if (!guildIds.length) guildIds = client.guilds.cache.map((g) => g.id);
|
|
3977
|
+
switch (mode) {
|
|
3978
|
+
case "guild":
|
|
3979
|
+
await client.commands.slash.registerGuild({ guilds: guildIds });
|
|
3980
|
+
break;
|
|
3981
|
+
case "global":
|
|
3982
|
+
await client.commands.slash.registerGlobal();
|
|
3983
|
+
break;
|
|
3984
|
+
}
|
|
3985
|
+
});
|
|
3986
|
+
CLI.addCommand("unregister", "Unregister app commands globally, or per guild", async (args, content) => {
|
|
3987
|
+
const client = CLI.getClientInstance(content);
|
|
3988
|
+
if (!client) return;
|
|
3989
|
+
const mode = args[0]?.toLowerCase() || "";
|
|
3990
|
+
if (!["guild", "global"].includes(mode)) {
|
|
3991
|
+
return CLI.logger.error(`'${mode}' is not a valid option. Your options are [guild|global]`);
|
|
3992
|
+
}
|
|
3993
|
+
let guildIds = (jsTools2.getFlagSubstring(content, "--guilds", 1) || jsTools2.getFlagSubstring(content, "-g", 1) || "").replaceAll(/["']/g, "").split(" ").filter(Boolean).map((s) => s.replaceAll(",", "").trim());
|
|
3994
|
+
if (!guildIds.length) guildIds = client.guilds.cache.map((g) => g.id);
|
|
3995
|
+
switch (mode) {
|
|
3996
|
+
case "guild":
|
|
3997
|
+
await client.commands.slash.unregisterGuild({ guilds: guildIds });
|
|
3998
|
+
break;
|
|
3999
|
+
case "global":
|
|
4000
|
+
await client.commands.slash.unregisterGlobal();
|
|
4001
|
+
break;
|
|
4002
|
+
}
|
|
4003
|
+
});
|
|
4004
|
+
CLI.addCommand("stats", "View statistics about a client instance", (args, content) => {
|
|
4005
|
+
const client = CLI.getClientInstance(content);
|
|
4006
|
+
if (!client) return;
|
|
4007
|
+
CLI.logger.table(`(stats) ~ ${client.config.app.name}`, {
|
|
4008
|
+
"Guilds:": jsTools2.formatThousands(client.guilds.cache.size),
|
|
4009
|
+
"Ping:": `${client.ws.ping || 0}ms`,
|
|
4010
|
+
"Uptime:": `${jsTools2.msToSec(client.uptime || 0)}s`,
|
|
4011
|
+
"Process Uptime:": `${Math.floor(process.uptime())}s`,
|
|
4012
|
+
"Memory Usage:": `${(process.memoryUsage().rss / 1024 / 1024).toFixed(2)} MB`
|
|
4013
|
+
});
|
|
4014
|
+
});
|
|
4015
|
+
CLI.addCommand("cmds", "List the loaded commands", (args, content) => {
|
|
4016
|
+
const client = CLI.getClientInstance(content);
|
|
4017
|
+
if (!client) return;
|
|
4018
|
+
const mode = args[0] || "slash";
|
|
4019
|
+
switch (mode) {
|
|
4020
|
+
case "slash":
|
|
4021
|
+
const slashCommands = Array.from(client.commands.slash.commands.values());
|
|
4022
|
+
slashCommands.sort((a, b) => a.builder.name.localeCompare(b.builder.name));
|
|
4023
|
+
const slashCommands_f = {};
|
|
4024
|
+
for (const cmd of slashCommands) {
|
|
4025
|
+
slashCommands_f[`/${cmd.builder.name}`] = `~ ${cmd.builder.description || "No description"}`;
|
|
4026
|
+
}
|
|
4027
|
+
return CLI.logger.table(
|
|
4028
|
+
`(cmds) ~ slash (${jsTools2.formatThousands(client.commands.slash.commands.size)})`,
|
|
4029
|
+
slashCommands_f
|
|
4030
|
+
);
|
|
4031
|
+
case "prefix":
|
|
4032
|
+
const prefixCommands = Array.from(client.commands.prefix.commands.values());
|
|
4033
|
+
prefixCommands.sort((a, b) => a.name.localeCompare(b.name));
|
|
4034
|
+
const prefixCommands_f = {};
|
|
4035
|
+
for (const cmd of prefixCommands) {
|
|
4036
|
+
prefixCommands_f[`${client.config.prefixCommands.defaultPrefix}${cmd.name}`] = `~ ${cmd.description || "No description"}`;
|
|
4037
|
+
}
|
|
4038
|
+
return CLI.logger.table(
|
|
4039
|
+
`(cmds) ~ prefix (${jsTools2.formatThousands(client.commands.prefix.commands.size)})`,
|
|
4040
|
+
prefixCommands_f
|
|
4041
|
+
);
|
|
4042
|
+
case "ctx":
|
|
4043
|
+
const contextCommands = Array.from(client.commands.context.commands.values());
|
|
4044
|
+
contextCommands.sort((a, b) => a.builder.name.localeCompare(b.builder.name));
|
|
4045
|
+
const contextCommands_f = {};
|
|
4046
|
+
for (const cmd of contextCommands) {
|
|
4047
|
+
contextCommands_f[`${cmd.builder.name}`] = "";
|
|
4048
|
+
}
|
|
4049
|
+
return CLI.logger.table(
|
|
4050
|
+
`(cmds) ~ ctx (${jsTools2.formatThousands(client.commands.context.commands.size)})`,
|
|
4051
|
+
contextCommands_f
|
|
4052
|
+
);
|
|
4053
|
+
default:
|
|
4054
|
+
return CLI.logger.error(`'${mode}' is not a valid option. Your options are [slash|prefix|ctx]`);
|
|
4055
|
+
}
|
|
4056
|
+
});
|
|
4057
|
+
function initCLI() {
|
|
4058
|
+
if (initCalled) return;
|
|
4059
|
+
CLI.logger.log(`~ Type ${CLI.options.prefix}help to view available commands`);
|
|
4060
|
+
initCalled = true;
|
|
4061
|
+
}
|
|
4062
|
+
|
|
4063
|
+
// src/utils/clientUtils.ts
|
|
4064
|
+
function useClient(index = 0) {
|
|
4065
|
+
return clientInstances.at(index);
|
|
4066
|
+
}
|
|
4067
|
+
async function useReadyClient(index = 0) {
|
|
4068
|
+
return useClient(index)?.whenReady();
|
|
4069
|
+
}
|
|
4070
|
+
function createClient(options, features = {}, config = {}) {
|
|
4071
|
+
const client = new Vimcord(options, features, config);
|
|
4072
|
+
initCLI();
|
|
4073
|
+
return client;
|
|
4074
|
+
}
|
|
4075
|
+
function getClientInstances() {
|
|
4076
|
+
return clientInstances;
|
|
4077
|
+
}
|
|
4078
|
+
export {
|
|
4079
|
+
BaseCommandBuilder,
|
|
4080
|
+
BetterContainer,
|
|
4081
|
+
BetterEmbed,
|
|
4082
|
+
BetterModal,
|
|
4083
|
+
CLI,
|
|
4084
|
+
CommandType,
|
|
4085
|
+
ContextCommandBuilder,
|
|
4086
|
+
DynaSend,
|
|
4087
|
+
EventBuilder,
|
|
4088
|
+
LogLevel,
|
|
4089
|
+
Logger,
|
|
4090
|
+
MissingPermissionReason,
|
|
4091
|
+
MongoDatabase,
|
|
4092
|
+
MongoSchemaBuilder,
|
|
4093
|
+
PaginationTimeoutType,
|
|
4094
|
+
PaginationType,
|
|
4095
|
+
Paginator,
|
|
4096
|
+
PrefixCommandBuilder,
|
|
4097
|
+
Prompt,
|
|
4098
|
+
PromptResolveType,
|
|
4099
|
+
RateLimitScope,
|
|
4100
|
+
SendMethod,
|
|
4101
|
+
SlashCommandBuilder,
|
|
4102
|
+
StatusType,
|
|
4103
|
+
Vimcord,
|
|
4104
|
+
VimcordCLI,
|
|
4105
|
+
VimcordCommandManager,
|
|
4106
|
+
VimcordContextCommandManager,
|
|
4107
|
+
VimcordEventManager,
|
|
4108
|
+
VimcordPrefixCommandManager,
|
|
4109
|
+
VimcordSlashCommandManager,
|
|
4110
|
+
VimcordStatusManager,
|
|
4111
|
+
__zero,
|
|
4112
|
+
cleanMention,
|
|
4113
|
+
clientInstances,
|
|
4114
|
+
createClient,
|
|
4115
|
+
createMongoSchema,
|
|
4116
|
+
createToolsConfig,
|
|
4117
|
+
createVimcordAppConfig,
|
|
4118
|
+
createVimcordContextCommandConfig,
|
|
4119
|
+
createVimcordPrefixCommandConfig,
|
|
4120
|
+
createVimcordSlashCommandConfig,
|
|
4121
|
+
createVimcordStaffConfig,
|
|
4122
|
+
createVimcordStatusConfig,
|
|
4123
|
+
defineGlobalToolsConfig,
|
|
4124
|
+
dynaSend,
|
|
4125
|
+
fetchChannel,
|
|
4126
|
+
fetchGuild,
|
|
4127
|
+
fetchMember,
|
|
4128
|
+
fetchMessage,
|
|
4129
|
+
fetchRole,
|
|
4130
|
+
fetchUser,
|
|
4131
|
+
formatThousands,
|
|
4132
|
+
getCallerFileName,
|
|
4133
|
+
getClientInstances,
|
|
4134
|
+
getFirstMentionId,
|
|
4135
|
+
getMessageMention,
|
|
4136
|
+
getProcessDir,
|
|
4137
|
+
globalVimcordToolsConfig,
|
|
4138
|
+
importModulesFromDir,
|
|
4139
|
+
initCLI,
|
|
4140
|
+
isMentionOrSnowflake,
|
|
4141
|
+
logger,
|
|
4142
|
+
pickRandom,
|
|
4143
|
+
prompt,
|
|
4144
|
+
retryExponentialBackoff,
|
|
4145
|
+
sendCommandErrorEmbed,
|
|
4146
|
+
useClient,
|
|
4147
|
+
useMongoDatabase,
|
|
4148
|
+
useReadyClient,
|
|
4149
|
+
validateCommandPermissions
|
|
4150
|
+
};
|
|
4151
|
+
//# sourceMappingURL=index.js.map
|