djs-next 1.0.0-dev.1 → 1.0.0-dev.3
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.
Potentially problematic release.
This version of djs-next might be problematic. Click here for more details.
- package/README.md +61 -30
- package/bin/create-djs-next.js +78 -0
- package/dist/index.d.mts +57 -5
- package/dist/index.d.ts +57 -5
- package/dist/index.js +620 -256
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +620 -246
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +277 -68
- package/src/index.ts +4 -1
- package/src/plugins/dnxt.ts +215 -176
- package/src/templates/cjs.ts +11 -6
- package/src/templates/esm.ts +11 -4
- package/src/templates/ts.ts +11 -5
- package/src/types.ts +27 -2
- package/src/utils/PaginationBuilder.ts +94 -0
- package/src/utils/paginate.ts +32 -13
- package/src/utils/prompts.ts +76 -0
- package/test_payload.js +3 -0
- package/test_reply.js +4 -0
- package/tsup.config.ts +1 -1
- package/dist/cli.d.mts +0 -1
- package/dist/cli.d.ts +0 -1
- package/dist/cli.js +0 -344
- package/dist/cli.js.map +0 -1
- package/dist/cli.mjs +0 -321
- package/dist/cli.mjs.map +0 -1
- package/src/cli.ts +0 -203
package/dist/index.mjs
CHANGED
|
@@ -5,6 +5,9 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
5
5
|
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
6
|
});
|
|
7
7
|
|
|
8
|
+
// src/index.ts
|
|
9
|
+
import "dotenv/config";
|
|
10
|
+
|
|
8
11
|
// src/client.ts
|
|
9
12
|
import { Client, Collection as Collection3 } from "discord.js";
|
|
10
13
|
|
|
@@ -252,9 +255,9 @@ function loadLocales(localesDir, defaultLocale) {
|
|
|
252
255
|
for (const file of files) {
|
|
253
256
|
if (file.endsWith(".json")) {
|
|
254
257
|
const lang = file.replace(".json", "");
|
|
255
|
-
const
|
|
258
|
+
const content2 = fs7.readFileSync(path5.join(localesDir, file), "utf8");
|
|
256
259
|
try {
|
|
257
|
-
localesCache[lang] = JSON.parse(
|
|
260
|
+
localesCache[lang] = JSON.parse(content2);
|
|
258
261
|
} catch (e) {
|
|
259
262
|
console.error(`[djs-next] Failed to parse locale file: ${file}`, e);
|
|
260
263
|
}
|
|
@@ -288,26 +291,92 @@ function getLocalesCache() {
|
|
|
288
291
|
}
|
|
289
292
|
|
|
290
293
|
// src/plugins/dnxt.ts
|
|
294
|
+
import { version as djsVersion } from "discord.js";
|
|
291
295
|
import { exec } from "child_process";
|
|
292
296
|
import util from "util";
|
|
293
297
|
import os from "os";
|
|
294
298
|
var execAsync = util.promisify(exec);
|
|
295
|
-
|
|
299
|
+
function buildDisplayMessage(content2, color = 5397233) {
|
|
300
|
+
const djs = __require("discord.js");
|
|
301
|
+
const container = new djs.ContainerBuilder().setAccentColor(color).addTextDisplayComponents(
|
|
302
|
+
new djs.TextDisplayBuilder().setContent(content2)
|
|
303
|
+
);
|
|
304
|
+
return { components: [container], flags: 32768 };
|
|
305
|
+
}
|
|
306
|
+
async function handleDNXT(message, client, devPrefix) {
|
|
296
307
|
if (message.author.bot) return;
|
|
297
|
-
if (!client
|
|
298
|
-
if (
|
|
299
|
-
const
|
|
308
|
+
if (!client._developers.includes(message.author.id)) return;
|
|
309
|
+
if (client.config?.devGuildId && message.guildId !== client.config.devGuildId) return;
|
|
310
|
+
const originalReply = message.reply.bind(message);
|
|
311
|
+
message.reply = (async (content2) => {
|
|
312
|
+
if (typeof content2 === "string") {
|
|
313
|
+
return await originalReply({ ...buildDisplayMessage(content2), allowedMentions: { repliedUser: false } });
|
|
314
|
+
}
|
|
315
|
+
if (!content2.allowedMentions) content2.allowedMentions = { repliedUser: false };
|
|
316
|
+
return await originalReply(content2);
|
|
317
|
+
});
|
|
318
|
+
const content = message.content.trim();
|
|
319
|
+
const validTriggers = [devPrefix];
|
|
320
|
+
const clientPrefixes = client._prefixes || [];
|
|
321
|
+
for (const p of clientPrefixes) {
|
|
322
|
+
if (p !== "") validTriggers.push(`${p}${devPrefix}`);
|
|
323
|
+
}
|
|
324
|
+
let matchedTrigger = validTriggers.find((t) => content === t || content.startsWith(`${t} `));
|
|
325
|
+
if (!matchedTrigger) return;
|
|
326
|
+
const args = content.slice(matchedTrigger.length).trim().split(/ +/g);
|
|
300
327
|
const command = args.shift()?.toLowerCase();
|
|
328
|
+
if (command === "help") {
|
|
329
|
+
const djs = __require("discord.js");
|
|
330
|
+
const container = new djs.ContainerBuilder().setAccentColor(5397233).addTextDisplayComponents(
|
|
331
|
+
new djs.TextDisplayBuilder().setContent(`# \u{1F4D6} DNXT Toolkit Reference
|
|
332
|
+
> Current prefix trigger: \`${matchedTrigger}\``)
|
|
333
|
+
).addSeparatorComponents(new djs.SeparatorBuilder()).addTextDisplayComponents(
|
|
334
|
+
new djs.TextDisplayBuilder().setContent(`### \u{1F4CA} Core Framework
|
|
335
|
+
- \`${matchedTrigger}\` \u2014 Developer system dashboard
|
|
336
|
+
- \`${matchedTrigger} help\` \u2014 Shows this reference menu`),
|
|
337
|
+
new djs.TextDisplayBuilder().setContent(`### \u{1F4BB} Execution & Diagnostics
|
|
338
|
+
- \`${matchedTrigger} js <code>\` \u2014 Evaluates raw JS code
|
|
339
|
+
- \`${matchedTrigger} sh <cmd>\` \u2014 Runs terminal shell script
|
|
340
|
+
- \`${matchedTrigger} debug <code>\` \u2014 Evaluates JS with precise memory deltas`),
|
|
341
|
+
new djs.TextDisplayBuilder().setContent(`### \u{1F4C2} File System & Network
|
|
342
|
+
- \`${matchedTrigger} cat <file>\` \u2014 Reads file contents
|
|
343
|
+
- \`${matchedTrigger} curl <url>\` \u2014 Fetches remote URL data
|
|
344
|
+
- \`${matchedTrigger} git <cmd>\` \u2014 Executes git repository commands`),
|
|
345
|
+
new djs.TextDisplayBuilder().setContent(`### \u{1F9F0} Utilities
|
|
346
|
+
- \`${matchedTrigger} <load|unload|reload> <target>\` \u2014 Manages system modules
|
|
347
|
+
- \`${matchedTrigger} sync\` \u2014 Forces global slash command sync
|
|
348
|
+
- \`${matchedTrigger} in <channel> <cmd>\` \u2014 Executes command in target channel
|
|
349
|
+
- \`${matchedTrigger} restart\` \u2014 Restarts the bot completely
|
|
350
|
+
- \`${matchedTrigger} shutdown\` \u2014 Stops the bot completely`)
|
|
351
|
+
);
|
|
352
|
+
await message.reply({ components: [container], flags: 32768, allowedMentions: { repliedUser: false } });
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
301
355
|
if (!command) {
|
|
302
356
|
const mem = process.memoryUsage();
|
|
303
|
-
const discordJsVersion = __require("discord.js/package.json").version;
|
|
304
357
|
const botPing = client.ws.ping;
|
|
305
|
-
const
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
358
|
+
const djs = __require("discord.js");
|
|
359
|
+
const container = new djs.ContainerBuilder().setAccentColor(5397233).addTextDisplayComponents(
|
|
360
|
+
new djs.TextDisplayBuilder().setContent(`# \u{1F6E0}\uFE0F Developer System Dashboard
|
|
361
|
+
> **DNXT Framework Engine**`)
|
|
362
|
+
).addSeparatorComponents(new djs.SeparatorBuilder()).addTextDisplayComponents(
|
|
363
|
+
new djs.TextDisplayBuilder().setContent(`### \u{1F4E1} Network Status
|
|
364
|
+
- **System Uptime:** <t:${Math.floor((Date.now() - client.uptime) / 1e3)}:R>
|
|
365
|
+
- **WebSocket Latency:** \`${botPing}ms\``),
|
|
366
|
+
new djs.TextDisplayBuilder().setContent(`### \u{1F4E6} Host Environment
|
|
367
|
+
- **Discord.js Library:** \`v${djsVersion}\`
|
|
368
|
+
- **Node.js Runtime:** \`${process.version}\`
|
|
369
|
+
- **Operating System:** \`${os.type()}\` (\`${os.cpus().length}\` thread cores, \`${(os.uptime() / 60 / 60).toFixed(2)}\` hrs uptime)`),
|
|
370
|
+
new djs.TextDisplayBuilder().setContent(`### \u{1F9E0} Resource Utilization
|
|
371
|
+
- **Physical Memory:** \`${(mem.rss / 1024 / 1024).toFixed(2)} MB\`
|
|
372
|
+
- **Heap Allocated:** \`${(mem.heapUsed / 1024 / 1024).toFixed(2)} MB\``)
|
|
373
|
+
);
|
|
374
|
+
await message.reply({
|
|
375
|
+
components: [container],
|
|
376
|
+
flags: 32768,
|
|
377
|
+
// MessageFlags.IsComponentsV2
|
|
378
|
+
allowedMentions: { repliedUser: false }
|
|
379
|
+
});
|
|
311
380
|
return;
|
|
312
381
|
}
|
|
313
382
|
if (command === "js" || command === "eval" || command === "py") {
|
|
@@ -321,12 +390,12 @@ System: \`${os.cpus().length}\` thread(s), \`${(os.uptime() / 60 / 60).toFixed(2
|
|
|
321
390
|
let evaled = await eval(`(async () => { ${code} })()`);
|
|
322
391
|
const end = process.hrtime.bigint();
|
|
323
392
|
const timeMs = Number(end - start) / 1e6;
|
|
324
|
-
if (typeof evaled !== "string") evaled = util.inspect(evaled, { depth: 1 });
|
|
393
|
+
if (typeof evaled !== "string") evaled = util.inspect(evaled, { depth: 1, colors: true });
|
|
325
394
|
await sendPaginatedText(message, `\u2705 **Evaluated in ${timeMs.toFixed(3)}ms**
|
|
326
|
-
`, evaled, "
|
|
395
|
+
`, evaled, "ansi");
|
|
327
396
|
} catch (err) {
|
|
328
397
|
await sendPaginatedText(message, `\u274C **Evaluation Error**
|
|
329
|
-
`, err.message, "
|
|
398
|
+
`, err.message, "ansi");
|
|
330
399
|
}
|
|
331
400
|
return;
|
|
332
401
|
}
|
|
@@ -335,52 +404,74 @@ System: \`${os.cpus().length}\` thread(s), \`${(os.uptime() / 60 / 60).toFixed(2
|
|
|
335
404
|
if (!cmd) return void await message.reply("\u274C Please provide a command to execute.");
|
|
336
405
|
try {
|
|
337
406
|
const start2 = process.hrtime.bigint();
|
|
338
|
-
|
|
407
|
+
let stdout = "", stderr = "", code2 = 0;
|
|
408
|
+
try {
|
|
409
|
+
const result = await execAsync(cmd);
|
|
410
|
+
stdout = result.stdout;
|
|
411
|
+
stderr = result.stderr;
|
|
412
|
+
} catch (err) {
|
|
413
|
+
stdout = err.stdout || "";
|
|
414
|
+
stderr = err.stderr || err.message || "";
|
|
415
|
+
code2 = err.code || 1;
|
|
416
|
+
}
|
|
339
417
|
const end2 = process.hrtime.bigint();
|
|
340
418
|
const timeMs2 = Number(end2 - start2) / 1e6;
|
|
341
|
-
|
|
419
|
+
let resultText = `$ ${cmd}
|
|
420
|
+
`;
|
|
421
|
+
if (stdout) resultText += `${stdout}
|
|
422
|
+
`;
|
|
423
|
+
if (stderr) resultText += `${stderr}
|
|
424
|
+
`;
|
|
425
|
+
resultText += `
|
|
426
|
+
[status] Return code ${code2}`;
|
|
342
427
|
await sendPaginatedText(message, `\u2705 **Executed in ${timeMs2.toFixed(3)}ms**
|
|
343
|
-
`,
|
|
428
|
+
`, resultText, "ansi");
|
|
344
429
|
} catch (err) {
|
|
345
430
|
await sendPaginatedText(message, `\u274C **Shell Error**
|
|
346
|
-
`, err.message, "
|
|
431
|
+
`, err.message, "ansi");
|
|
347
432
|
}
|
|
348
433
|
return;
|
|
349
434
|
}
|
|
350
|
-
if (command === "
|
|
435
|
+
if (command === "load" || command === "unload" || command === "reload") {
|
|
351
436
|
const target = args[0]?.toLowerCase();
|
|
352
437
|
try {
|
|
353
438
|
if (target === "commands" && client["_commandsDir"]) {
|
|
354
|
-
client.commands.clear();
|
|
355
|
-
client.commands = await loadAndDeployCommands(client["_commandsDir"], client.token, client["_clientId"], client["_guildId"]);
|
|
356
|
-
await message.reply(
|
|
439
|
+
if (command === "unload" || command === "reload") client.commands.clear();
|
|
440
|
+
if (command === "load" || command === "reload") client.commands = await loadAndDeployCommands(client["_commandsDir"], client.token, client["_clientId"], client["_guildId"]);
|
|
441
|
+
await message.reply(`\u2705 Successfully ${command}ed commands.`);
|
|
357
442
|
} else if (target === "events" && client["_eventsDir"]) {
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
443
|
+
if (command === "unload" || command === "reload") {
|
|
444
|
+
client.removeAllListeners();
|
|
445
|
+
client["attachCoreListeners"]();
|
|
446
|
+
}
|
|
447
|
+
if (command === "load" || command === "reload") await loadEvents(client, client["_eventsDir"]);
|
|
448
|
+
await message.reply(`\u2705 Successfully ${command}ed events.`);
|
|
362
449
|
} else if (target === "components" && client["_componentsDir"]) {
|
|
363
|
-
client.components.clear();
|
|
364
|
-
client.components = await loadComponents(client["_componentsDir"]);
|
|
365
|
-
await message.reply(
|
|
450
|
+
if (command === "unload" || command === "reload") client.components.clear();
|
|
451
|
+
if (command === "load" || command === "reload") client.components = await loadComponents(client["_componentsDir"]);
|
|
452
|
+
await message.reply(`\u2705 Successfully ${command}ed components.`);
|
|
366
453
|
} else if (target === "locales" && client["_localesDir"]) {
|
|
367
|
-
loadLocales(client["_localesDir"], client.config.defaultLocale);
|
|
368
|
-
await message.reply(
|
|
454
|
+
if (command === "load" || command === "reload") loadLocales(client["_localesDir"], client.config.defaultLocale);
|
|
455
|
+
await message.reply(`\u2705 Successfully ${command}ed locales.`);
|
|
369
456
|
} else if (target === "all" || !target) {
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
if (
|
|
377
|
-
|
|
378
|
-
|
|
457
|
+
if (command === "unload" || command === "reload") {
|
|
458
|
+
client.removeAllListeners();
|
|
459
|
+
client["attachCoreListeners"]();
|
|
460
|
+
client.commands.clear();
|
|
461
|
+
client.components.clear();
|
|
462
|
+
}
|
|
463
|
+
if (command === "load" || command === "reload") {
|
|
464
|
+
if (client["_eventsDir"]) await loadEvents(client, client["_eventsDir"]);
|
|
465
|
+
if (client["_componentsDir"]) client.components = await loadComponents(client["_componentsDir"]);
|
|
466
|
+
if (client["_localesDir"]) loadLocales(client["_localesDir"], client.config.defaultLocale);
|
|
467
|
+
if (client["_commandsDir"]) client.commands = await loadAndDeployCommands(client["_commandsDir"], client.token, client["_clientId"], client["_guildId"]);
|
|
468
|
+
}
|
|
469
|
+
await message.reply(`\u2705 Successfully ${command}ed all framework modules.`);
|
|
379
470
|
} else {
|
|
380
471
|
await message.reply("\u274C Unknown target. Valid targets: `commands, events, components, locales, all`");
|
|
381
472
|
}
|
|
382
473
|
} catch (err) {
|
|
383
|
-
await message.reply(`\u274C
|
|
474
|
+
await message.reply(`\u274C **${command.toUpperCase()} Error:** ${err.message}`);
|
|
384
475
|
}
|
|
385
476
|
return;
|
|
386
477
|
}
|
|
@@ -390,49 +481,14 @@ System: \`${os.cpus().length}\` thread(s), \`${(os.uptime() / 60 / 60).toFixed(2
|
|
|
390
481
|
const file = args.join(" ");
|
|
391
482
|
if (!file) return void await message.reply("\u274C Please provide a file to read.");
|
|
392
483
|
try {
|
|
393
|
-
const
|
|
484
|
+
const content2 = fs9.readFileSync(path7.resolve(process.cwd(), file), "utf8");
|
|
394
485
|
await sendPaginatedText(message, `\u{1F4C4} **${file}**
|
|
395
|
-
`,
|
|
486
|
+
`, content2, file.split(".").pop() || "");
|
|
396
487
|
} catch (e) {
|
|
397
488
|
await message.reply(`\u274C **Error reading file:** ${e.message}`);
|
|
398
489
|
}
|
|
399
490
|
return;
|
|
400
491
|
}
|
|
401
|
-
if (command === "su" || command === "sudo") {
|
|
402
|
-
const targetUserId = args.shift();
|
|
403
|
-
const cmd = args.join(" ");
|
|
404
|
-
if (!targetUserId || !cmd) return void await message.reply(`\u274C Usage: ${prefix} su <user_id> <command>`);
|
|
405
|
-
try {
|
|
406
|
-
const targetUser = await client.users.fetch(targetUserId.replace(/<@!?|>/g, ""));
|
|
407
|
-
if (!targetUser) throw new Error("User not found.");
|
|
408
|
-
const mockMessage = Object.assign(Object.create(Object.getPrototypeOf(message)), message);
|
|
409
|
-
mockMessage.author = targetUser;
|
|
410
|
-
if (message.guild) {
|
|
411
|
-
mockMessage.member = await message.guild.members.fetch(targetUser.id).catch(() => null);
|
|
412
|
-
}
|
|
413
|
-
mockMessage.content = cmd;
|
|
414
|
-
client.emit("messageCreate", mockMessage);
|
|
415
|
-
await message.reply(`\u2705 Invoked \`${cmd}\` as **${targetUser.tag}**.`);
|
|
416
|
-
} catch (e) {
|
|
417
|
-
await message.reply(`\u274C **Sudo Error:** ${e.message}`);
|
|
418
|
-
}
|
|
419
|
-
return;
|
|
420
|
-
}
|
|
421
|
-
if (command === "source" || command === "src") {
|
|
422
|
-
const target = args.join(" ");
|
|
423
|
-
if (!target) return void await message.reply("\u274C Please provide a command name.");
|
|
424
|
-
const cmdData = client.commands.get(target);
|
|
425
|
-
if (!cmdData || !cmdData.filepath) return void await message.reply("\u274C Command not found or has no associated filepath.");
|
|
426
|
-
try {
|
|
427
|
-
const fs9 = await import("fs");
|
|
428
|
-
const content = fs9.readFileSync(cmdData.filepath, "utf8");
|
|
429
|
-
await sendPaginatedText(message, `\u{1F4C4} **Source of \`${target}\`**
|
|
430
|
-
`, content, cmdData.filepath.split(".").pop() || "");
|
|
431
|
-
} catch (e) {
|
|
432
|
-
await message.reply(`\u274C **Error reading source:** ${e.message}`);
|
|
433
|
-
}
|
|
434
|
-
return;
|
|
435
|
-
}
|
|
436
492
|
if (command === "curl") {
|
|
437
493
|
const url = args[0];
|
|
438
494
|
if (!url) return void await message.reply("\u274C Please provide a URL.");
|
|
@@ -460,64 +516,38 @@ System: \`${os.cpus().length}\` thread(s), \`${(os.uptime() / 60 / 60).toFixed(2
|
|
|
460
516
|
const endMem = process.memoryUsage().heapUsed;
|
|
461
517
|
const timeMs = Number(end - start) / 1e6;
|
|
462
518
|
const memDiff = (endMem - startMem) / 1024 / 1024;
|
|
463
|
-
if (typeof evaled !== "string") evaled = util.inspect(evaled, { depth: 1 });
|
|
519
|
+
if (typeof evaled !== "string") evaled = util.inspect(evaled, { depth: 1, colors: true });
|
|
464
520
|
await sendPaginatedText(message, `\u23F1\uFE0F **Debug Execution**
|
|
465
521
|
Time: \`${timeMs.toFixed(3)}ms\` | Heap Delta: \`${memDiff.toFixed(3)}MB\`
|
|
466
|
-
`, evaled, "
|
|
522
|
+
`, evaled, "ansi");
|
|
467
523
|
} catch (err) {
|
|
468
524
|
await sendPaginatedText(message, `\u274C **Debug Error**
|
|
469
|
-
`, err.message, "
|
|
525
|
+
`, err.message, "ansi");
|
|
470
526
|
}
|
|
471
527
|
return;
|
|
472
528
|
}
|
|
473
529
|
if (command === "in") {
|
|
474
530
|
const channelId = args.shift()?.replace(/<#|>/g, "");
|
|
475
531
|
const cmd = args.join(" ");
|
|
476
|
-
if (!channelId || !cmd) return void await message.reply(`\u274C Usage: ${
|
|
532
|
+
if (!channelId || !cmd) return void await message.reply(`\u274C Usage: ${matchedTrigger} in <#channel|id> <command>`);
|
|
477
533
|
try {
|
|
478
534
|
const targetChannel = await client.channels.fetch(channelId);
|
|
479
535
|
if (!targetChannel || !targetChannel.isTextBased()) throw new Error("Invalid Text Channel.");
|
|
480
536
|
const mockMessage = Object.assign(Object.create(Object.getPrototypeOf(message)), message);
|
|
481
|
-
mockMessage
|
|
482
|
-
|
|
483
|
-
|
|
537
|
+
Object.defineProperty(mockMessage, "client", { value: client, configurable: true });
|
|
538
|
+
if (message.guild) {
|
|
539
|
+
Object.defineProperty(mockMessage, "guild", { value: message.guild, configurable: true });
|
|
540
|
+
Object.defineProperty(mockMessage, "guildId", { value: message.guildId, configurable: true });
|
|
541
|
+
}
|
|
542
|
+
Object.defineProperty(mockMessage, "channel", { value: targetChannel, configurable: true });
|
|
543
|
+
Object.defineProperty(mockMessage, "channelId", { value: targetChannel.id, configurable: true });
|
|
544
|
+
Object.defineProperty(mockMessage, "content", { value: cmd, configurable: true });
|
|
484
545
|
client.emit("messageCreate", mockMessage);
|
|
485
|
-
await message.reply(`\u2705 Redirected execution to <#${targetChannel.id}>.`);
|
|
486
546
|
} catch (e) {
|
|
487
547
|
await message.reply(`\u274C **In Error:** ${e.message}`);
|
|
488
548
|
}
|
|
489
549
|
return;
|
|
490
550
|
}
|
|
491
|
-
if (command === "tasks") {
|
|
492
|
-
const tasks = client._activeTasks;
|
|
493
|
-
if (!tasks || tasks.size === 0) return void await message.reply("No active background tasks running.");
|
|
494
|
-
let text = `\u2699\uFE0F **Active Background Tasks (${tasks.size})**
|
|
495
|
-
`;
|
|
496
|
-
for (const [name] of tasks.entries()) {
|
|
497
|
-
text += `- \`${name.split("/").pop()}\`
|
|
498
|
-
`;
|
|
499
|
-
}
|
|
500
|
-
await sendPaginatedText(message, "", text, "");
|
|
501
|
-
return;
|
|
502
|
-
}
|
|
503
|
-
if (command === "cancel") {
|
|
504
|
-
const target = args.join(" ");
|
|
505
|
-
if (!target) return void await message.reply(`\u274C Usage: ${prefix} cancel <task_name>`);
|
|
506
|
-
const tasks = client._activeTasks;
|
|
507
|
-
if (!tasks) return void await message.reply("No active tasks to cancel.");
|
|
508
|
-
let found = false;
|
|
509
|
-
for (const [name, intervalId] of tasks.entries()) {
|
|
510
|
-
if (name.includes(target)) {
|
|
511
|
-
clearInterval(intervalId);
|
|
512
|
-
tasks.delete(name);
|
|
513
|
-
found = true;
|
|
514
|
-
await message.reply(`\u2705 Cancelled background task: \`${name.split("/").pop()}\``);
|
|
515
|
-
break;
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
if (!found) await message.reply("\u274C Task not found.");
|
|
519
|
-
return;
|
|
520
|
-
}
|
|
521
551
|
if (command === "sync") {
|
|
522
552
|
try {
|
|
523
553
|
await message.reply("\u{1F504} Force syncing slash commands...");
|
|
@@ -528,80 +558,102 @@ Time: \`${timeMs.toFixed(3)}ms\` | Heap Delta: \`${memDiff.toFixed(3)}MB\`
|
|
|
528
558
|
}
|
|
529
559
|
return;
|
|
530
560
|
}
|
|
531
|
-
if (command === "
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
}
|
|
543
|
-
const inspect = util.inspect(res, { depth: 2 });
|
|
544
|
-
await sendPaginatedText(message, `\u{1F5C4}\uFE0F **SQL Query**
|
|
545
|
-
`, inspect, "js");
|
|
546
|
-
} catch (e) {
|
|
547
|
-
await message.reply(`\u274C **SQL Error:** ${e.message}`);
|
|
548
|
-
}
|
|
561
|
+
if (command === "restart") {
|
|
562
|
+
await message.reply("\u{1F504} Restarting framework...");
|
|
563
|
+
client.destroy();
|
|
564
|
+
const { spawn } = __require("child_process");
|
|
565
|
+
const child = spawn(process.argv[0], process.argv.slice(1), {
|
|
566
|
+
detached: true,
|
|
567
|
+
stdio: "ignore",
|
|
568
|
+
cwd: process.cwd()
|
|
569
|
+
});
|
|
570
|
+
child.unref();
|
|
571
|
+
process.exit(0);
|
|
549
572
|
return;
|
|
550
573
|
}
|
|
551
|
-
if (command === "
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
const text = `\u{1F399}\uFE0F **Voice Debugger**
|
|
556
|
-
Channel: <#${me.voice.channel.id}> (\`${me.voice.channel.id}\`)
|
|
557
|
-
Muted: ${me.voice.mute} | Deafened: ${me.voice.deaf}
|
|
558
|
-
Session ID: \`${me.voice.sessionId || "None"}\``;
|
|
559
|
-
await message.reply(text);
|
|
574
|
+
if (command === "shutdown" || command === "stop") {
|
|
575
|
+
await message.reply("\u{1F6D1} Shutting down framework...");
|
|
576
|
+
client.destroy();
|
|
577
|
+
process.exit(0);
|
|
560
578
|
return;
|
|
561
579
|
}
|
|
562
|
-
await message.reply(`\u2753 Unknown ${
|
|
580
|
+
await message.reply(`\u2753 Unknown ${matchedTrigger} command. Available: \`js, sh, git, cat, curl, in, debug, reload, sync, restart, shutdown\``);
|
|
563
581
|
}
|
|
564
|
-
async function sendPaginatedText(message2, header,
|
|
565
|
-
const maxLength =
|
|
566
|
-
if (
|
|
567
|
-
await message2.reply(
|
|
568
|
-
|
|
569
|
-
|
|
582
|
+
async function sendPaginatedText(message2, header, content2, language = "") {
|
|
583
|
+
const maxLength = 800;
|
|
584
|
+
if (content2.length <= maxLength) {
|
|
585
|
+
await message2.reply(buildDisplayMessage(`### ${header.replace(/\\*\\*/g, "")}
|
|
586
|
+
\`\`\`${language}
|
|
587
|
+
${content2}
|
|
588
|
+
\`\`\``));
|
|
570
589
|
return;
|
|
571
590
|
}
|
|
572
591
|
const chunks = [];
|
|
573
|
-
for (let i = 0; i <
|
|
574
|
-
chunks.push(
|
|
592
|
+
for (let i = 0; i < content2.length; i += maxLength) {
|
|
593
|
+
chunks.push(content2.substring(i, i + maxLength));
|
|
575
594
|
}
|
|
576
595
|
let index = 0;
|
|
577
|
-
const
|
|
578
|
-
|
|
596
|
+
const djs = __require("discord.js");
|
|
597
|
+
function buildPage(idx) {
|
|
598
|
+
const text = `### ${header.replace(/\\*\\*/g, "")}
|
|
599
|
+
\`\`\`${language}
|
|
600
|
+
${chunks[idx]}
|
|
579
601
|
\`\`\`
|
|
580
|
-
*Page 1 of ${chunks.length}
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
602
|
+
> *Page ${idx + 1} of ${chunks.length}*`;
|
|
603
|
+
const container = new djs.ContainerBuilder().setAccentColor(5397233).addTextDisplayComponents(
|
|
604
|
+
new djs.TextDisplayBuilder().setContent(text)
|
|
605
|
+
);
|
|
606
|
+
const row1 = new djs.ActionRowBuilder().addComponents(
|
|
607
|
+
new djs.ButtonBuilder().setCustomId("first").setLabel("\u226A").setStyle(2),
|
|
608
|
+
new djs.ButtonBuilder().setCustomId("prev").setLabel("\uFF1C").setStyle(2),
|
|
609
|
+
new djs.ButtonBuilder().setCustomId("goto").setLabel("\u2398").setStyle(1),
|
|
610
|
+
new djs.ButtonBuilder().setCustomId("next").setLabel("\uFF1E").setStyle(2),
|
|
611
|
+
new djs.ButtonBuilder().setCustomId("last").setLabel("\u226B").setStyle(2)
|
|
612
|
+
);
|
|
613
|
+
const row2 = new djs.ActionRowBuilder().addComponents(
|
|
614
|
+
new djs.ButtonBuilder().setCustomId("stop").setLabel("\u2716 Close").setStyle(4)
|
|
615
|
+
);
|
|
616
|
+
container.addActionRowComponents(row1, row2);
|
|
617
|
+
return { components: [container], flags: 32768 };
|
|
618
|
+
}
|
|
619
|
+
const reply = await message2.reply(buildPage(index));
|
|
620
|
+
const collector = reply.createMessageComponentCollector({
|
|
621
|
+
filter: (i) => ["first", "prev", "goto", "stop", "next", "last"].includes(i.customId) && i.user.id === message2.author.id,
|
|
586
622
|
time: 12e4
|
|
587
623
|
});
|
|
588
|
-
collector.on("collect", async (
|
|
589
|
-
|
|
590
|
-
|
|
624
|
+
collector.on("collect", async (i) => {
|
|
625
|
+
if (i.customId === "first") {
|
|
626
|
+
index = 0;
|
|
627
|
+
} else if (i.customId === "prev") {
|
|
591
628
|
index = index > 0 ? index - 1 : index;
|
|
592
|
-
} else if (
|
|
629
|
+
} else if (i.customId === "next") {
|
|
593
630
|
index = index < chunks.length - 1 ? index + 1 : index;
|
|
594
|
-
} else if (
|
|
631
|
+
} else if (i.customId === "last") {
|
|
632
|
+
index = chunks.length - 1;
|
|
633
|
+
} else if (i.customId === "goto") {
|
|
634
|
+
const modal = new djs.ModalBuilder().setCustomId("goto_modal").setTitle("Go to Page");
|
|
635
|
+
const input = new djs.TextInputBuilder().setCustomId("page_num").setLabel(`Page Number (1-${chunks.length})`).setStyle(1).setRequired(true);
|
|
636
|
+
modal.addComponents(new djs.ActionRowBuilder().addComponents(input));
|
|
637
|
+
await i.showModal(modal);
|
|
638
|
+
try {
|
|
639
|
+
const modalSubmit = await i.awaitModalSubmit({ filter: (mi) => mi.user.id === message2.author.id && mi.customId === "goto_modal", time: 6e4 });
|
|
640
|
+
const targetPage = parseInt(modalSubmit.fields.getTextInputValue("page_num"), 10);
|
|
641
|
+
if (!isNaN(targetPage) && targetPage >= 1 && targetPage <= chunks.length) {
|
|
642
|
+
index = targetPage - 1;
|
|
643
|
+
}
|
|
644
|
+
await modalSubmit.update(buildPage(index));
|
|
645
|
+
} catch {
|
|
646
|
+
}
|
|
647
|
+
return;
|
|
648
|
+
} else if (i.customId === "stop") {
|
|
649
|
+
await reply.delete().catch(() => null);
|
|
595
650
|
collector.stop();
|
|
596
651
|
return;
|
|
597
652
|
}
|
|
598
|
-
await
|
|
599
|
-
${chunks[index]}
|
|
600
|
-
\`\`\`
|
|
601
|
-
*Page ${index + 1} of ${chunks.length}*`);
|
|
653
|
+
await i.update(buildPage(index));
|
|
602
654
|
});
|
|
603
655
|
collector.on("end", () => {
|
|
604
|
-
reply.
|
|
656
|
+
reply.edit({ components: [] }).catch(() => null);
|
|
605
657
|
});
|
|
606
658
|
}
|
|
607
659
|
|
|
@@ -622,6 +674,11 @@ var DJSNextClient = class extends Client {
|
|
|
622
674
|
_guildId;
|
|
623
675
|
_developers;
|
|
624
676
|
_middleware;
|
|
677
|
+
_prefixes;
|
|
678
|
+
_enableSlashCommands;
|
|
679
|
+
_enableTextCommands;
|
|
680
|
+
_enableMentionPrefix;
|
|
681
|
+
_enableNoPrefix;
|
|
625
682
|
constructor(options) {
|
|
626
683
|
super(options);
|
|
627
684
|
this.commands = new Collection3();
|
|
@@ -635,6 +692,14 @@ var DJSNextClient = class extends Client {
|
|
|
635
692
|
this._guildId = options.guildId;
|
|
636
693
|
this._developers = options.developers || [];
|
|
637
694
|
this._middleware = options.middleware;
|
|
695
|
+
const prefs = options.prefixes || [];
|
|
696
|
+
this._prefixes = Array.isArray(prefs) ? prefs : [prefs];
|
|
697
|
+
this._prefixes = this._prefixes.filter((p) => p !== "");
|
|
698
|
+
this._enableSlashCommands = options.enableSlashCommands ?? true;
|
|
699
|
+
this._enableTextCommands = options.enableTextCommands ?? true;
|
|
700
|
+
this._enableMentionPrefix = options.enableMentionPrefix ?? true;
|
|
701
|
+
this._enableNoPrefix = options.enableNoPrefix ?? false;
|
|
702
|
+
if (options.db) this.db = options.db;
|
|
638
703
|
this.attachCoreListeners();
|
|
639
704
|
}
|
|
640
705
|
attachCoreListeners() {
|
|
@@ -649,6 +714,7 @@ var DJSNextClient = class extends Client {
|
|
|
649
714
|
}
|
|
650
715
|
}
|
|
651
716
|
if (interaction.isChatInputCommand()) {
|
|
717
|
+
if (!this._enableSlashCommands) return;
|
|
652
718
|
let commandKey = interaction.commandName;
|
|
653
719
|
const group = interaction.options.getSubcommandGroup(false);
|
|
654
720
|
const sub = interaction.options.getSubcommand(false);
|
|
@@ -656,48 +722,11 @@ var DJSNextClient = class extends Client {
|
|
|
656
722
|
if (sub) commandKey += ` ${sub}`;
|
|
657
723
|
const command2 = this.commands.get(commandKey);
|
|
658
724
|
if (!command2 || !command2.execute) return;
|
|
659
|
-
if (
|
|
660
|
-
return interaction.reply({ content: "Only developers can use this command.", ephemeral: true });
|
|
661
|
-
}
|
|
662
|
-
if (command2.guildOnly && !interaction.inGuild()) {
|
|
663
|
-
return interaction.reply({ content: "This command can only be used in a server.", ephemeral: true });
|
|
664
|
-
}
|
|
665
|
-
if (command2.userPermissions && interaction.memberPermissions) {
|
|
666
|
-
const missing = interaction.memberPermissions.missing(command2.userPermissions);
|
|
667
|
-
if (missing.length > 0) {
|
|
668
|
-
return interaction.reply({ content: `You are missing permissions: \`${missing.join(", ")}\``, ephemeral: true });
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
if (command2.botPermissions && interaction.guild?.members.me?.permissions) {
|
|
672
|
-
const missing = interaction.guild.members.me.permissions.missing(command2.botPermissions);
|
|
673
|
-
if (missing.length > 0) {
|
|
674
|
-
return interaction.reply({ content: `I am missing permissions to run this: \`${missing.join(", ")}\``, ephemeral: true });
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
if (command2.cooldown) {
|
|
678
|
-
if (!this.cooldowns.has(commandKey)) this.cooldowns.set(commandKey, new Collection3());
|
|
679
|
-
const now = Date.now();
|
|
680
|
-
const timestamps = this.cooldowns.get(commandKey);
|
|
681
|
-
const cooldownAmount = command2.cooldown * 1e3;
|
|
682
|
-
if (timestamps.has(interaction.user.id)) {
|
|
683
|
-
const expirationTime = timestamps.get(interaction.user.id) + cooldownAmount;
|
|
684
|
-
if (now < expirationTime) {
|
|
685
|
-
return interaction.reply({
|
|
686
|
-
content: `Please wait, you are on a cooldown. You can use it again <t:${Math.round(expirationTime / 1e3)}:R>.`,
|
|
687
|
-
ephemeral: true
|
|
688
|
-
});
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
timestamps.set(interaction.user.id, now);
|
|
692
|
-
setTimeout(() => timestamps.delete(interaction.user.id), cooldownAmount);
|
|
693
|
-
}
|
|
725
|
+
if (!await this.handlePreconditions(command2, interaction, commandKey)) return;
|
|
694
726
|
try {
|
|
695
727
|
await command2.execute(interaction, this);
|
|
696
728
|
} catch (error) {
|
|
697
|
-
|
|
698
|
-
const msg = { content: "We ran into an internal error executing this command. The developers have been notified.", ephemeral: true };
|
|
699
|
-
if (interaction.replied || interaction.deferred) await interaction.followUp(msg).catch(() => null);
|
|
700
|
-
else await interaction.reply(msg).catch(() => null);
|
|
729
|
+
await this.handleCommandError(error, commandKey, interaction);
|
|
701
730
|
}
|
|
702
731
|
}
|
|
703
732
|
if (interaction.isAutocomplete()) {
|
|
@@ -733,28 +762,77 @@ var DJSNextClient = class extends Client {
|
|
|
733
762
|
}
|
|
734
763
|
}
|
|
735
764
|
if (component) {
|
|
765
|
+
if (!await this.handlePreconditions(component, interaction, interaction.customId)) return;
|
|
736
766
|
try {
|
|
737
767
|
await component.execute(interaction, this, params);
|
|
738
768
|
} catch (error) {
|
|
739
|
-
|
|
740
|
-
const msg = { content: "We ran into an internal error executing this component.", ephemeral: true };
|
|
741
|
-
if (interaction.replied || interaction.deferred) await interaction.followUp(msg).catch(() => null);
|
|
742
|
-
else await interaction.reply(msg).catch(() => null);
|
|
769
|
+
await this.handleCommandError(error, interaction.customId, interaction);
|
|
743
770
|
}
|
|
744
771
|
}
|
|
745
772
|
}
|
|
746
773
|
});
|
|
774
|
+
this.on("messageCreate", async (message2) => {
|
|
775
|
+
if (message2.author.bot) return;
|
|
776
|
+
if (!this._enableTextCommands) return;
|
|
777
|
+
if (this._middleware) {
|
|
778
|
+
try {
|
|
779
|
+
const shouldContinue = await this._middleware(message2, this);
|
|
780
|
+
if (!shouldContinue) return;
|
|
781
|
+
} catch (error) {
|
|
782
|
+
console.error(`[djs-next] Middleware error (Message):`, error);
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
let content2 = message2.content.trim();
|
|
787
|
+
let matchedPrefix = "";
|
|
788
|
+
let isCommand = false;
|
|
789
|
+
const mentionRegex = new RegExp(`^<@!?${this.user?.id}>\\s*`);
|
|
790
|
+
const canUseMention = this._enableMentionPrefix === true || Array.isArray(this._enableMentionPrefix) && this._enableMentionPrefix.includes(message2.author.id);
|
|
791
|
+
if (canUseMention && mentionRegex.test(content2)) {
|
|
792
|
+
matchedPrefix = content2.match(mentionRegex)[0];
|
|
793
|
+
isCommand = true;
|
|
794
|
+
} else {
|
|
795
|
+
const sortedPrefs = [...this._prefixes].sort((a, b) => b.length - a.length);
|
|
796
|
+
for (const p of sortedPrefs) {
|
|
797
|
+
if (content2.startsWith(p)) {
|
|
798
|
+
matchedPrefix = p;
|
|
799
|
+
isCommand = true;
|
|
800
|
+
break;
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
const canUseNoPrefix = this._enableNoPrefix === true || Array.isArray(this._enableNoPrefix) && this._enableNoPrefix.includes(message2.author.id);
|
|
804
|
+
if (!isCommand && canUseNoPrefix) {
|
|
805
|
+
isCommand = true;
|
|
806
|
+
matchedPrefix = "";
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
if (!isCommand) return;
|
|
810
|
+
const args2 = content2.slice(matchedPrefix.length).trim().split(/ +/g);
|
|
811
|
+
const commandName = args2.shift()?.toLowerCase();
|
|
812
|
+
if (!commandName) return;
|
|
813
|
+
let command2 = this.commands.get(commandName);
|
|
814
|
+
if (!command2) {
|
|
815
|
+
command2 = this.commands.find((c) => c.aliases?.includes(commandName) || false);
|
|
816
|
+
}
|
|
817
|
+
if (!command2 || !command2.executeText) return;
|
|
818
|
+
if (!await this.handlePreconditions(command2, message2, commandName)) return;
|
|
819
|
+
try {
|
|
820
|
+
await command2.executeText(message2, args2, this);
|
|
821
|
+
} catch (error) {
|
|
822
|
+
await this.handleCommandError(error, commandName, message2);
|
|
823
|
+
}
|
|
824
|
+
});
|
|
747
825
|
}
|
|
748
826
|
async start(token) {
|
|
749
827
|
if (!token) throw new Error("[djs-next] A token must be provided to start the bot.");
|
|
750
828
|
this.config = await loadConfig();
|
|
751
829
|
this._guildId = this._guildId || this.config.devGuildId;
|
|
752
|
-
if (!this._commandsDir
|
|
753
|
-
if (!this._eventsDir
|
|
754
|
-
if (!this._componentsDir
|
|
755
|
-
if (!this._tasksDir
|
|
756
|
-
if (!this._localesDir
|
|
757
|
-
if (this._localesDir) loadLocales(this._localesDir, this.config.defaultLocale);
|
|
830
|
+
if (!this._commandsDir) this._commandsDir = path6.resolve(process.cwd(), this.config.directories?.commands || "src/commands");
|
|
831
|
+
if (!this._eventsDir) this._eventsDir = path6.resolve(process.cwd(), this.config.directories?.events || "src/events");
|
|
832
|
+
if (!this._componentsDir) this._componentsDir = path6.resolve(process.cwd(), this.config.directories?.components || "src/components");
|
|
833
|
+
if (!this._tasksDir) this._tasksDir = path6.resolve(process.cwd(), this.config.directories?.tasks || "src/tasks");
|
|
834
|
+
if (!this._localesDir) this._localesDir = path6.resolve(process.cwd(), this.config.directories?.locales || "src/locales");
|
|
835
|
+
if (fs8.existsSync(this._localesDir)) loadLocales(this._localesDir, this.config.defaultLocale);
|
|
758
836
|
if (!this._middleware) {
|
|
759
837
|
const exts = [".js", ".mjs", ".cjs", ".ts", ".mts", ".cts"];
|
|
760
838
|
const cwd = process.cwd();
|
|
@@ -774,24 +852,24 @@ var DJSNextClient = class extends Client {
|
|
|
774
852
|
}
|
|
775
853
|
}
|
|
776
854
|
}
|
|
777
|
-
if (this._eventsDir) await loadEvents(this, this._eventsDir);
|
|
778
|
-
if (this._componentsDir) this.components = await loadComponents(this._componentsDir);
|
|
779
|
-
if (this._tasksDir) await loadTasks(this, this._tasksDir);
|
|
780
|
-
if (this._commandsDir) {
|
|
781
|
-
if (!this._clientId) throw new Error("[djs-next] You must provide a clientId to deploy commands.");
|
|
782
|
-
this.commands = await loadAndDeployCommands(this._commandsDir, token, this._clientId, this._guildId);
|
|
783
|
-
}
|
|
855
|
+
if (fs8.existsSync(this._eventsDir)) await loadEvents(this, this._eventsDir);
|
|
856
|
+
if (fs8.existsSync(this._componentsDir)) this.components = await loadComponents(this._componentsDir);
|
|
857
|
+
if (fs8.existsSync(this._tasksDir)) await loadTasks(this, this._tasksDir);
|
|
784
858
|
await this.login(token);
|
|
785
859
|
console.log(`[djs-next] Bot is ready and logged in as ${this.user?.tag}!`);
|
|
860
|
+
if (fs8.existsSync(this._commandsDir)) {
|
|
861
|
+
this._clientId = this._clientId || this.user.id;
|
|
862
|
+
this.commands = await loadAndDeployCommands(this._commandsDir, token, this._clientId, this._guildId);
|
|
863
|
+
}
|
|
786
864
|
}
|
|
787
|
-
enableDevTools(
|
|
788
|
-
if (
|
|
789
|
-
throw new Error(`[djs-next] Developer Tools prefix must be either 'dnxt' or 'nxt'. Received: ${
|
|
865
|
+
enableDevTools(prefix = "dnxt") {
|
|
866
|
+
if (prefix !== "dnxt" && prefix !== "nxt") {
|
|
867
|
+
throw new Error(`[djs-next] Developer Tools prefix must be either 'dnxt' or 'nxt'. Received: ${prefix}`);
|
|
790
868
|
}
|
|
791
869
|
this.on("messageCreate", async (message2) => {
|
|
792
|
-
await handleDNXT(message2, this,
|
|
870
|
+
await handleDNXT(message2, this, prefix);
|
|
793
871
|
});
|
|
794
|
-
console.log(`[djs-next] \u{1F6E0}\uFE0F Developer Tools enabled. Use "${
|
|
872
|
+
console.log(`[djs-next] \u{1F6E0}\uFE0F Developer Tools enabled. Use "${prefix}" in chat. Ensure MessageContent intent is enabled!`);
|
|
795
873
|
}
|
|
796
874
|
async enableHMR() {
|
|
797
875
|
try {
|
|
@@ -821,6 +899,157 @@ var DJSNextClient = class extends Client {
|
|
|
821
899
|
console.warn(`[djs-next] chokidar not installed. HMR is disabled. Please run "npm install chokidar" to use this feature.`);
|
|
822
900
|
}
|
|
823
901
|
}
|
|
902
|
+
async handleCommandError(error, commandKey, context) {
|
|
903
|
+
console.error(`[djs-next] Error executing command/component: "${commandKey}"`, error);
|
|
904
|
+
const djs = __require("discord.js");
|
|
905
|
+
if (this.config.responses?.errorBoundary === null) {
|
|
906
|
+
} else {
|
|
907
|
+
try {
|
|
908
|
+
const errorContent = this.config.responses?.errorBoundary || `### \u26A0\uFE0F Execution Error
|
|
909
|
+
> The framework encountered a fatal error while executing \`${commandKey}\`.`;
|
|
910
|
+
const codeBlock = `\`\`\`js
|
|
911
|
+
${String(error.stack || error.message).substring(0, 1500)}
|
|
912
|
+
\`\`\``;
|
|
913
|
+
if ("commandName" in context || "customId" in context) {
|
|
914
|
+
const container = new djs.ContainerBuilder().setAccentColor(15548997).addTextDisplayComponents(new djs.TextDisplayBuilder().setContent(errorContent)).addSeparatorComponents(new djs.SeparatorBuilder()).addTextDisplayComponents(new djs.TextDisplayBuilder().setContent(codeBlock));
|
|
915
|
+
const payload = { components: [container], flags: 32768, ephemeral: true };
|
|
916
|
+
const i = context;
|
|
917
|
+
if (i.isRepliable()) {
|
|
918
|
+
if (i.replied || i.deferred) await i.followUp(payload);
|
|
919
|
+
else await i.reply(payload);
|
|
920
|
+
}
|
|
921
|
+
} else {
|
|
922
|
+
const embed = new djs.EmbedBuilder().setColor(15548997).setDescription(`${errorContent}
|
|
923
|
+
|
|
924
|
+
${codeBlock}`);
|
|
925
|
+
const m = context;
|
|
926
|
+
await m.reply({ embeds: [embed] });
|
|
927
|
+
}
|
|
928
|
+
} catch (e) {
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
if (this.config.errorLogChannelId) {
|
|
932
|
+
try {
|
|
933
|
+
const channel = await this.channels.fetch(this.config.errorLogChannelId);
|
|
934
|
+
if (channel && channel.isTextBased()) {
|
|
935
|
+
const userId = "user" in context ? context.user.id : context.author.id;
|
|
936
|
+
await channel.send(`\u{1F6A8} **Global Error Boundary Caught Exception**
|
|
937
|
+
**Context:** \`${commandKey}\`
|
|
938
|
+
**User:** <@${userId}>
|
|
939
|
+
\`\`\`js
|
|
940
|
+
${String(error.stack || error.message).substring(0, 1900)}
|
|
941
|
+
\`\`\``);
|
|
942
|
+
}
|
|
943
|
+
} catch (e) {
|
|
944
|
+
console.error(`[djs-next] Failed to send error to log channel:`, e);
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
async handlePreconditions(command2, context, commandKey) {
|
|
949
|
+
const userId = "user" in context ? context.user.id : context.author.id;
|
|
950
|
+
const isGuild = "guild" in context && context.guild !== null;
|
|
951
|
+
const member = context.member;
|
|
952
|
+
const sendErr = async (msg) => {
|
|
953
|
+
if (msg === null) return;
|
|
954
|
+
if ("reply" in context && typeof context.reply === "function") {
|
|
955
|
+
try {
|
|
956
|
+
if ("replied" in context && context.isRepliable() && (context.replied || context.deferred)) {
|
|
957
|
+
await context.followUp({ content: msg, ephemeral: true });
|
|
958
|
+
} else {
|
|
959
|
+
await context.reply({ content: msg, ephemeral: true, allowedMentions: { repliedUser: false } });
|
|
960
|
+
}
|
|
961
|
+
} catch (e) {
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
};
|
|
965
|
+
if ("developerOnly" in command2 && command2.developerOnly && !this._developers.includes(userId)) {
|
|
966
|
+
await sendErr(this.config.responses?.developerOnly !== void 0 ? this.config.responses.developerOnly : "Only developers can use this command.");
|
|
967
|
+
return false;
|
|
968
|
+
}
|
|
969
|
+
if ("guildOnly" in command2 && command2.guildOnly && !isGuild) {
|
|
970
|
+
await sendErr(this.config.responses?.guildOnly !== void 0 ? this.config.responses.guildOnly : "This command can only be used in a server.");
|
|
971
|
+
return false;
|
|
972
|
+
}
|
|
973
|
+
if ("userPermissions" in command2 && command2.userPermissions && member?.permissions) {
|
|
974
|
+
const missing = member.permissions.missing(command2.userPermissions);
|
|
975
|
+
if (missing.length > 0) {
|
|
976
|
+
await sendErr(this.config.responses?.missingPerms !== void 0 ? this.config.responses.missingPerms === null ? null : this.config.responses.missingPerms.replace("{perms}", missing.join(", ")) : `You are missing permissions: \`${missing.join(", ")}\``);
|
|
977
|
+
return false;
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
if ("botPermissions" in command2 && command2.botPermissions && context.guild?.members.me?.permissions) {
|
|
981
|
+
const missing = context.guild.members.me.permissions.missing(command2.botPermissions);
|
|
982
|
+
if (missing.length > 0) {
|
|
983
|
+
await sendErr(this.config.responses?.missingPerms !== void 0 ? this.config.responses.missingPerms === null ? null : this.config.responses.missingPerms.replace("{perms}", missing.join(", ")) : `I am missing permissions to run this: \`${missing.join(", ")}\``);
|
|
984
|
+
return false;
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
let totalCooldown = "cooldown" in command2 && command2.cooldown ? command2.cooldown * 1e3 : 0;
|
|
988
|
+
if (command2.preconditions) {
|
|
989
|
+
for (const pre of command2.preconditions) {
|
|
990
|
+
const parts = pre.split(":");
|
|
991
|
+
const type = parts[0].toLowerCase();
|
|
992
|
+
const val = parts.slice(1).join(":");
|
|
993
|
+
if (type === "owneronly" || type === "developeronly") {
|
|
994
|
+
if (!this._developers.includes(userId)) {
|
|
995
|
+
await sendErr(this.config.responses?.developerOnly !== void 0 ? this.config.responses.developerOnly : "Only developers can use this command.");
|
|
996
|
+
return false;
|
|
997
|
+
}
|
|
998
|
+
} else if (type === "guildonly") {
|
|
999
|
+
if (!isGuild) {
|
|
1000
|
+
await sendErr(this.config.responses?.guildOnly !== void 0 ? this.config.responses.guildOnly : "This command can only be used in a server.");
|
|
1001
|
+
return false;
|
|
1002
|
+
}
|
|
1003
|
+
} else if (type === "requireperms") {
|
|
1004
|
+
const perms = val.split(",").map((p) => p.trim());
|
|
1005
|
+
if (member?.permissions) {
|
|
1006
|
+
const missing = member.permissions.missing(perms);
|
|
1007
|
+
if (missing.length > 0) {
|
|
1008
|
+
await sendErr(this.config.responses?.missingPerms !== void 0 ? this.config.responses.missingPerms === null ? null : this.config.responses.missingPerms.replace("{perms}", missing.join(", ")) : `You are missing permissions: \`${missing.join(", ")}\``);
|
|
1009
|
+
return false;
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
} else if (type === "cooldown") {
|
|
1013
|
+
const match = val.match(/(\d+)(s|m|h)/);
|
|
1014
|
+
if (match) {
|
|
1015
|
+
let amount = parseInt(match[1]) * 1e3;
|
|
1016
|
+
if (match[2] === "m") amount *= 60;
|
|
1017
|
+
if (match[2] === "h") amount *= 3600;
|
|
1018
|
+
totalCooldown = Math.max(totalCooldown, amount);
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
if (totalCooldown > 0) {
|
|
1024
|
+
const now = Date.now();
|
|
1025
|
+
if (this.config.cooldownAdapter) {
|
|
1026
|
+
const lastUsed = await this.config.cooldownAdapter.get(commandKey, userId);
|
|
1027
|
+
if (lastUsed) {
|
|
1028
|
+
const expirationTime = lastUsed + totalCooldown;
|
|
1029
|
+
if (now < expirationTime) {
|
|
1030
|
+
const timeStr = `<t:${Math.round(expirationTime / 1e3)}:R>`;
|
|
1031
|
+
await sendErr(this.config.responses?.cooldown !== void 0 ? this.config.responses.cooldown === null ? null : this.config.responses.cooldown.replace("{time}", timeStr) : `Please wait, you are on a cooldown. You can use it again ${timeStr}.`);
|
|
1032
|
+
return false;
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
await this.config.cooldownAdapter.set(commandKey, userId, now);
|
|
1036
|
+
} else {
|
|
1037
|
+
if (!this.cooldowns.has(commandKey)) this.cooldowns.set(commandKey, new Collection3());
|
|
1038
|
+
const timestamps = this.cooldowns.get(commandKey);
|
|
1039
|
+
if (timestamps.has(userId)) {
|
|
1040
|
+
const expirationTime = timestamps.get(userId) + totalCooldown;
|
|
1041
|
+
if (now < expirationTime) {
|
|
1042
|
+
const timeStr = `<t:${Math.round(expirationTime / 1e3)}:R>`;
|
|
1043
|
+
await sendErr(this.config.responses?.cooldown !== void 0 ? this.config.responses.cooldown === null ? null : this.config.responses.cooldown.replace("{time}", timeStr) : `Please wait, you are on a cooldown. You can use it again ${timeStr}.`);
|
|
1044
|
+
return false;
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
timestamps.set(userId, now);
|
|
1048
|
+
setTimeout(() => timestamps.delete(userId), totalCooldown);
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
return true;
|
|
1052
|
+
}
|
|
824
1053
|
};
|
|
825
1054
|
|
|
826
1055
|
// src/utils/paginate.ts
|
|
@@ -829,23 +1058,38 @@ import {
|
|
|
829
1058
|
ButtonBuilder,
|
|
830
1059
|
ButtonStyle
|
|
831
1060
|
} from "discord.js";
|
|
832
|
-
async function paginate(
|
|
833
|
-
|
|
834
|
-
|
|
1061
|
+
async function paginate(context, pages, time = 6e4) {
|
|
1062
|
+
const isMessage = "author" in context;
|
|
1063
|
+
if (!isMessage) {
|
|
1064
|
+
if (!context.deferred && !context.replied) {
|
|
1065
|
+
await context.deferReply();
|
|
1066
|
+
}
|
|
835
1067
|
}
|
|
836
1068
|
if (pages.length === 1) {
|
|
837
|
-
|
|
1069
|
+
if (isMessage) {
|
|
1070
|
+
return context.reply({ embeds: [pages[0]], components: [] });
|
|
1071
|
+
} else {
|
|
1072
|
+
return context.editReply({ embeds: [pages[0]], components: [] });
|
|
1073
|
+
}
|
|
838
1074
|
}
|
|
839
1075
|
let index = 0;
|
|
840
1076
|
const prevButton = new ButtonBuilder().setCustomId("djs_prev").setLabel("Previous").setStyle(ButtonStyle.Primary).setDisabled(true);
|
|
841
1077
|
const nextButton = new ButtonBuilder().setCustomId("djs_next").setLabel("Next").setStyle(ButtonStyle.Primary);
|
|
842
1078
|
const row = new ActionRowBuilder().addComponents(prevButton, nextButton);
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
1079
|
+
let message2;
|
|
1080
|
+
if (isMessage) {
|
|
1081
|
+
message2 = await context.reply({
|
|
1082
|
+
embeds: [pages[index]],
|
|
1083
|
+
components: [row]
|
|
1084
|
+
});
|
|
1085
|
+
} else {
|
|
1086
|
+
message2 = await context.editReply({
|
|
1087
|
+
embeds: [pages[index]],
|
|
1088
|
+
components: [row]
|
|
1089
|
+
});
|
|
1090
|
+
}
|
|
847
1091
|
const collector = message2.createMessageComponentCollector({
|
|
848
|
-
filter: (i) => i.user.id ===
|
|
1092
|
+
filter: (i) => i.user.id === (isMessage ? context.author.id : context.user.id),
|
|
849
1093
|
time
|
|
850
1094
|
});
|
|
851
1095
|
collector.on("collect", async (i) => {
|
|
@@ -864,17 +1108,147 @@ async function paginate(interaction, pages, time = 6e4) {
|
|
|
864
1108
|
collector.on("end", async () => {
|
|
865
1109
|
prevButton.setDisabled(true);
|
|
866
1110
|
nextButton.setDisabled(true);
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
1111
|
+
if (message2.editable) {
|
|
1112
|
+
await message2.edit({
|
|
1113
|
+
components: [new ActionRowBuilder().addComponents(prevButton, nextButton)]
|
|
1114
|
+
}).catch(() => {
|
|
1115
|
+
});
|
|
1116
|
+
}
|
|
871
1117
|
});
|
|
872
1118
|
}
|
|
873
1119
|
|
|
1120
|
+
// src/utils/prompts.ts
|
|
1121
|
+
import {
|
|
1122
|
+
ActionRowBuilder as ActionRowBuilder2,
|
|
1123
|
+
ButtonBuilder as ButtonBuilder2,
|
|
1124
|
+
ButtonStyle as ButtonStyle2
|
|
1125
|
+
} from "discord.js";
|
|
1126
|
+
async function confirmPrompt(context, content2, time = 3e4) {
|
|
1127
|
+
const isMessage = "author" in context;
|
|
1128
|
+
if (!isMessage) {
|
|
1129
|
+
if (!context.deferred && !context.replied) {
|
|
1130
|
+
await context.deferReply();
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
const yesButton = new ButtonBuilder2().setCustomId("djs_prompt_yes").setLabel("Yes").setStyle(ButtonStyle2.Success);
|
|
1134
|
+
const noButton = new ButtonBuilder2().setCustomId("djs_prompt_no").setLabel("No").setStyle(ButtonStyle2.Danger);
|
|
1135
|
+
const row = new ActionRowBuilder2().addComponents(yesButton, noButton);
|
|
1136
|
+
const payload = { components: [row] };
|
|
1137
|
+
if (typeof content2 === "string") {
|
|
1138
|
+
payload.content = content2;
|
|
1139
|
+
} else {
|
|
1140
|
+
payload.embeds = [content2];
|
|
1141
|
+
}
|
|
1142
|
+
let message2;
|
|
1143
|
+
if (isMessage) {
|
|
1144
|
+
message2 = await context.reply(payload);
|
|
1145
|
+
} else {
|
|
1146
|
+
message2 = await context.editReply(payload);
|
|
1147
|
+
}
|
|
1148
|
+
try {
|
|
1149
|
+
const interaction = await message2.awaitMessageComponent({
|
|
1150
|
+
filter: (i) => i.user.id === (isMessage ? context.author.id : context.user.id),
|
|
1151
|
+
time
|
|
1152
|
+
});
|
|
1153
|
+
if (interaction.customId === "djs_prompt_yes") {
|
|
1154
|
+
await interaction.update({ components: [] });
|
|
1155
|
+
return true;
|
|
1156
|
+
} else {
|
|
1157
|
+
await interaction.update({ components: [] });
|
|
1158
|
+
return false;
|
|
1159
|
+
}
|
|
1160
|
+
} catch (error) {
|
|
1161
|
+
if (message2.editable) {
|
|
1162
|
+
await message2.edit({ components: [] }).catch(() => {
|
|
1163
|
+
});
|
|
1164
|
+
}
|
|
1165
|
+
return null;
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
// src/utils/PaginationBuilder.ts
|
|
1170
|
+
import {
|
|
1171
|
+
ActionRowBuilder as ActionRowBuilder3,
|
|
1172
|
+
ButtonBuilder as ButtonBuilder3,
|
|
1173
|
+
ButtonStyle as ButtonStyle3,
|
|
1174
|
+
CommandInteraction as CommandInteraction3,
|
|
1175
|
+
ComponentType
|
|
1176
|
+
} from "discord.js";
|
|
1177
|
+
var PaginationBuilder = class {
|
|
1178
|
+
pages = [];
|
|
1179
|
+
timeout = 6e4;
|
|
1180
|
+
constructor(pages) {
|
|
1181
|
+
if (pages) this.pages = pages;
|
|
1182
|
+
}
|
|
1183
|
+
addPage(embed) {
|
|
1184
|
+
this.pages.push(embed);
|
|
1185
|
+
return this;
|
|
1186
|
+
}
|
|
1187
|
+
setPages(pages) {
|
|
1188
|
+
this.pages = pages;
|
|
1189
|
+
return this;
|
|
1190
|
+
}
|
|
1191
|
+
setTimeout(ms) {
|
|
1192
|
+
this.timeout = ms;
|
|
1193
|
+
return this;
|
|
1194
|
+
}
|
|
1195
|
+
async build(target) {
|
|
1196
|
+
if (this.pages.length === 0) throw new Error("[djs-next] PaginationBuilder requires at least one page.");
|
|
1197
|
+
if (this.pages.length === 1) {
|
|
1198
|
+
if (target instanceof CommandInteraction3) {
|
|
1199
|
+
if (target.deferred || target.replied) {
|
|
1200
|
+
return await target.editReply({ embeds: [this.pages[0]] });
|
|
1201
|
+
}
|
|
1202
|
+
return await target.reply({ embeds: [this.pages[0]], fetchReply: true });
|
|
1203
|
+
} else {
|
|
1204
|
+
return await target.reply({ embeds: [this.pages[0]] });
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
let currentPage = 0;
|
|
1208
|
+
const row = new ActionRowBuilder3().addComponents(
|
|
1209
|
+
new ButtonBuilder3().setCustomId("prev").setLabel("\u25C0").setStyle(ButtonStyle3.Primary).setDisabled(true),
|
|
1210
|
+
new ButtonBuilder3().setCustomId("page").setLabel(`1 / ${this.pages.length}`).setStyle(ButtonStyle3.Secondary).setDisabled(true),
|
|
1211
|
+
new ButtonBuilder3().setCustomId("next").setLabel("\u25B6").setStyle(ButtonStyle3.Primary)
|
|
1212
|
+
);
|
|
1213
|
+
let replyMsg;
|
|
1214
|
+
if (target instanceof CommandInteraction3) {
|
|
1215
|
+
if (target.deferred || target.replied) {
|
|
1216
|
+
replyMsg = await target.editReply({ embeds: [this.pages[0]], components: [row] });
|
|
1217
|
+
} else {
|
|
1218
|
+
replyMsg = await target.reply({ embeds: [this.pages[0]], components: [row], fetchReply: true });
|
|
1219
|
+
}
|
|
1220
|
+
} else {
|
|
1221
|
+
replyMsg = await target.reply({ embeds: [this.pages[0]], components: [row] });
|
|
1222
|
+
}
|
|
1223
|
+
const userId = target instanceof CommandInteraction3 ? target.user.id : target.author.id;
|
|
1224
|
+
const collector = replyMsg.createMessageComponentCollector({
|
|
1225
|
+
componentType: ComponentType.Button,
|
|
1226
|
+
time: this.timeout,
|
|
1227
|
+
filter: (i) => i.user.id === userId
|
|
1228
|
+
});
|
|
1229
|
+
collector.on("collect", async (i) => {
|
|
1230
|
+
if (i.customId === "prev") currentPage--;
|
|
1231
|
+
else if (i.customId === "next") currentPage++;
|
|
1232
|
+
const newRow = new ActionRowBuilder3().addComponents(
|
|
1233
|
+
new ButtonBuilder3().setCustomId("prev").setLabel("\u25C0").setStyle(ButtonStyle3.Primary).setDisabled(currentPage === 0),
|
|
1234
|
+
new ButtonBuilder3().setCustomId("page").setLabel(`${currentPage + 1} / ${this.pages.length}`).setStyle(ButtonStyle3.Secondary).setDisabled(true),
|
|
1235
|
+
new ButtonBuilder3().setCustomId("next").setLabel("\u25B6").setStyle(ButtonStyle3.Primary).setDisabled(currentPage === this.pages.length - 1)
|
|
1236
|
+
);
|
|
1237
|
+
await i.update({ embeds: [this.pages[currentPage]], components: [newRow] });
|
|
1238
|
+
});
|
|
1239
|
+
collector.on("end", () => {
|
|
1240
|
+
replyMsg.edit({ components: [] }).catch(() => null);
|
|
1241
|
+
});
|
|
1242
|
+
return replyMsg;
|
|
1243
|
+
}
|
|
1244
|
+
};
|
|
1245
|
+
|
|
874
1246
|
// src/index.ts
|
|
875
1247
|
export * from "discord.js";
|
|
876
1248
|
export {
|
|
877
1249
|
DJSNextClient,
|
|
1250
|
+
PaginationBuilder,
|
|
1251
|
+
confirmPrompt,
|
|
878
1252
|
defineConfig,
|
|
879
1253
|
getLocalesCache,
|
|
880
1254
|
loadConfig,
|