@seedcord/gateway 0.1.0-next.5 → 0.1.0-next.6
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 +330 -1035
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +101 -420
- package/dist/index.mjs +335 -1031
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -6
package/dist/index.cjs
CHANGED
|
@@ -33,11 +33,9 @@ let _seedcord_errors = require("@seedcord/errors");
|
|
|
33
33
|
let _seedcord_errors_internal = require("@seedcord/errors/internal");
|
|
34
34
|
let _seedcord_core = require("@seedcord/core");
|
|
35
35
|
let _seedcord_logger = require("@seedcord/logger");
|
|
36
|
+
let discord_js = require("discord.js");
|
|
36
37
|
let _discordjs_builders = require("@discordjs/builders");
|
|
37
38
|
let _seedcord_utils_internal = require("@seedcord/utils/internal");
|
|
38
|
-
let discord_js = require("discord.js");
|
|
39
|
-
let node_crypto = require("node:crypto");
|
|
40
|
-
node_crypto = __toESM(node_crypto, 1);
|
|
41
39
|
let _seedcord_core_plugin = require("@seedcord/core/plugin");
|
|
42
40
|
let _seedcord_core_node_internal = require("@seedcord/core/node/internal");
|
|
43
41
|
let _seedcord_rate_limiter = require("@seedcord/rate-limiter");
|
|
@@ -46,10 +44,11 @@ let envapt = require("envapt");
|
|
|
46
44
|
let chalk = require("chalk");
|
|
47
45
|
chalk = __toESM(chalk, 1);
|
|
48
46
|
let envapt_legacy = require("envapt/legacy");
|
|
49
|
-
let node_path = require("node:path");
|
|
50
47
|
let _seedcord_core_hmr = require("@seedcord/core/hmr");
|
|
51
48
|
let _seedcord_utils = require("@seedcord/utils");
|
|
52
49
|
let _seedcord_utils_node = require("@seedcord/utils/node");
|
|
50
|
+
let node_crypto = require("node:crypto");
|
|
51
|
+
node_crypto = __toESM(node_crypto, 1);
|
|
53
52
|
|
|
54
53
|
//#region src/logger-bootstrap.ts
|
|
55
54
|
(0, _seedcord_logger_node.installNodeDefaults)();
|
|
@@ -242,59 +241,6 @@ var CannotAssignBotRole = class extends _seedcord_core.Notice {
|
|
|
242
241
|
return { components: [new _seedcord_core_internal.NoticeCard("I cannot assign a managed role.").component] };
|
|
243
242
|
}
|
|
244
243
|
};
|
|
245
|
-
var HasDangerousPermissions = class extends _seedcord_core.Notice {
|
|
246
|
-
subject;
|
|
247
|
-
dangerousPerms;
|
|
248
|
-
customLead;
|
|
249
|
-
constructor(message, subject, dangerousPerms) {
|
|
250
|
-
super(message ?? "A dangerous permission is enabled.");
|
|
251
|
-
this.subject = subject;
|
|
252
|
-
this.dangerousPerms = dangerousPerms;
|
|
253
|
-
this.customLead = message;
|
|
254
|
-
}
|
|
255
|
-
render() {
|
|
256
|
-
const bullets = this.dangerousPerms.map((perm) => `• ${perm}`).join("\n");
|
|
257
|
-
return { components: [new _seedcord_core_internal.NoticeCard(`${this.customLead ?? `${this.subject} has the following permission entries that must not be enabled:`}\n\n${bullets}`).component] };
|
|
258
|
-
}
|
|
259
|
-
};
|
|
260
|
-
var UserNotFound = class extends _seedcord_core.Notice {
|
|
261
|
-
userArg;
|
|
262
|
-
constructor(userArg) {
|
|
263
|
-
super(`User not found: ${userArg}`);
|
|
264
|
-
this.userArg = userArg;
|
|
265
|
-
}
|
|
266
|
-
render() {
|
|
267
|
-
return { components: [new _seedcord_core_internal.NoticeCard(`User probably doesn't exist or was deleted.\n**User Argument:** \`${this.userArg}\`\nPlease check the user ID and try again. Only pass valid user IDs as the argument.`, "User Not Found").component] };
|
|
268
|
-
}
|
|
269
|
-
};
|
|
270
|
-
var UserNotInGuild = class extends _seedcord_core.Notice {
|
|
271
|
-
constructor(message = "User is not in the guild.") {
|
|
272
|
-
super(message);
|
|
273
|
-
}
|
|
274
|
-
render() {
|
|
275
|
-
return { components: [new _seedcord_core_internal.NoticeCard(this.message).component] };
|
|
276
|
-
}
|
|
277
|
-
};
|
|
278
|
-
var RoleDoesNotExist = class extends _seedcord_core.Notice {
|
|
279
|
-
roleId;
|
|
280
|
-
constructor(message, roleId) {
|
|
281
|
-
super(message);
|
|
282
|
-
this.roleId = roleId;
|
|
283
|
-
}
|
|
284
|
-
render() {
|
|
285
|
-
return { components: [new _seedcord_core_internal.NoticeCard(`The role with ID \`${this.roleId}\` does not exist.`).component] };
|
|
286
|
-
}
|
|
287
|
-
};
|
|
288
|
-
var CouldNotFindChannel = class extends _seedcord_core.Notice {
|
|
289
|
-
channelId;
|
|
290
|
-
constructor(message, channelId) {
|
|
291
|
-
super(message);
|
|
292
|
-
this.channelId = channelId;
|
|
293
|
-
}
|
|
294
|
-
render() {
|
|
295
|
-
return { components: [new _seedcord_core_internal.NoticeCard(`Could not find channel with ID \`${this.channelId}\`. It could also be that the channel is not a text channel.`).component] };
|
|
296
|
-
}
|
|
297
|
-
};
|
|
298
244
|
|
|
299
245
|
//#endregion
|
|
300
246
|
//#region src/bot/gates/catalog/Nsfw.ts
|
|
@@ -332,20 +278,15 @@ function Nsfw(options) {
|
|
|
332
278
|
|
|
333
279
|
//#endregion
|
|
334
280
|
//#region src/bot/injectors/EmojiInjector.ts
|
|
335
|
-
const emojiStorage =
|
|
336
|
-
function isEmojiTuple(v) {
|
|
337
|
-
return Array.isArray(v) && v.length === 2 && typeof v[0] === "string" && typeof v[1] === "string";
|
|
338
|
-
}
|
|
281
|
+
const emojiStorage = (0, _seedcord_core_internal.accessorStore)();
|
|
339
282
|
/**
|
|
340
|
-
*
|
|
341
|
-
*
|
|
342
|
-
* @see {@link EmojiMap}
|
|
283
|
+
* The bot's resolved emojis, keyed by {@link EmojiMap}. Filled by {@link EmojiInjector} during startup.
|
|
284
|
+
* A read before that throws.
|
|
343
285
|
*/
|
|
344
|
-
const Emojis = emojiStorage;
|
|
286
|
+
const Emojis = (0, _seedcord_core_internal.guardedAccessor)("Emojis", emojiStorage);
|
|
345
287
|
/**
|
|
346
|
-
* Loads and injects emojis based on bot configuration.
|
|
347
|
-
*
|
|
348
|
-
* For emojis injected from specific guilds, ensure that the Guilds intent is provided in client options.
|
|
288
|
+
* Loads and injects emojis based on bot configuration. A guild emoji requires the Guilds intent in the client
|
|
289
|
+
* options.
|
|
349
290
|
*
|
|
350
291
|
* @internal
|
|
351
292
|
*/
|
|
@@ -365,7 +306,7 @@ var EmojiInjector = class {
|
|
|
365
306
|
await this.core.bot.client.application?.emojis.fetch();
|
|
366
307
|
const failures = [];
|
|
367
308
|
const fetchedGuilds = /* @__PURE__ */ new Set();
|
|
368
|
-
for (const [key, value] of Object.entries(configEmojis)) if (isEmojiTuple(value)) await this.resolveTuple(key, value, failures, fetchedGuilds);
|
|
309
|
+
for (const [key, value] of Object.entries(configEmojis)) if ((0, _seedcord_core_internal.isEmojiTuple)(value)) await this.resolveTuple(key, value, failures, fetchedGuilds);
|
|
369
310
|
else if (typeof value === "string") this.resolveString(key, value, failures);
|
|
370
311
|
else failures.push(` - "${key}" has an invalid value (expected a name or [name, guildId])`);
|
|
371
312
|
if (failures.length > 0) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.ConfigEmojiUnresolved, [failures.length, failures.join("\n")]);
|
|
@@ -398,126 +339,10 @@ var EmojiInjector = class {
|
|
|
398
339
|
emojiStorage[key] = appEmoji;
|
|
399
340
|
}
|
|
400
341
|
clearEmojis() {
|
|
401
|
-
|
|
402
|
-
}
|
|
403
|
-
};
|
|
404
|
-
|
|
405
|
-
//#endregion
|
|
406
|
-
//#region src/bot/injectors/CommandMentionInjector.ts
|
|
407
|
-
const mentionStorage = {};
|
|
408
|
-
/**
|
|
409
|
-
* Ready-to-send command mention strings, keyed by {@link SlashOptionRegistry}.
|
|
410
|
-
*
|
|
411
|
-
* A value is a clickable `</name:id>` when the command's id resolved from the deploy, otherwise the plain
|
|
412
|
-
* `/name` text. Populated by {@link CommandMentionInjector} after each deploy.
|
|
413
|
-
*/
|
|
414
|
-
const CommandMentions = mentionStorage;
|
|
415
|
-
const MULTI_GUILD = Symbol("seedcord:commandmentions:multi-guild");
|
|
416
|
-
/**
|
|
417
|
-
* Turns each deployed slash route into a clickable command mention exposed through {@link CommandMentions}.
|
|
418
|
-
*
|
|
419
|
-
* Consumes the ids {@link DeployResult} carries, so it runs after a deploy rather than resolving from a name
|
|
420
|
-
* cache (discord.js offers none for command ids). A global command is clickable everywhere by its app-wide id.
|
|
421
|
-
* A guild command deployed to one guild is clickable by that guild's id. A command deployed to two or more
|
|
422
|
-
* guilds mints a different id per guild, so one accessor key cannot resolve it, and it falls back to plain
|
|
423
|
-
* `/name` text with one warn. On a non-owning shard a guild command also falls back to plain text.
|
|
424
|
-
*
|
|
425
|
-
* @internal
|
|
426
|
-
*/
|
|
427
|
-
var CommandMentionInjector = class {
|
|
428
|
-
core;
|
|
429
|
-
logger = new _seedcord_logger.Logger("CommandMentions", { channel: "bot" });
|
|
430
|
-
constructor(core) {
|
|
431
|
-
this.core = core;
|
|
432
|
-
}
|
|
433
|
-
inject(deploy) {
|
|
434
|
-
this.clear();
|
|
435
|
-
const globalIds = this.indexByName(deploy.global);
|
|
436
|
-
const guildIds = this.indexGuilds(deploy.guilds);
|
|
437
|
-
const warned = /* @__PURE__ */ new Set();
|
|
438
|
-
let clickable = 0;
|
|
439
|
-
for (const command of this.allSlashBuilders()) {
|
|
440
|
-
const id = this.resolveId(command.name, globalIds, guildIds, warned);
|
|
441
|
-
for (const leaf of (0, _seedcord_utils_internal.routeLeavesOf)(command.toJSON())) {
|
|
442
|
-
mentionStorage[leaf.route] = id ? this.toMention(leaf.route, id) : this.toPlain(leaf.route);
|
|
443
|
-
if (id) clickable++;
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
this.logger.utils.summary("Linked mentions", {
|
|
447
|
-
clickable,
|
|
448
|
-
plain: Object.keys(mentionStorage).length - clickable
|
|
449
|
-
});
|
|
450
|
-
}
|
|
451
|
-
allSlashBuilders() {
|
|
452
|
-
const registry = this.core.bot.commands;
|
|
453
|
-
if (!registry) return [];
|
|
454
|
-
const slash = [...registry.globalCommands, ...registry.guildCommands.values()].flat().filter((command) => command instanceof _discordjs_builders.SlashCommandBuilder);
|
|
455
|
-
return [...new Set(slash)];
|
|
456
|
-
}
|
|
457
|
-
indexByName(collection) {
|
|
458
|
-
const map = /* @__PURE__ */ new Map();
|
|
459
|
-
for (const command of collection.values()) map.set(command.name, command.id);
|
|
460
|
-
return map;
|
|
461
|
-
}
|
|
462
|
-
indexGuilds(guilds) {
|
|
463
|
-
const map = /* @__PURE__ */ new Map();
|
|
464
|
-
for (const collection of guilds.values()) for (const command of collection.values()) {
|
|
465
|
-
const existing = map.get(command.name);
|
|
466
|
-
if (existing === void 0) map.set(command.name, command.id);
|
|
467
|
-
else if (existing !== command.id) map.set(command.name, MULTI_GUILD);
|
|
468
|
-
}
|
|
469
|
-
return map;
|
|
470
|
-
}
|
|
471
|
-
resolveId(name, globalIds, guildIds, warned) {
|
|
472
|
-
const global = globalIds.get(name);
|
|
473
|
-
if (global) return global;
|
|
474
|
-
const guild = guildIds.get(name);
|
|
475
|
-
if (guild === void 0) return void 0;
|
|
476
|
-
if (guild === MULTI_GUILD) {
|
|
477
|
-
if (!warned.has(name)) {
|
|
478
|
-
this.logger.warn(`${name} is deployed to multiple guilds, falling back to plain text (not clickable).`);
|
|
479
|
-
warned.add(name);
|
|
480
|
-
}
|
|
481
|
-
return;
|
|
482
|
-
}
|
|
483
|
-
return guild;
|
|
484
|
-
}
|
|
485
|
-
toMention(route, id) {
|
|
486
|
-
const [name, middle, sub] = route.split("/");
|
|
487
|
-
if (name && middle && sub) return (0, discord_js.chatInputApplicationCommandMention)(name, middle, sub, id);
|
|
488
|
-
if (name && middle) return (0, discord_js.chatInputApplicationCommandMention)(name, middle, id);
|
|
489
|
-
return (0, discord_js.chatInputApplicationCommandMention)(name ?? route, id);
|
|
490
|
-
}
|
|
491
|
-
toPlain(route) {
|
|
492
|
-
return `/${route.replaceAll("/", " ")}`;
|
|
493
|
-
}
|
|
494
|
-
clear() {
|
|
495
|
-
for (const key of Object.keys(mentionStorage)) Reflect.deleteProperty(mentionStorage, key);
|
|
342
|
+
(0, _seedcord_core_internal.clearStore)(emojiStorage);
|
|
496
343
|
}
|
|
497
344
|
};
|
|
498
345
|
|
|
499
|
-
//#endregion
|
|
500
|
-
//#region src/bot/utilities/channels/fetchText.ts
|
|
501
|
-
/**
|
|
502
|
-
* Fetches and validates a text channel. Refuses when the channel doesn't exist or isn't a text channel.
|
|
503
|
-
*
|
|
504
|
-
* @param client - The Discord client instance
|
|
505
|
-
* @param channelId - Channel ID or TextChannel instance
|
|
506
|
-
* @returns Promise resolving to the text channel
|
|
507
|
-
*/
|
|
508
|
-
async function fetchText(client, channelId) {
|
|
509
|
-
if (channelId instanceof discord_js.TextChannel) return channelId;
|
|
510
|
-
let channel = client.channels.cache.get(channelId);
|
|
511
|
-
if (!channel) try {
|
|
512
|
-
channel = await client.channels.fetch(channelId);
|
|
513
|
-
} catch (err) {
|
|
514
|
-
if (err instanceof discord_js.DiscordAPIError && err.code === discord_js.RESTJSONErrorCodes.UnknownChannel) throw new CouldNotFindChannel("Channel not found or not a text channel", channelId);
|
|
515
|
-
throw err;
|
|
516
|
-
}
|
|
517
|
-
if (channel?.isTextBased()) return channel;
|
|
518
|
-
throw new CouldNotFindChannel("Channel not found or not a text channel", channelId);
|
|
519
|
-
}
|
|
520
|
-
|
|
521
346
|
//#endregion
|
|
522
347
|
//#region src/bot/notices/utils.ts
|
|
523
348
|
function mentionFor(subject) {
|
|
@@ -528,48 +353,37 @@ function mentionFor(subject) {
|
|
|
528
353
|
//#endregion
|
|
529
354
|
//#region src/bot/utilities/permissions/checkPermissions.ts
|
|
530
355
|
function checkPermissions(a, b, c, d, e) {
|
|
531
|
-
const { for: pFor, in: pIn, scope, inverse = false, missingNotice
|
|
356
|
+
const { for: pFor, in: pIn, scope, inverse = false, missingNotice, dangerousNotice } = a instanceof discord_js.Role || a instanceof discord_js.GuildMember ? {
|
|
532
357
|
for: a,
|
|
533
358
|
in: b,
|
|
534
359
|
scope: c,
|
|
535
360
|
inverse: d ?? false,
|
|
536
361
|
...e
|
|
537
362
|
} : a;
|
|
538
|
-
const Missing = missingCtor ?? _seedcord_core_internal.MissingPermissions;
|
|
539
|
-
const Dangerous = dangerousCtor ?? HasDangerousPermissions;
|
|
540
|
-
const subject = mentionFor(pFor);
|
|
541
363
|
const perms = pIn instanceof discord_js.Guild ? pFor.permissions : pIn.permissionsFor(pFor, true);
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
364
|
+
(0, _seedcord_core.assertPermissions)({
|
|
365
|
+
subject: mentionFor(pFor),
|
|
366
|
+
permissions: perms.bitfield,
|
|
367
|
+
scope,
|
|
368
|
+
inverse,
|
|
369
|
+
...missingNotice && { missingNotice },
|
|
370
|
+
...dangerousNotice && { dangerousNotice }
|
|
371
|
+
});
|
|
550
372
|
}
|
|
551
373
|
|
|
552
374
|
//#endregion
|
|
553
375
|
//#region src/bot/utilities/permissions/checkBotPermissions.ts
|
|
554
376
|
/**
|
|
555
|
-
* Checks
|
|
556
|
-
*
|
|
557
|
-
*
|
|
558
|
-
* @param target - Guild or text channel to check in
|
|
559
|
-
* @param scope - Permission bits to validate
|
|
560
|
-
* @param inverse - Whether to check for absence of the given permissions
|
|
561
|
-
* @param errors - Optional custom error constructors
|
|
377
|
+
* Checks the bot's own permissions in a {@link Guild} or {@link TextChannel}. Refuses when a permission in
|
|
378
|
+
* `scope` is missing, or with `inverse` when one is present. When the bot member is uncached, the refusal
|
|
379
|
+
* reports every permission in `scope` as missing.
|
|
562
380
|
*
|
|
563
381
|
* @example
|
|
564
382
|
* ```ts
|
|
565
383
|
* import { PermissionFlagsBits } from 'discord.js';
|
|
566
384
|
* import { checkBotPermissions } from '@seedcord/gateway';
|
|
567
385
|
*
|
|
568
|
-
*
|
|
569
|
-
* checkBotPermissions(textChannel, [
|
|
570
|
-
* PermissionFlagsBits.SendMessages,
|
|
571
|
-
* PermissionFlagsBits.ViewChannel
|
|
572
|
-
* ]);
|
|
386
|
+
* checkBotPermissions(textChannel, [PermissionFlagsBits.SendMessages, PermissionFlagsBits.ViewChannel]);
|
|
573
387
|
* ```
|
|
574
388
|
*/
|
|
575
389
|
function checkBotPermissions(target, scope, inverse = false, errors) {
|
|
@@ -592,26 +406,17 @@ function checkBotPermissions(target, scope, inverse = false, errors) {
|
|
|
592
406
|
}
|
|
593
407
|
|
|
594
408
|
//#endregion
|
|
595
|
-
//#region src/bot/utilities/
|
|
596
|
-
|
|
597
|
-
* Gets the bot's managed role in a guild.
|
|
598
|
-
*
|
|
599
|
-
* @param guild - The guild to get the bot role from
|
|
600
|
-
* @returns The bot's managed role in the guild
|
|
601
|
-
* @throws A **SeedcordError** if the client user is unavailable or if the bot role is missing
|
|
602
|
-
*/
|
|
603
|
-
function getBotRole(guild) {
|
|
409
|
+
//#region src/bot/utilities/permissions/hasPermsToAssign.ts
|
|
410
|
+
function botRoleOf(guild) {
|
|
604
411
|
const botRole = guild.roles.botRoleFor(guild.client.user);
|
|
605
412
|
if (!botRole) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.CoreBotRoleMissing, [guild.id]);
|
|
606
413
|
return botRole;
|
|
607
414
|
}
|
|
608
|
-
|
|
609
|
-
//#endregion
|
|
610
|
-
//#region src/bot/utilities/permissions/hasPermsToAssign.ts
|
|
611
415
|
/**
|
|
612
|
-
* Validates
|
|
416
|
+
* Validates that the bot can assign a target role. Refuses when the target role is above the bot's, is managed, or the bot lacks Manage Roles.
|
|
613
417
|
*
|
|
614
418
|
* @param roleOrOptions - Target role or complete options for the check
|
|
419
|
+
* @throws A `Notice` on refusal, or a `SeedcordError` when the bot has no role in the guild's role cache.
|
|
615
420
|
*
|
|
616
421
|
* @example
|
|
617
422
|
* ```ts
|
|
@@ -639,135 +444,108 @@ function hasPermsToAssign(roleOrOptions) {
|
|
|
639
444
|
const HigherNotice = errors?.higherNotice ?? RoleHigherThanMe;
|
|
640
445
|
const ManagedNotice = errors?.managedNotice ?? CannotAssignBotRole;
|
|
641
446
|
const MissingNotice = errors?.missingNotice ?? _seedcord_core_internal.MissingPermissions;
|
|
642
|
-
const botRole =
|
|
447
|
+
const botRole = botRoleOf(targetRole.guild);
|
|
643
448
|
if (targetRole.comparePositionTo(botRole) >= 0) throw new HigherNotice("Role is higher than me", targetRole, botRole);
|
|
644
449
|
if (targetRole.managed) throw new ManagedNotice(`Cannot assign managed role ${targetRole.name}`);
|
|
645
450
|
checkBotPermissions(targetRole.guild, [discord_js.PermissionFlagsBits.ManageRoles], false, { missingNotice: MissingNotice });
|
|
646
451
|
}
|
|
647
452
|
|
|
648
453
|
//#endregion
|
|
649
|
-
//#region src/bot/
|
|
650
|
-
|
|
651
|
-
return err instanceof discord_js.DiscordAPIError && err.code === discord_js.RESTJSONErrorCodes.UnknownRole;
|
|
652
|
-
}
|
|
653
|
-
async function fetchRoleInGuild(guild, roleId) {
|
|
654
|
-
const cached = guild.roles.cache.get(roleId);
|
|
655
|
-
if (cached) return cached;
|
|
656
|
-
try {
|
|
657
|
-
return await guild.roles.fetch(roleId);
|
|
658
|
-
} catch (err) {
|
|
659
|
-
if (isUnknownRole(err)) throw new RoleDoesNotExist("Role not found in specified guild", roleId);
|
|
660
|
-
throw err;
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
async function scanGuildsForRole(client, roleId) {
|
|
664
|
-
const cached = client.guilds.cache.map((guild) => guild.roles.cache.get(roleId)).find(Boolean);
|
|
665
|
-
if (cached) return cached;
|
|
666
|
-
for (const guild of client.guilds.cache.values()) try {
|
|
667
|
-
const role = await guild.roles.fetch(roleId);
|
|
668
|
-
if (role) return role;
|
|
669
|
-
} catch (err) {
|
|
670
|
-
if (isUnknownRole(err)) continue;
|
|
671
|
-
throw err;
|
|
672
|
-
}
|
|
673
|
-
return null;
|
|
674
|
-
}
|
|
454
|
+
//#region src/bot/confirm/reserved.ts
|
|
455
|
+
const RESERVED_CONFIRM_PREFIX = "__seedcord_confirm";
|
|
675
456
|
/**
|
|
676
|
-
*
|
|
457
|
+
* The reserved confirm/cancel codec. It has no route, so the dispatcher ignores its clicks and only the
|
|
458
|
+
* in-process collector reads them. Do not mint a real component from it.
|
|
677
459
|
*
|
|
678
|
-
* @
|
|
679
|
-
* @param roleId - The role ID to fetch
|
|
680
|
-
* @returns Promise resolving to the role
|
|
460
|
+
* @internal
|
|
681
461
|
*/
|
|
682
|
-
|
|
683
|
-
if (!roleId) throw new RoleDoesNotExist("Role ID is null or undefined", roleId);
|
|
684
|
-
const role = clientOrGuild instanceof discord_js.Guild ? await fetchRoleInGuild(clientOrGuild, roleId) : await scanGuildsForRole(clientOrGuild, roleId);
|
|
685
|
-
if (!role) throw new RoleDoesNotExist("Role not found", roleId);
|
|
686
|
-
return role;
|
|
687
|
-
}
|
|
462
|
+
const CONFIRM_DEF = new _seedcord_core.CustomId(RESERVED_CONFIRM_PREFIX).oneOf("choice", ["confirm", "cancel"]);
|
|
688
463
|
|
|
689
464
|
//#endregion
|
|
690
|
-
//#region src/bot/
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
465
|
+
//#region src/bot/confirm/getConfirmation.ts
|
|
466
|
+
const DEFAULT_TIMEOUT_MS = 3e4;
|
|
467
|
+
const CONFIRM_IDS = {
|
|
468
|
+
confirm: CONFIRM_DEF.encode({ choice: "confirm" }),
|
|
469
|
+
cancel: CONFIRM_DEF.encode({ choice: "cancel" })
|
|
470
|
+
};
|
|
471
|
+
var ConfirmButtons = class extends _seedcord_core.RowComponent {
|
|
472
|
+
constructor(options) {
|
|
473
|
+
super("button");
|
|
474
|
+
this.instance.addComponents(new _discordjs_builders.ButtonBuilder().setCustomId(CONFIRM_IDS.confirm).setLabel(options?.confirmLabel ?? "Confirm").setStyle(options?.confirmStyle ?? discord_js.ButtonStyle.Danger), new _discordjs_builders.ButtonBuilder().setCustomId(CONFIRM_IDS.cancel).setLabel(options?.cancelLabel ?? "Cancel").setStyle(discord_js.ButtonStyle.Secondary));
|
|
475
|
+
}
|
|
476
|
+
};
|
|
477
|
+
var ConfirmPromptCard = class extends _seedcord_core.BuilderComponent {
|
|
478
|
+
constructor(text, options) {
|
|
479
|
+
super("container");
|
|
480
|
+
this.instance.addTextDisplayComponents(new _discordjs_builders.TextDisplayBuilder().setContent(text)).addActionRowComponents(new ConfirmButtons(options).component);
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
function defaultPrompt(text, options) {
|
|
484
|
+
return { components: [new ConfirmPromptCard(text, options).component] };
|
|
705
485
|
}
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
async function fetchManyGuildMembers(guild, userIds) {
|
|
717
|
-
return (await Promise.allSettled(userIds.map((userId) => fetchGuildMember(guild, userId)))).filter((result) => result.status === "fulfilled").map((result) => result.value);
|
|
486
|
+
async function collectChoice(message, userId, timeoutMs) {
|
|
487
|
+
try {
|
|
488
|
+
return await message.awaitMessageComponent({
|
|
489
|
+
componentType: discord_js.ComponentType.Button,
|
|
490
|
+
time: timeoutMs,
|
|
491
|
+
filter: (click) => click.user.id === userId && CONFIRM_DEF.owns(click.customId)
|
|
492
|
+
});
|
|
493
|
+
} catch {
|
|
494
|
+
return null;
|
|
495
|
+
}
|
|
718
496
|
}
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
const
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
}
|
|
737
|
-
|
|
497
|
+
async function settle(sender, message, outcome) {
|
|
498
|
+
if (outcome === void 0) {
|
|
499
|
+
await sender.delete(message).catch(() => void 0);
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
await sender.edit(message, typeof outcome === "function" ? await outcome() : outcome);
|
|
503
|
+
}
|
|
504
|
+
async function getConfirmation(handler, prompt, options) {
|
|
505
|
+
const { ephemeral = true, timeoutMs = DEFAULT_TIMEOUT_MS } = options ?? {};
|
|
506
|
+
const response = typeof prompt === "string" ? defaultPrompt(prompt, options) : await prompt(CONFIRM_IDS);
|
|
507
|
+
const interaction = handler.getEvent();
|
|
508
|
+
const sender = handler.getSender();
|
|
509
|
+
const message = await sender.send(response, { ephemeral });
|
|
510
|
+
const winner = await collectChoice(message, interaction.user.id, timeoutMs);
|
|
511
|
+
if (!winner) {
|
|
512
|
+
await settle(sender, message, options?.onTimeout);
|
|
513
|
+
return false;
|
|
514
|
+
}
|
|
515
|
+
await winner.deferUpdate().catch(() => void 0);
|
|
516
|
+
const confirmed = winner.customId === CONFIRM_IDS.confirm;
|
|
517
|
+
await settle(sender, message, confirmed ? options?.onConfirm : options?.onCancel);
|
|
518
|
+
return confirmed;
|
|
738
519
|
}
|
|
739
520
|
|
|
740
521
|
//#endregion
|
|
741
|
-
//#region src/
|
|
522
|
+
//#region src/handlers/BaseHandler.ts
|
|
742
523
|
/**
|
|
743
|
-
*
|
|
744
|
-
*
|
|
745
|
-
* @param client - The Discord client instance
|
|
746
|
-
* @param userIds - Array of Discord user IDs
|
|
747
|
-
* @param options - Optional overrides threaded to each {@link fetchUser} call
|
|
748
|
-
* @returns Promise resolving to array of successfully fetched users
|
|
524
|
+
* The gateway base, fixes the core base's `TCore` to the gateway {@link Core} and adds the `getEvent`
|
|
525
|
+
* gate hook.
|
|
749
526
|
*/
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
527
|
+
var BaseHandler = class extends _seedcord_core.BaseHandler {
|
|
528
|
+
/** @internal */
|
|
529
|
+
getEvent() {
|
|
530
|
+
return this.event;
|
|
531
|
+
}
|
|
532
|
+
};
|
|
753
533
|
|
|
754
534
|
//#endregion
|
|
755
|
-
//#region src/
|
|
535
|
+
//#region src/handlers/interaction/InteractionMiddleware.ts
|
|
756
536
|
/**
|
|
757
|
-
*
|
|
537
|
+
* Base class for interaction middleware
|
|
758
538
|
*
|
|
759
|
-
*
|
|
760
|
-
*
|
|
761
|
-
*
|
|
762
|
-
* @
|
|
539
|
+
* Middleware runs before interaction handlers and can modify behavior or block execution.
|
|
540
|
+
* Unlike handlers, middleware should not send responses directly.
|
|
541
|
+
*
|
|
542
|
+
* @typeParam Repliable - The interaction type this middleware processes
|
|
763
543
|
*/
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
await member.roles.set([...updated]);
|
|
770
|
-
}
|
|
544
|
+
var InteractionMiddleware = class extends BaseHandler {
|
|
545
|
+
constructor(event, core) {
|
|
546
|
+
super(event, core, void 0, "interactions");
|
|
547
|
+
}
|
|
548
|
+
};
|
|
771
549
|
|
|
772
550
|
//#endregion
|
|
773
551
|
//#region src/bot/ReplySender.ts
|
|
@@ -879,266 +657,6 @@ var ReplySender = class extends _seedcord_core_internal.BaseReplySender {
|
|
|
879
657
|
}
|
|
880
658
|
};
|
|
881
659
|
|
|
882
|
-
//#endregion
|
|
883
|
-
//#region src/bot/utilities/miscellaneous/slashRouteOf.ts
|
|
884
|
-
/**
|
|
885
|
-
* Computes the dispatch route for a chat-input or autocomplete interaction. The controller and
|
|
886
|
-
* `seedcord codegen` both assemble route keys through the same {@link buildSlashRoute}, so a dispatched
|
|
887
|
-
* interaction and a generated registry key always match.
|
|
888
|
-
*
|
|
889
|
-
* @internal
|
|
890
|
-
*/
|
|
891
|
-
function slashRouteOf(interaction) {
|
|
892
|
-
const group = interaction.options.getSubcommandGroup(false) ?? void 0;
|
|
893
|
-
const subcommand = interaction.options.getSubcommand(false) ?? void 0;
|
|
894
|
-
return (0, _seedcord_utils_internal.buildSlashRoute)(interaction.commandName, subcommand, group);
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
//#endregion
|
|
898
|
-
//#region src/miscellaneous/extractErrorResponse.ts
|
|
899
|
-
const logger$2 = new _seedcord_logger.Logger("ErrorsHandling", { channel: "errors" });
|
|
900
|
-
/**
|
|
901
|
-
* Processes an error into a user-facing reply and routes its report.
|
|
902
|
-
*
|
|
903
|
-
* A {@link Notice} always renders its own reply. A reporting denial (`report: true`) additionally
|
|
904
|
-
* publishes `handledException` on the interaction path. A raw, non-denial throw publishes
|
|
905
|
-
* `unknownException` and renders the configured `defaultError` (or a generic {@link Fault}). One uuid is
|
|
906
|
-
* threaded into both the reply and the bus payload.
|
|
907
|
-
*/
|
|
908
|
-
function extractErrorResponse(error, core, origin) {
|
|
909
|
-
const uuid = node_crypto.randomUUID();
|
|
910
|
-
const developerUsername = core.config.notifications?.developerUsername;
|
|
911
|
-
const ctx = developerUsername === void 0 ? { uuid } : {
|
|
912
|
-
uuid,
|
|
913
|
-
developerUsername
|
|
914
|
-
};
|
|
915
|
-
if (error instanceof _seedcord_core.Notice) {
|
|
916
|
-
if (error.report) reportFault(error, core, origin, uuid);
|
|
917
|
-
return {
|
|
918
|
-
uuid,
|
|
919
|
-
response: error.render(ctx)
|
|
920
|
-
};
|
|
921
|
-
}
|
|
922
|
-
reportRawFault(error, core, origin, uuid);
|
|
923
|
-
const override = core.config.errors?.defaultError;
|
|
924
|
-
return {
|
|
925
|
-
uuid,
|
|
926
|
-
response: override ? new override(uuid).render(ctx) : new _seedcord_core.Fault().render(ctx)
|
|
927
|
-
};
|
|
928
|
-
}
|
|
929
|
-
function withThrottle(core, origin, error, publish) {
|
|
930
|
-
const throttle = _seedcord_core_internal.FaultThrottle.for(core);
|
|
931
|
-
const key = faultKey(origin, error);
|
|
932
|
-
if (!throttle.shouldReport(key)) {
|
|
933
|
-
logger$2.debug(`throttled duplicate fault ${key}`);
|
|
934
|
-
return;
|
|
935
|
-
}
|
|
936
|
-
publish();
|
|
937
|
-
throttle.markReported(key);
|
|
938
|
-
}
|
|
939
|
-
function reportFault(denial, core, origin, uuid) {
|
|
940
|
-
logger$2.error(`${denial.name}: ${uuid}`, denial);
|
|
941
|
-
withThrottle(core, origin, denial, () => {
|
|
942
|
-
if (origin.interaction) core.bus[_seedcord_core_internal.PublishDefault]("handledException", {
|
|
943
|
-
denial,
|
|
944
|
-
uuid,
|
|
945
|
-
source: buildInteractionSource(origin.interaction)
|
|
946
|
-
});
|
|
947
|
-
else if (origin.event) core.bus[_seedcord_core_internal.PublishDefault]("handledException", {
|
|
948
|
-
denial,
|
|
949
|
-
uuid,
|
|
950
|
-
source: buildEventSource(origin.event, origin)
|
|
951
|
-
});
|
|
952
|
-
else core.bus[_seedcord_core_internal.PublishDefault]("unknownException", {
|
|
953
|
-
uuid,
|
|
954
|
-
error: denial,
|
|
955
|
-
...scalarActors(origin),
|
|
956
|
-
metadata: metadataFor(origin)
|
|
957
|
-
});
|
|
958
|
-
});
|
|
959
|
-
}
|
|
960
|
-
function causeLine(error) {
|
|
961
|
-
const { cause } = error;
|
|
962
|
-
if (!Error.isError(cause)) return "";
|
|
963
|
-
const [first] = cause.message.split("\n");
|
|
964
|
-
return `\ncaused by ${cause.name}: ${first ?? cause.message}`;
|
|
965
|
-
}
|
|
966
|
-
function reportRawFault(error, core, origin, uuid) {
|
|
967
|
-
if (core.config.errors?.errorStack ?? false) logger$2.error(uuid, error);
|
|
968
|
-
else logger$2.error(`${uuid} | ${error.message}${causeLine(error)}`);
|
|
969
|
-
withThrottle(core, origin, error, () => {
|
|
970
|
-
core.bus[_seedcord_core_internal.PublishDefault]("unknownException", {
|
|
971
|
-
uuid,
|
|
972
|
-
error,
|
|
973
|
-
...scalarActors(origin),
|
|
974
|
-
metadata: metadataFor(origin)
|
|
975
|
-
});
|
|
976
|
-
});
|
|
977
|
-
}
|
|
978
|
-
function scalarActors(origin) {
|
|
979
|
-
return {
|
|
980
|
-
guild: origin.guild ? {
|
|
981
|
-
id: origin.guild.id,
|
|
982
|
-
name: origin.guild.name
|
|
983
|
-
} : void 0,
|
|
984
|
-
user: origin.user ? {
|
|
985
|
-
id: origin.user.id,
|
|
986
|
-
username: origin.user.username
|
|
987
|
-
} : void 0
|
|
988
|
-
};
|
|
989
|
-
}
|
|
990
|
-
function metadataFor(origin) {
|
|
991
|
-
if (origin.event) return {
|
|
992
|
-
eventName: origin.event.name,
|
|
993
|
-
handler: origin.event.handler,
|
|
994
|
-
args: origin.event.args
|
|
995
|
-
};
|
|
996
|
-
return origin.metadata;
|
|
997
|
-
}
|
|
998
|
-
function buildEventSource(event, origin) {
|
|
999
|
-
return {
|
|
1000
|
-
kind: "event",
|
|
1001
|
-
eventName: event.name,
|
|
1002
|
-
handler: event.handler,
|
|
1003
|
-
userId: origin.user?.id ?? null,
|
|
1004
|
-
guildId: origin.guild?.id ?? null,
|
|
1005
|
-
channelId: event.channelId,
|
|
1006
|
-
raw: event.args
|
|
1007
|
-
};
|
|
1008
|
-
}
|
|
1009
|
-
function faultKey(origin, error) {
|
|
1010
|
-
const name = error instanceof _seedcord_core.Notice ? error.name : error instanceof discord_js.DiscordAPIError ? String(error.code) : error.constructor.name;
|
|
1011
|
-
if (origin.event) return `${origin.event.name}:${origin.event.handler}:${name}`;
|
|
1012
|
-
if (origin.interaction) return `${interactionRoute(origin.interaction)}:${name}`;
|
|
1013
|
-
if (origin.route) return `autocomplete:${origin.route}:${name}`;
|
|
1014
|
-
return `autocomplete:${name}`;
|
|
1015
|
-
}
|
|
1016
|
-
/** The stable route id of a repliable interaction, its slash route path, command name, or customId prefix. */
|
|
1017
|
-
function interactionRoute(interaction) {
|
|
1018
|
-
if (interaction.isChatInputCommand()) return slashRouteOf(interaction);
|
|
1019
|
-
if (interaction.isContextMenuCommand()) return interaction.commandName;
|
|
1020
|
-
if (interaction.isButton() || interaction.isAnySelectMenu() || interaction.isModalSubmit()) return (0, _seedcord_core_internal.prefixOf)(interaction.customId) || interaction.customId;
|
|
1021
|
-
return "interaction";
|
|
1022
|
-
}
|
|
1023
|
-
function buildInteractionSource(interaction) {
|
|
1024
|
-
const command = interaction.isChatInputCommand() ? slashRouteOf(interaction) : interaction.isContextMenuCommand() ? interaction.commandName : null;
|
|
1025
|
-
const customId = interaction.isButton() || interaction.isAnySelectMenu() || interaction.isModalSubmit() ? interaction.customId : null;
|
|
1026
|
-
return {
|
|
1027
|
-
kind: "interaction",
|
|
1028
|
-
interactionKind: interactionKind(interaction),
|
|
1029
|
-
command,
|
|
1030
|
-
customId,
|
|
1031
|
-
userId: interaction.user.id,
|
|
1032
|
-
guildId: interaction.guildId,
|
|
1033
|
-
channelId: interaction.channelId,
|
|
1034
|
-
interactionId: interaction.id,
|
|
1035
|
-
raw: interaction
|
|
1036
|
-
};
|
|
1037
|
-
}
|
|
1038
|
-
function interactionKind(interaction) {
|
|
1039
|
-
if (interaction.isChatInputCommand()) return "slash";
|
|
1040
|
-
if (interaction.isContextMenuCommand()) return "context-menu";
|
|
1041
|
-
if (interaction.isButton()) return "button";
|
|
1042
|
-
if (interaction.isAnySelectMenu()) return "select";
|
|
1043
|
-
return "modal";
|
|
1044
|
-
}
|
|
1045
|
-
|
|
1046
|
-
//#endregion
|
|
1047
|
-
//#region src/bot/confirm/reserved.ts
|
|
1048
|
-
const RESERVED_CONFIRM_PREFIX = "__seedcord_confirm";
|
|
1049
|
-
/**
|
|
1050
|
-
* The reserved confirm/cancel codec. It has no route, so the dispatcher ignores its clicks and only the
|
|
1051
|
-
* in-process collector reads them. Do not mint a real component from it.
|
|
1052
|
-
*
|
|
1053
|
-
* @internal
|
|
1054
|
-
*/
|
|
1055
|
-
const CONFIRM_DEF = new _seedcord_core.CustomId(RESERVED_CONFIRM_PREFIX).oneOf("choice", ["confirm", "cancel"]);
|
|
1056
|
-
|
|
1057
|
-
//#endregion
|
|
1058
|
-
//#region src/bot/confirm/getConfirmation.ts
|
|
1059
|
-
const DEFAULT_TIMEOUT_MS = 3e4;
|
|
1060
|
-
const CONFIRM_IDS = {
|
|
1061
|
-
confirm: CONFIRM_DEF.encode({ choice: "confirm" }),
|
|
1062
|
-
cancel: CONFIRM_DEF.encode({ choice: "cancel" })
|
|
1063
|
-
};
|
|
1064
|
-
var ConfirmButtons = class extends _seedcord_core.RowComponent {
|
|
1065
|
-
constructor(options) {
|
|
1066
|
-
super("button");
|
|
1067
|
-
this.instance.addComponents(new _discordjs_builders.ButtonBuilder().setCustomId(CONFIRM_IDS.confirm).setLabel(options?.confirmLabel ?? "Confirm").setStyle(options?.confirmStyle ?? discord_js.ButtonStyle.Danger), new _discordjs_builders.ButtonBuilder().setCustomId(CONFIRM_IDS.cancel).setLabel(options?.cancelLabel ?? "Cancel").setStyle(discord_js.ButtonStyle.Secondary));
|
|
1068
|
-
}
|
|
1069
|
-
};
|
|
1070
|
-
var ConfirmPromptCard = class extends _seedcord_core.BuilderComponent {
|
|
1071
|
-
constructor(text, options) {
|
|
1072
|
-
super("container");
|
|
1073
|
-
this.instance.addTextDisplayComponents(new _discordjs_builders.TextDisplayBuilder().setContent(text)).addActionRowComponents(new ConfirmButtons(options).component);
|
|
1074
|
-
}
|
|
1075
|
-
};
|
|
1076
|
-
function defaultPrompt(text, options) {
|
|
1077
|
-
return { components: [new ConfirmPromptCard(text, options).component] };
|
|
1078
|
-
}
|
|
1079
|
-
async function collectChoice(message, userId, timeoutMs) {
|
|
1080
|
-
try {
|
|
1081
|
-
return await message.awaitMessageComponent({
|
|
1082
|
-
componentType: discord_js.ComponentType.Button,
|
|
1083
|
-
time: timeoutMs,
|
|
1084
|
-
filter: (click) => click.user.id === userId && CONFIRM_DEF.owns(click.customId)
|
|
1085
|
-
});
|
|
1086
|
-
} catch {
|
|
1087
|
-
return null;
|
|
1088
|
-
}
|
|
1089
|
-
}
|
|
1090
|
-
async function settle(sender, message, outcome) {
|
|
1091
|
-
if (outcome === void 0) {
|
|
1092
|
-
await sender.delete(message).catch(() => void 0);
|
|
1093
|
-
return;
|
|
1094
|
-
}
|
|
1095
|
-
await sender.edit(message, typeof outcome === "function" ? await outcome() : outcome);
|
|
1096
|
-
}
|
|
1097
|
-
async function getConfirmation(interaction, prompt, options) {
|
|
1098
|
-
const { ephemeral = true, timeoutMs = DEFAULT_TIMEOUT_MS } = options ?? {};
|
|
1099
|
-
const response = typeof prompt === "string" ? defaultPrompt(prompt, options) : await prompt(CONFIRM_IDS);
|
|
1100
|
-
const sender = new ReplySender(interaction, interactionRoute(interaction));
|
|
1101
|
-
const message = await sender.send(response, { ephemeral });
|
|
1102
|
-
const winner = await collectChoice(message, interaction.user.id, timeoutMs);
|
|
1103
|
-
if (!winner) {
|
|
1104
|
-
await settle(sender, message, options?.onTimeout);
|
|
1105
|
-
return false;
|
|
1106
|
-
}
|
|
1107
|
-
await winner.deferUpdate().catch(() => void 0);
|
|
1108
|
-
const confirmed = winner.customId === CONFIRM_IDS.confirm;
|
|
1109
|
-
await settle(sender, message, confirmed ? options?.onConfirm : options?.onCancel);
|
|
1110
|
-
return confirmed;
|
|
1111
|
-
}
|
|
1112
|
-
|
|
1113
|
-
//#endregion
|
|
1114
|
-
//#region src/handlers/BaseHandler.ts
|
|
1115
|
-
/**
|
|
1116
|
-
* The gateway base, fixes the core base's `TCore` to the gateway {@link Core} and adds the `getEvent`
|
|
1117
|
-
* gate hook.
|
|
1118
|
-
*/
|
|
1119
|
-
var BaseHandler = class extends _seedcord_core.BaseHandler {
|
|
1120
|
-
/** @internal */
|
|
1121
|
-
getEvent() {
|
|
1122
|
-
return this.event;
|
|
1123
|
-
}
|
|
1124
|
-
};
|
|
1125
|
-
|
|
1126
|
-
//#endregion
|
|
1127
|
-
//#region src/handlers/interaction/InteractionMiddleware.ts
|
|
1128
|
-
/**
|
|
1129
|
-
* Base class for interaction middleware
|
|
1130
|
-
*
|
|
1131
|
-
* Middleware runs before interaction handlers and can modify behavior or block execution.
|
|
1132
|
-
* Unlike handlers, middleware should not send responses directly.
|
|
1133
|
-
*
|
|
1134
|
-
* @typeParam Repliable - The interaction type this middleware processes
|
|
1135
|
-
*/
|
|
1136
|
-
var InteractionMiddleware = class extends BaseHandler {
|
|
1137
|
-
constructor(event, core) {
|
|
1138
|
-
super(event, core, void 0, "interactions");
|
|
1139
|
-
}
|
|
1140
|
-
};
|
|
1141
|
-
|
|
1142
660
|
//#endregion
|
|
1143
661
|
//#region src/handlers/RepliableHandler.ts
|
|
1144
662
|
/**
|
|
@@ -1184,6 +702,21 @@ var InteractionHandler = class extends RepliableHandler {
|
|
|
1184
702
|
}
|
|
1185
703
|
};
|
|
1186
704
|
|
|
705
|
+
//#endregion
|
|
706
|
+
//#region src/bot/utilities/miscellaneous/slashRouteOf.ts
|
|
707
|
+
/**
|
|
708
|
+
* Computes the dispatch route for a chat-input or autocomplete interaction. The controller and
|
|
709
|
+
* `seedcord codegen` both assemble route keys through the same {@link buildSlashRoute}, so a dispatched
|
|
710
|
+
* interaction and a generated registry key always match.
|
|
711
|
+
*
|
|
712
|
+
* @internal
|
|
713
|
+
*/
|
|
714
|
+
function slashRouteOf(interaction) {
|
|
715
|
+
const group = interaction.options.getSubcommandGroup(false) ?? void 0;
|
|
716
|
+
const subcommand = interaction.options.getSubcommand(false) ?? void 0;
|
|
717
|
+
return (0, _seedcord_utils_internal.buildSlashRoute)(interaction.commandName, subcommand, group);
|
|
718
|
+
}
|
|
719
|
+
|
|
1187
720
|
//#endregion
|
|
1188
721
|
//#region src/handlers/interaction/SlashHandler.ts
|
|
1189
722
|
/**
|
|
@@ -1503,7 +1036,22 @@ var ButtonHandler = class extends ComponentHandler {};
|
|
|
1503
1036
|
* }
|
|
1504
1037
|
* ```
|
|
1505
1038
|
*/
|
|
1506
|
-
var ModalHandler = class extends ComponentHandler {
|
|
1039
|
+
var ModalHandler = class extends ComponentHandler {
|
|
1040
|
+
/** Rewrite the message this modal was opened from. Throws when a command opened the modal. */
|
|
1041
|
+
update(response) {
|
|
1042
|
+
this.ensureSourceMessage("update");
|
|
1043
|
+
return super.update(response);
|
|
1044
|
+
}
|
|
1045
|
+
/** Acknowledge the submit without changing the source message. Throws when a command opened the modal. */
|
|
1046
|
+
deferUpdate() {
|
|
1047
|
+
this.ensureSourceMessage("deferUpdate");
|
|
1048
|
+
return super.deferUpdate();
|
|
1049
|
+
}
|
|
1050
|
+
ensureSourceMessage(method) {
|
|
1051
|
+
if (this.event.isFromMessage()) return;
|
|
1052
|
+
throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.ReplyUpdateWithoutSource, [method, this.routeId]);
|
|
1053
|
+
}
|
|
1054
|
+
};
|
|
1507
1055
|
|
|
1508
1056
|
//#endregion
|
|
1509
1057
|
//#region src/handlers/interaction/components/SelectMenuHandler.ts
|
|
@@ -1617,138 +1165,14 @@ var EventMiddleware = class extends BaseHandler {
|
|
|
1617
1165
|
}
|
|
1618
1166
|
/**
|
|
1619
1167
|
* The event that fired this middleware. Read it on a catchall or multi-event middleware, where `this.event`
|
|
1620
|
-
* is `never`, to do work that does not depend on the payload shape. A single-event middleware reads
|
|
1621
|
-
* `this.event` directly and does not need this.
|
|
1622
|
-
*/
|
|
1623
|
-
get eventName() {
|
|
1624
|
-
if (this.firedEvent === void 0) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.EventMiddlewareNameUnavailable);
|
|
1625
|
-
return this.firedEvent;
|
|
1626
|
-
}
|
|
1627
|
-
};
|
|
1628
|
-
|
|
1629
|
-
//#endregion
|
|
1630
|
-
//#region src/pagination/controls.ts
|
|
1631
|
-
const DEFAULT_LABEL = {
|
|
1632
|
-
first: "First",
|
|
1633
|
-
prev: "Prev",
|
|
1634
|
-
indicator: "",
|
|
1635
|
-
next: "Next",
|
|
1636
|
-
last: "Last"
|
|
1637
|
-
};
|
|
1638
|
-
const CONTROL_SLOT = {
|
|
1639
|
-
first: 0,
|
|
1640
|
-
prev: 1,
|
|
1641
|
-
indicator: 2,
|
|
1642
|
-
next: 3,
|
|
1643
|
-
last: 4
|
|
1644
|
-
};
|
|
1645
|
-
const MAX_BUTTONS_PER_ROW = 5;
|
|
1646
|
-
var Controls = class {
|
|
1647
|
-
cursor;
|
|
1648
|
-
view;
|
|
1649
|
-
constructor(cursor, view) {
|
|
1650
|
-
this.cursor = cursor;
|
|
1651
|
-
this.view = view;
|
|
1652
|
-
}
|
|
1653
|
-
button(key, cosmetics) {
|
|
1654
|
-
const { target, disabled } = this.navState(key);
|
|
1655
|
-
const defaultLabel = key === "indicator" ? this.indicatorText() : DEFAULT_LABEL[key];
|
|
1656
|
-
const label = cosmetics?.label ?? (key === "indicator" || !cosmetics?.emoji ? defaultLabel : void 0);
|
|
1657
|
-
const button = new _discordjs_builders.ButtonBuilder().setCustomId(this.cursor.encode({
|
|
1658
|
-
page: Math.min(Math.max(0, target), _seedcord_core_internal.PAGE_MAX),
|
|
1659
|
-
slot: CONTROL_SLOT[key]
|
|
1660
|
-
})).setStyle(cosmetics?.style ?? discord_js.ButtonStyle.Secondary).setDisabled(disabled);
|
|
1661
|
-
if (label !== void 0) button.setLabel(label);
|
|
1662
|
-
if (cosmetics?.emoji) button.setEmoji(cosmetics.emoji);
|
|
1663
|
-
return button;
|
|
1664
|
-
}
|
|
1665
|
-
navState(key) {
|
|
1666
|
-
const { page, totalPages, hasPrev, hasNext } = this.view;
|
|
1667
|
-
const knownTotal = totalPages !== void 0;
|
|
1668
|
-
return {
|
|
1669
|
-
target: {
|
|
1670
|
-
first: 0,
|
|
1671
|
-
prev: page - 1,
|
|
1672
|
-
indicator: page,
|
|
1673
|
-
next: page + 1,
|
|
1674
|
-
last: knownTotal ? totalPages - 1 : page
|
|
1675
|
-
}[key],
|
|
1676
|
-
disabled: {
|
|
1677
|
-
first: !hasPrev,
|
|
1678
|
-
prev: !hasPrev,
|
|
1679
|
-
indicator: true,
|
|
1680
|
-
next: !hasNext || page + 1 > _seedcord_core_internal.PAGE_MAX,
|
|
1681
|
-
last: !hasNext || !knownTotal || totalPages - 1 > _seedcord_core_internal.PAGE_MAX
|
|
1682
|
-
}[key]
|
|
1683
|
-
};
|
|
1684
|
-
}
|
|
1685
|
-
row(...keys) {
|
|
1686
|
-
if (keys.length === 0) throw new _seedcord_errors_internal.SeedcordRangeError(_seedcord_errors.SeedcordErrorCode.PaginationEmptyControls);
|
|
1687
|
-
if (keys.length > MAX_BUTTONS_PER_ROW) throw new _seedcord_errors_internal.SeedcordRangeError(_seedcord_errors.SeedcordErrorCode.PaginationTooManyControls, [keys.length]);
|
|
1688
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1689
|
-
for (const key of keys) {
|
|
1690
|
-
if (seen.has(key)) throw new _seedcord_errors_internal.SeedcordTypeError(_seedcord_errors.SeedcordErrorCode.PaginationDuplicateControls, [key]);
|
|
1691
|
-
seen.add(key);
|
|
1692
|
-
}
|
|
1693
|
-
return new _discordjs_builders.ActionRowBuilder().addComponents(keys.map((key) => this.button(key)));
|
|
1694
|
-
}
|
|
1695
|
-
indicatorText() {
|
|
1696
|
-
const { page, totalPages } = this.view;
|
|
1697
|
-
return totalPages === void 0 ? `Page ${page + 1}` : `Page ${page + 1} of ${totalPages}`;
|
|
1698
|
-
}
|
|
1699
|
-
};
|
|
1700
|
-
|
|
1701
|
-
//#endregion
|
|
1702
|
-
//#region src/pagination/render.ts
|
|
1703
|
-
var PageContainer = class extends _seedcord_core.BuilderComponent {
|
|
1704
|
-
constructor(view, renderItem, controlRow) {
|
|
1705
|
-
super("container");
|
|
1706
|
-
const base = view.page * view.perPage;
|
|
1707
|
-
let lines = [];
|
|
1708
|
-
const flush = () => {
|
|
1709
|
-
if (lines.length === 0) return;
|
|
1710
|
-
this.instance.addTextDisplayComponents(new _discordjs_builders.TextDisplayBuilder().setContent(lines.join("\n")));
|
|
1711
|
-
lines = [];
|
|
1712
|
-
};
|
|
1713
|
-
view.items.forEach((item, offset) => {
|
|
1714
|
-
const rendered = renderItem(item, base + offset);
|
|
1715
|
-
if (typeof rendered === "string") {
|
|
1716
|
-
lines.push(rendered);
|
|
1717
|
-
return;
|
|
1718
|
-
}
|
|
1719
|
-
flush();
|
|
1720
|
-
this.instance.addSectionComponents(rendered.component);
|
|
1721
|
-
});
|
|
1722
|
-
flush();
|
|
1723
|
-
this.instance.addActionRowComponents(controlRow);
|
|
1168
|
+
* is `never`, to do work that does not depend on the payload shape. A single-event middleware reads
|
|
1169
|
+
* `this.event` directly and does not need this.
|
|
1170
|
+
*/
|
|
1171
|
+
get eventName() {
|
|
1172
|
+
if (this.firedEvent === void 0) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.EventMiddlewareNameUnavailable);
|
|
1173
|
+
return this.firedEvent;
|
|
1724
1174
|
}
|
|
1725
1175
|
};
|
|
1726
|
-
function toReplyResponse(renderable) {
|
|
1727
|
-
if (typeof renderable === "string") return { components: [new _discordjs_builders.TextDisplayBuilder().setContent(renderable)] };
|
|
1728
|
-
if (Array.isArray(renderable)) return { components: renderable };
|
|
1729
|
-
return renderable;
|
|
1730
|
-
}
|
|
1731
|
-
const ARRAY_KEYS = [
|
|
1732
|
-
"first",
|
|
1733
|
-
"prev",
|
|
1734
|
-
"indicator",
|
|
1735
|
-
"next",
|
|
1736
|
-
"last"
|
|
1737
|
-
];
|
|
1738
|
-
const CURSOR_KEYS = [
|
|
1739
|
-
"prev",
|
|
1740
|
-
"indicator",
|
|
1741
|
-
"next"
|
|
1742
|
-
];
|
|
1743
|
-
/**
|
|
1744
|
-
* Build a page's V2 reply. A `render` override builds the whole tree, otherwise the default container lists
|
|
1745
|
-
* the items and appends the controls (all five for a known total, prev/indicator/next for a cursor source).
|
|
1746
|
-
*/
|
|
1747
|
-
function renderPage(view, cursor, config) {
|
|
1748
|
-
const controls = new Controls(cursor, view);
|
|
1749
|
-
if (config.render) return toReplyResponse(config.render(view, controls));
|
|
1750
|
-
return { components: [new PageContainer(view, config.renderItem ?? String, controls.row(...view.totalPages === void 0 ? CURSOR_KEYS : ARRAY_KEYS)).component] };
|
|
1751
|
-
}
|
|
1752
1176
|
|
|
1753
1177
|
//#endregion
|
|
1754
1178
|
//#region src/pagination/Paginator.ts
|
|
@@ -1757,7 +1181,7 @@ function contextOf(interaction, core) {
|
|
|
1757
1181
|
interaction,
|
|
1758
1182
|
user: interaction.user,
|
|
1759
1183
|
guild: interaction.guild,
|
|
1760
|
-
|
|
1184
|
+
core
|
|
1761
1185
|
};
|
|
1762
1186
|
}
|
|
1763
1187
|
/**
|
|
@@ -1779,15 +1203,11 @@ function contextOf(interaction, core) {
|
|
|
1779
1203
|
* export class BansNav extends Bans.Handler {}
|
|
1780
1204
|
* ```
|
|
1781
1205
|
*/
|
|
1782
|
-
var Paginator = class {
|
|
1783
|
-
/** The page cursor, pass it to your `@ButtonRoute`. */
|
|
1784
|
-
cursor;
|
|
1206
|
+
var Paginator = class extends _seedcord_core.PaginatorBase {
|
|
1785
1207
|
/** The nav handler base. Extend it with an empty body and decorate it, `@ButtonRoute(p.cursor)`. */
|
|
1786
1208
|
Handler;
|
|
1787
|
-
config;
|
|
1788
1209
|
constructor(config) {
|
|
1789
|
-
|
|
1790
|
-
this.config = config;
|
|
1210
|
+
super(config);
|
|
1791
1211
|
const loadPage = (ctx, n) => this.page(ctx, n);
|
|
1792
1212
|
this.Handler = class Nav extends ButtonHandler {
|
|
1793
1213
|
async execute() {
|
|
@@ -1798,7 +1218,8 @@ var Paginator = class {
|
|
|
1798
1218
|
};
|
|
1799
1219
|
}
|
|
1800
1220
|
/**
|
|
1801
|
-
* Render page 0 and send it through the handler's sender,
|
|
1221
|
+
* Render page 0 and send it through the handler's sender, which picks reply, edit, or followUp from its
|
|
1222
|
+
* ack state.
|
|
1802
1223
|
*
|
|
1803
1224
|
* @param handler - The handler starting the paginator, normally `this`.
|
|
1804
1225
|
*/
|
|
@@ -1807,34 +1228,18 @@ var Paginator = class {
|
|
|
1807
1228
|
const response = await this.page(contextOf(interaction, handler.core), 0);
|
|
1808
1229
|
return handler.getSender().send(response, { ephemeral: this.config.ephemeral ?? false });
|
|
1809
1230
|
}
|
|
1810
|
-
/** Render a page as a {@link ReplyResponse}. To post it elsewhere, add `flags: MessageFlags.IsComponentsV2`. */
|
|
1811
|
-
async page(ctx, n) {
|
|
1812
|
-
return renderPage(await this.config.source.page(ctx, n), this.cursor, this.config);
|
|
1813
|
-
}
|
|
1814
1231
|
};
|
|
1815
1232
|
|
|
1816
1233
|
//#endregion
|
|
1817
1234
|
//#region src/pagination/sources.ts
|
|
1818
|
-
const DEFAULT_PER_PAGE = 10;
|
|
1819
1235
|
/**
|
|
1820
1236
|
* A source for a bounded list you can load whole. It loads the full list on every click, then slices the
|
|
1821
|
-
* page
|
|
1822
|
-
*
|
|
1237
|
+
* page, so the real total is known (a real last button, "Page X of Y"). Cache inside your loader if the
|
|
1238
|
+
* load is expensive.
|
|
1823
1239
|
*
|
|
1824
1240
|
* @typeParam Item - The item type, inferred from the loader's return.
|
|
1825
1241
|
*/
|
|
1826
|
-
var ArraySource = class {
|
|
1827
|
-
load;
|
|
1828
|
-
perPage;
|
|
1829
|
-
constructor(load, opts) {
|
|
1830
|
-
this.load = load;
|
|
1831
|
-
this.perPage = opts?.perPage ?? DEFAULT_PER_PAGE;
|
|
1832
|
-
if (!Number.isSafeInteger(this.perPage) || this.perPage <= 0) throw new _seedcord_errors_internal.SeedcordRangeError(_seedcord_errors.SeedcordErrorCode.PaginationInvalidPerPage, [this.perPage]);
|
|
1833
|
-
}
|
|
1834
|
-
async page(ctx, n) {
|
|
1835
|
-
return (0, _seedcord_core.paginate)(await this.load(ctx), n, this.perPage);
|
|
1836
|
-
}
|
|
1837
|
-
};
|
|
1242
|
+
var ArraySource = class extends _seedcord_core.ArraySource {};
|
|
1838
1243
|
/**
|
|
1839
1244
|
* A source for a large or unknown-length set you fetch one page at a time (SQL LIMIT/OFFSET, a paged API).
|
|
1840
1245
|
* The fetcher receives the page index and the page size and reports whether a next page exists. A cursor
|
|
@@ -1843,26 +1248,7 @@ var ArraySource = class {
|
|
|
1843
1248
|
*
|
|
1844
1249
|
* @typeParam Item - The item type, inferred from the fetcher's slice.
|
|
1845
1250
|
*/
|
|
1846
|
-
var CursorSource = class {
|
|
1847
|
-
fetch;
|
|
1848
|
-
perPage;
|
|
1849
|
-
constructor(fetch, opts) {
|
|
1850
|
-
this.fetch = fetch;
|
|
1851
|
-
this.perPage = opts?.perPage ?? DEFAULT_PER_PAGE;
|
|
1852
|
-
if (!Number.isSafeInteger(this.perPage) || this.perPage <= 0) throw new _seedcord_errors_internal.SeedcordRangeError(_seedcord_errors.SeedcordErrorCode.PaginationInvalidPerPage, [this.perPage]);
|
|
1853
|
-
}
|
|
1854
|
-
async page(ctx, n) {
|
|
1855
|
-
const page = Math.max(0, Math.trunc(n));
|
|
1856
|
-
const { items, hasNext } = await this.fetch(ctx, page, this.perPage);
|
|
1857
|
-
return {
|
|
1858
|
-
items: [...items],
|
|
1859
|
-
page,
|
|
1860
|
-
perPage: this.perPage,
|
|
1861
|
-
hasPrev: page > 0,
|
|
1862
|
-
hasNext
|
|
1863
|
-
};
|
|
1864
|
-
}
|
|
1865
|
-
};
|
|
1251
|
+
var CursorSource = class extends _seedcord_core.CursorSource {};
|
|
1866
1252
|
|
|
1867
1253
|
//#endregion
|
|
1868
1254
|
//#region src/subscribers/index.ts
|
|
@@ -1886,236 +1272,12 @@ var WebhookLog = class extends _seedcord_core.WebhookLog {};
|
|
|
1886
1272
|
* Base class for a gateway plugin, binding `this.core` to the gateway {@link Core}.
|
|
1887
1273
|
*
|
|
1888
1274
|
* A plugin that runs on either transport extends the base from `@seedcord/core/plugin`, whose
|
|
1889
|
-
* `this.core`
|
|
1275
|
+
* `this.core` is typed to the shared members only.
|
|
1890
1276
|
*
|
|
1891
|
-
* @typeParam Opts - The transport
|
|
1277
|
+
* @typeParam Opts - The transport and runtime the plugin declares.
|
|
1892
1278
|
*/
|
|
1893
1279
|
var Plugin = class extends _seedcord_core_plugin.Plugin {};
|
|
1894
1280
|
|
|
1895
|
-
//#endregion
|
|
1896
|
-
//#region src/bot/utilities/miscellaneous/contextMenuLeaves.ts
|
|
1897
|
-
/**
|
|
1898
|
-
* Collects the names of every registered context-menu command across a set of command builders, split into a
|
|
1899
|
-
* user set and a message set. The same builder may appear more than once (a guild command is pushed once per
|
|
1900
|
-
* guild), and slash commands carry no context-menu name, so both collapse here. The split mirrors the
|
|
1901
|
-
* controller's two name-keyed maps, so the boot guard checks each name against its own kind.
|
|
1902
|
-
*
|
|
1903
|
-
* @internal
|
|
1904
|
-
*/
|
|
1905
|
-
function contextMenuLeaves(commands) {
|
|
1906
|
-
const user = /* @__PURE__ */ new Set();
|
|
1907
|
-
const message = /* @__PURE__ */ new Set();
|
|
1908
|
-
for (const command of commands) {
|
|
1909
|
-
if (!(command instanceof _discordjs_builders.ContextMenuCommandBuilder)) continue;
|
|
1910
|
-
if (command.type === discord_js.ApplicationCommandType.User) user.add(command.name);
|
|
1911
|
-
else message.add(command.name);
|
|
1912
|
-
}
|
|
1913
|
-
return {
|
|
1914
|
-
user,
|
|
1915
|
-
message
|
|
1916
|
-
};
|
|
1917
|
-
}
|
|
1918
|
-
|
|
1919
|
-
//#endregion
|
|
1920
|
-
//#region src/bot/utilities/miscellaneous/slashRouteLeaves.ts
|
|
1921
|
-
/**
|
|
1922
|
-
* Collects the route keys of every executable slash leaf across a set of command builders, deduplicated. The
|
|
1923
|
-
* same builder may appear more than once (a guild command is pushed once per guild), and context-menu
|
|
1924
|
-
* commands carry no slash route, so both collapse here. Route keys come from the same {@link routeLeavesOf}
|
|
1925
|
-
* that `seedcord codegen` reads, so the boot-time check compares against the generated registry's keys.
|
|
1926
|
-
*
|
|
1927
|
-
* @internal
|
|
1928
|
-
*/
|
|
1929
|
-
function slashRouteLeaves(commands) {
|
|
1930
|
-
const leaves = /* @__PURE__ */ new Set();
|
|
1931
|
-
for (const command of commands) {
|
|
1932
|
-
if (!(command instanceof _discordjs_builders.SlashCommandBuilder)) continue;
|
|
1933
|
-
for (const leaf of (0, _seedcord_utils_internal.routeLeavesOf)(command.toJSON())) leaves.add(leaf.route);
|
|
1934
|
-
}
|
|
1935
|
-
return leaves;
|
|
1936
|
-
}
|
|
1937
|
-
|
|
1938
|
-
//#endregion
|
|
1939
|
-
//#region src/bot/controllers/CommandRegistry.ts
|
|
1940
|
-
/**
|
|
1941
|
-
* Manages Discord application command registration and deployment.
|
|
1942
|
-
*
|
|
1943
|
-
* Scans command directories, builds command structures, and registers both global and guild-scoped commands
|
|
1944
|
-
* to Discord's API. Accessed via `core.bot.commands`. Do not construct it directly.
|
|
1945
|
-
*/
|
|
1946
|
-
var CommandRegistry = class {
|
|
1947
|
-
core;
|
|
1948
|
-
onDeployed;
|
|
1949
|
-
logger = new _seedcord_logger.Logger("Commands", { channel: "commands" });
|
|
1950
|
-
isInitialised = false;
|
|
1951
|
-
globalCommands = [];
|
|
1952
|
-
guildCommands = new discord_js.Collection();
|
|
1953
|
-
ctorToCommand = /* @__PURE__ */ new Map();
|
|
1954
|
-
loading = false;
|
|
1955
|
-
loadedCommands = [];
|
|
1956
|
-
hmrHandler;
|
|
1957
|
-
pendingEvents = /* @__PURE__ */ new Map();
|
|
1958
|
-
constructor(core, onDeployed) {
|
|
1959
|
-
this.core = core;
|
|
1960
|
-
this.onDeployed = onDeployed;
|
|
1961
|
-
const commandsDir = this.core.config.bot.commands.path;
|
|
1962
|
-
if (!commandsDir) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.CoreControllerPathMissing, ["CommandRegistry", "commands"]);
|
|
1963
|
-
if (!envapt.Envapter.isDevelopment && !envapt.Envapter.isTest) return;
|
|
1964
|
-
this.hmrHandler = new _seedcord_core_hmr.HmrModuleHandler({
|
|
1965
|
-
handlersDir: commandsDir,
|
|
1966
|
-
isHandler: this.isCommandClass.bind(this),
|
|
1967
|
-
registerHandler: this.registerCommand.bind(this),
|
|
1968
|
-
unregisterHandler: this.unregisterCommand.bind(this),
|
|
1969
|
-
getArtifacts: this.getArtifacts.bind(this),
|
|
1970
|
-
logger: this.logger
|
|
1971
|
-
});
|
|
1972
|
-
}
|
|
1973
|
-
getArtifacts(ctor) {
|
|
1974
|
-
return this.ctorToCommand.get(ctor);
|
|
1975
|
-
}
|
|
1976
|
-
/** @internal */
|
|
1977
|
-
async init() {
|
|
1978
|
-
if (this.isInitialised) return;
|
|
1979
|
-
this.isInitialised = true;
|
|
1980
|
-
const commandsDir = this.core.config.bot.commands.path;
|
|
1981
|
-
if (!commandsDir) return;
|
|
1982
|
-
this.loading = true;
|
|
1983
|
-
this.loadedCommands.length = 0;
|
|
1984
|
-
try {
|
|
1985
|
-
await this.loadCommands(commandsDir);
|
|
1986
|
-
} finally {
|
|
1987
|
-
this.loading = false;
|
|
1988
|
-
}
|
|
1989
|
-
this.reportLoad();
|
|
1990
|
-
(0, _seedcord_core_internal.getDevChannel)()?.on("seedcord:refresh-commands", (data) => {
|
|
1991
|
-
this.refresh(data.shouldRefresh);
|
|
1992
|
-
});
|
|
1993
|
-
}
|
|
1994
|
-
reportLoad() {
|
|
1995
|
-
const { utils } = this.logger;
|
|
1996
|
-
utils.summary("Loaded commands", {
|
|
1997
|
-
global: this.globalCommands.length,
|
|
1998
|
-
"guild groups": this.guildCommands.size
|
|
1999
|
-
});
|
|
2000
|
-
let slash = 0;
|
|
2001
|
-
let menu = 0;
|
|
2002
|
-
for (const command of this.loadedCommands) if (command.kind === "slash command") slash++;
|
|
2003
|
-
else menu++;
|
|
2004
|
-
utils.block("Loaded commands", [...utils.entries(this.loadedCommands), ...utils.counts({
|
|
2005
|
-
"slash commands": slash,
|
|
2006
|
-
"context menus": menu
|
|
2007
|
-
})]);
|
|
2008
|
-
}
|
|
2009
|
-
/** @internal */
|
|
2010
|
-
async refresh(shouldRefresh = true) {
|
|
2011
|
-
if (!shouldRefresh) {
|
|
2012
|
-
this.logger.info(chalk.default.italic("Command refresh cancelled."));
|
|
2013
|
-
this.pendingEvents.clear();
|
|
2014
|
-
return;
|
|
2015
|
-
}
|
|
2016
|
-
this.logger.info(chalk.default.italic("Refreshing commands..."));
|
|
2017
|
-
for (const event of this.pendingEvents.values()) await this.hmrHandler?.handle(event);
|
|
2018
|
-
this.pendingEvents.clear();
|
|
2019
|
-
await this.setCommands();
|
|
2020
|
-
}
|
|
2021
|
-
/** @internal */
|
|
2022
|
-
async onHmr(event) {
|
|
2023
|
-
const commandsDir = this.core.config.bot.commands.path;
|
|
2024
|
-
if (commandsDir && event.file.startsWith((0, node_path.resolve)(process.cwd(), commandsDir))) {
|
|
2025
|
-
this.pendingEvents.delete(event.file);
|
|
2026
|
-
this.pendingEvents.set(event.file, event);
|
|
2027
|
-
(0, _seedcord_core_internal.getDevChannel)()?.send("seedcord:commands-update-prompt", { files: [...this.pendingEvents.keys()].map((f) => (0, _seedcord_utils.formatFilePath)(f)) });
|
|
2028
|
-
} else await this.hmrHandler?.handle(event);
|
|
2029
|
-
}
|
|
2030
|
-
async loadCommands(dir) {
|
|
2031
|
-
await (0, _seedcord_utils_node.traverseDirectory)(dir, (fullPath, rel, mod) => {
|
|
2032
|
-
for (const exported of Object.values(mod)) if (this.isCommandClass(exported)) {
|
|
2033
|
-
this.registerCommand(exported, rel);
|
|
2034
|
-
this.hmrHandler?.trackHandler(fullPath, exported);
|
|
2035
|
-
}
|
|
2036
|
-
});
|
|
2037
|
-
}
|
|
2038
|
-
isCommandClass(obj) {
|
|
2039
|
-
if (typeof obj !== "function") return false;
|
|
2040
|
-
return obj.prototype instanceof _seedcord_core.BuilderComponent && Reflect.hasMetadata(_seedcord_core_internal.CommandMetadataKey, obj);
|
|
2041
|
-
}
|
|
2042
|
-
registerCommand(ctor, rel) {
|
|
2043
|
-
const meta = Reflect.getMetadata(_seedcord_core_internal.CommandMetadataKey, ctor);
|
|
2044
|
-
if (!meta) return;
|
|
2045
|
-
const comp = new ctor().component;
|
|
2046
|
-
const kind = comp instanceof _discordjs_builders.SlashCommandBuilder ? "slash command" : "context menu";
|
|
2047
|
-
if (meta.scope === "global") this.globalCommands.push(comp);
|
|
2048
|
-
else for (const g of meta.guilds) {
|
|
2049
|
-
const arr = this.guildCommands.get(g) ?? [];
|
|
2050
|
-
arr.push(comp);
|
|
2051
|
-
this.guildCommands.set(g, arr);
|
|
2052
|
-
}
|
|
2053
|
-
this.ctorToCommand.set(ctor, {
|
|
2054
|
-
name: comp.name,
|
|
2055
|
-
scope: meta.scope,
|
|
2056
|
-
...meta.scope === "guild" && { guilds: meta.guilds }
|
|
2057
|
-
});
|
|
2058
|
-
if (this.loading) {
|
|
2059
|
-
this.loadedCommands.push({
|
|
2060
|
-
name: comp.name,
|
|
2061
|
-
from: (0, _seedcord_utils.formatFilePath)(rel),
|
|
2062
|
-
kind
|
|
2063
|
-
});
|
|
2064
|
-
return;
|
|
2065
|
-
}
|
|
2066
|
-
this.logger.utils.registration(comp.name, rel, `${meta.scope} ${kind}`);
|
|
2067
|
-
}
|
|
2068
|
-
unregisterCommand(ctor, artifacts) {
|
|
2069
|
-
const info = artifacts ?? this.ctorToCommand.get(ctor);
|
|
2070
|
-
if (!info) return;
|
|
2071
|
-
if (info.scope === "global") {
|
|
2072
|
-
const idx = this.globalCommands.findIndex((c) => c.name === info.name);
|
|
2073
|
-
if (idx !== -1) this.globalCommands.splice(idx, 1);
|
|
2074
|
-
} else for (const g of info.guilds ?? []) {
|
|
2075
|
-
const arr = this.guildCommands.get(g);
|
|
2076
|
-
if (arr) {
|
|
2077
|
-
const idx = arr.findIndex((c) => c.name === info.name);
|
|
2078
|
-
if (idx !== -1) arr.splice(idx, 1);
|
|
2079
|
-
}
|
|
2080
|
-
}
|
|
2081
|
-
this.ctorToCommand.delete(ctor);
|
|
2082
|
-
}
|
|
2083
|
-
/** @internal */
|
|
2084
|
-
async setCommands() {
|
|
2085
|
-
const result = {
|
|
2086
|
-
global: new discord_js.Collection(),
|
|
2087
|
-
guilds: new discord_js.Collection()
|
|
2088
|
-
};
|
|
2089
|
-
if (this.globalCommands.length > 0) {
|
|
2090
|
-
const deployed = await this.core.bot.client.application?.commands.set(this.globalCommands);
|
|
2091
|
-
if (deployed) result.global = deployed;
|
|
2092
|
-
const tag = this.globalCommands.length === 1 ? "command" : "commands";
|
|
2093
|
-
this.logger.utils.block(`Deployed ${this.globalCommands.length} global ${tag}`, this.logger.utils.wrap(this.globalCommands.map((command) => command.name)), "info");
|
|
2094
|
-
}
|
|
2095
|
-
for (const [guildId, commands] of this.guildCommands.entries()) {
|
|
2096
|
-
const guild = this.core.bot.client.guilds.cache.get(guildId);
|
|
2097
|
-
if (!guild) {
|
|
2098
|
-
this.logger.warn(`Guild with ID ${guildId} not found, skipping command registration.`);
|
|
2099
|
-
continue;
|
|
2100
|
-
}
|
|
2101
|
-
const deployed = await guild.commands.set(commands);
|
|
2102
|
-
result.guilds.set(guildId, deployed);
|
|
2103
|
-
const tag = commands.length === 1 ? "command" : "commands";
|
|
2104
|
-
this.logger.utils.block(`Deployed ${commands.length} ${tag} to ${_seedcord_logger.paint.amber.bold(guild.name)}`, this.logger.utils.wrap(commands.map((command) => command.name)), "info");
|
|
2105
|
-
}
|
|
2106
|
-
this.onDeployed?.(result);
|
|
2107
|
-
return result;
|
|
2108
|
-
}
|
|
2109
|
-
/** The deduplicated slash route keys across every global and guild command. @internal */
|
|
2110
|
-
routeLeaves() {
|
|
2111
|
-
return slashRouteLeaves([...this.globalCommands, ...this.guildCommands.values()].flat());
|
|
2112
|
-
}
|
|
2113
|
-
/** The registered context-menu command names, split by kind, across every global and guild command. @internal */
|
|
2114
|
-
contextMenuLeaves() {
|
|
2115
|
-
return contextMenuLeaves([...this.globalCommands, ...this.guildCommands.values()].flat());
|
|
2116
|
-
}
|
|
2117
|
-
};
|
|
2118
|
-
|
|
2119
1281
|
//#endregion
|
|
2120
1282
|
//#region src/miscellaneous/deriveEventActor.ts
|
|
2121
1283
|
function deriveEventActor(args) {
|
|
@@ -2193,6 +1355,154 @@ function eventGateContext(eventName, args, core) {
|
|
|
2193
1355
|
};
|
|
2194
1356
|
}
|
|
2195
1357
|
|
|
1358
|
+
//#endregion
|
|
1359
|
+
//#region src/miscellaneous/extractErrorResponse.ts
|
|
1360
|
+
const logger$2 = new _seedcord_logger.Logger("ErrorsHandling", { channel: "errors" });
|
|
1361
|
+
/**
|
|
1362
|
+
* Processes an error into a user-facing reply and routes its report.
|
|
1363
|
+
*
|
|
1364
|
+
* A {@link Notice} always renders its own reply. A reporting denial (`report: true`) additionally
|
|
1365
|
+
* publishes `handledException` on the interaction path. A raw, non-denial throw publishes
|
|
1366
|
+
* `unknownException` and renders the configured `defaultError` (or a generic {@link Fault}). One uuid is
|
|
1367
|
+
* threaded into both the reply and the bus payload.
|
|
1368
|
+
*/
|
|
1369
|
+
function extractErrorResponse(error, core, origin) {
|
|
1370
|
+
const uuid = node_crypto.randomUUID();
|
|
1371
|
+
const developerUsername = core.config.notifications?.developerUsername;
|
|
1372
|
+
const ctx = developerUsername === void 0 ? { uuid } : {
|
|
1373
|
+
uuid,
|
|
1374
|
+
developerUsername
|
|
1375
|
+
};
|
|
1376
|
+
if (error instanceof _seedcord_core.Notice) {
|
|
1377
|
+
if (error.report) reportFault(error, core, origin, uuid);
|
|
1378
|
+
return {
|
|
1379
|
+
uuid,
|
|
1380
|
+
response: error.render(ctx)
|
|
1381
|
+
};
|
|
1382
|
+
}
|
|
1383
|
+
reportRawFault(error, core, origin, uuid);
|
|
1384
|
+
const override = core.config.errors?.defaultError;
|
|
1385
|
+
return {
|
|
1386
|
+
uuid,
|
|
1387
|
+
response: override ? new override(uuid).render(ctx) : new _seedcord_core.Fault().render(ctx)
|
|
1388
|
+
};
|
|
1389
|
+
}
|
|
1390
|
+
function withThrottle(core, origin, error, publish) {
|
|
1391
|
+
const throttle = _seedcord_core_internal.FaultThrottle.for(core);
|
|
1392
|
+
const key = faultKey(origin, error);
|
|
1393
|
+
if (!throttle.shouldReport(key)) {
|
|
1394
|
+
logger$2.debug(`throttled duplicate fault ${key}`);
|
|
1395
|
+
return;
|
|
1396
|
+
}
|
|
1397
|
+
publish();
|
|
1398
|
+
throttle.markReported(key);
|
|
1399
|
+
}
|
|
1400
|
+
function reportFault(denial, core, origin, uuid) {
|
|
1401
|
+
logger$2.error(`${denial.name}: ${uuid}`, denial);
|
|
1402
|
+
withThrottle(core, origin, denial, () => {
|
|
1403
|
+
if (origin.interaction) core.bus[_seedcord_core_internal.PublishDefault]("handledException", {
|
|
1404
|
+
denial,
|
|
1405
|
+
uuid,
|
|
1406
|
+
source: buildInteractionSource(origin.interaction)
|
|
1407
|
+
});
|
|
1408
|
+
else if (origin.event) core.bus[_seedcord_core_internal.PublishDefault]("handledException", {
|
|
1409
|
+
denial,
|
|
1410
|
+
uuid,
|
|
1411
|
+
source: buildEventSource(origin.event, origin)
|
|
1412
|
+
});
|
|
1413
|
+
else core.bus[_seedcord_core_internal.PublishDefault]("unknownException", {
|
|
1414
|
+
uuid,
|
|
1415
|
+
error: denial,
|
|
1416
|
+
...scalarActors(origin),
|
|
1417
|
+
metadata: metadataFor(origin)
|
|
1418
|
+
});
|
|
1419
|
+
});
|
|
1420
|
+
}
|
|
1421
|
+
function causeLine(error) {
|
|
1422
|
+
const { cause } = error;
|
|
1423
|
+
if (!Error.isError(cause)) return "";
|
|
1424
|
+
const [first] = cause.message.split("\n");
|
|
1425
|
+
return `\ncaused by ${cause.name}: ${first ?? cause.message}`;
|
|
1426
|
+
}
|
|
1427
|
+
function reportRawFault(error, core, origin, uuid) {
|
|
1428
|
+
if (core.config.errors?.errorStack ?? false) logger$2.error(uuid, error);
|
|
1429
|
+
else logger$2.error(`${uuid} | ${error.message}${causeLine(error)}`);
|
|
1430
|
+
withThrottle(core, origin, error, () => {
|
|
1431
|
+
core.bus[_seedcord_core_internal.PublishDefault]("unknownException", {
|
|
1432
|
+
uuid,
|
|
1433
|
+
error,
|
|
1434
|
+
...scalarActors(origin),
|
|
1435
|
+
metadata: metadataFor(origin)
|
|
1436
|
+
});
|
|
1437
|
+
});
|
|
1438
|
+
}
|
|
1439
|
+
function scalarActors(origin) {
|
|
1440
|
+
return {
|
|
1441
|
+
guild: origin.guild ? {
|
|
1442
|
+
id: origin.guild.id,
|
|
1443
|
+
name: origin.guild.name
|
|
1444
|
+
} : void 0,
|
|
1445
|
+
user: origin.user ? {
|
|
1446
|
+
id: origin.user.id,
|
|
1447
|
+
username: origin.user.username
|
|
1448
|
+
} : void 0
|
|
1449
|
+
};
|
|
1450
|
+
}
|
|
1451
|
+
function metadataFor(origin) {
|
|
1452
|
+
if (origin.event) return {
|
|
1453
|
+
eventName: origin.event.name,
|
|
1454
|
+
handler: origin.event.handler,
|
|
1455
|
+
args: origin.event.args
|
|
1456
|
+
};
|
|
1457
|
+
return origin.metadata;
|
|
1458
|
+
}
|
|
1459
|
+
function buildEventSource(event, origin) {
|
|
1460
|
+
return {
|
|
1461
|
+
kind: "event",
|
|
1462
|
+
eventName: event.name,
|
|
1463
|
+
handler: event.handler,
|
|
1464
|
+
userId: origin.user?.id ?? null,
|
|
1465
|
+
guildId: origin.guild?.id ?? null,
|
|
1466
|
+
channelId: event.channelId,
|
|
1467
|
+
raw: event.args
|
|
1468
|
+
};
|
|
1469
|
+
}
|
|
1470
|
+
function faultKey(origin, error) {
|
|
1471
|
+
const name = error instanceof _seedcord_core.Notice ? error.name : error instanceof discord_js.DiscordAPIError ? String(error.code) : error.constructor.name;
|
|
1472
|
+
if (origin.event) return `${origin.event.name}:${origin.event.handler}:${name}`;
|
|
1473
|
+
if (origin.interaction) return `${interactionRoute(origin.interaction)}:${name}`;
|
|
1474
|
+
if (origin.route) return `autocomplete:${origin.route}:${name}`;
|
|
1475
|
+
return `autocomplete:${name}`;
|
|
1476
|
+
}
|
|
1477
|
+
function interactionRoute(interaction) {
|
|
1478
|
+
if (interaction.isChatInputCommand()) return slashRouteOf(interaction);
|
|
1479
|
+
if (interaction.isContextMenuCommand()) return interaction.commandName;
|
|
1480
|
+
if (interaction.isButton() || interaction.isAnySelectMenu() || interaction.isModalSubmit()) return (0, _seedcord_core_internal.prefixOf)(interaction.customId) || interaction.customId;
|
|
1481
|
+
return "interaction";
|
|
1482
|
+
}
|
|
1483
|
+
function buildInteractionSource(interaction) {
|
|
1484
|
+
const command = interaction.isChatInputCommand() ? slashRouteOf(interaction) : interaction.isContextMenuCommand() ? interaction.commandName : null;
|
|
1485
|
+
const customId = interaction.isButton() || interaction.isAnySelectMenu() || interaction.isModalSubmit() ? interaction.customId : null;
|
|
1486
|
+
return {
|
|
1487
|
+
kind: "interaction",
|
|
1488
|
+
interactionKind: interactionKind(interaction),
|
|
1489
|
+
command,
|
|
1490
|
+
customId,
|
|
1491
|
+
userId: interaction.user.id,
|
|
1492
|
+
guildId: interaction.guildId,
|
|
1493
|
+
channelId: interaction.channelId,
|
|
1494
|
+
interactionId: interaction.id,
|
|
1495
|
+
raw: interaction
|
|
1496
|
+
};
|
|
1497
|
+
}
|
|
1498
|
+
function interactionKind(interaction) {
|
|
1499
|
+
if (interaction.isChatInputCommand()) return "slash";
|
|
1500
|
+
if (interaction.isContextMenuCommand()) return "context-menu";
|
|
1501
|
+
if (interaction.isButton()) return "button";
|
|
1502
|
+
if (interaction.isAnySelectMenu()) return "select";
|
|
1503
|
+
return "modal";
|
|
1504
|
+
}
|
|
1505
|
+
|
|
2196
1506
|
//#endregion
|
|
2197
1507
|
//#region src/bot/handleEventFault.ts
|
|
2198
1508
|
const logger$1 = new _seedcord_logger.Logger("EventBoundary", { channel: "errors" });
|
|
@@ -2626,21 +1936,9 @@ var InteractionDispatcher = class {
|
|
|
2626
1936
|
logger: this.logger
|
|
2627
1937
|
});
|
|
2628
1938
|
}
|
|
2629
|
-
/**
|
|
2630
|
-
* Warns for each command route with no registered `@SlashRoute` handler, which would fall through to
|
|
2631
|
-
* UnhandledRepliable at runtime. A bot may route commands outside the registry, so this does not throw.
|
|
2632
|
-
*
|
|
2633
|
-
* @internal
|
|
2634
|
-
*/
|
|
2635
1939
|
warnUnhandledRoutes(commandLeaves) {
|
|
2636
1940
|
for (const route of commandLeaves) if (!this.slashMap.has(route)) this.logger.warn(`Slash route ${_seedcord_logger.paint.sky.bold(route)} has no registered ${chalk.default.bold("@SlashRoute")} handler.`);
|
|
2637
1941
|
}
|
|
2638
|
-
/**
|
|
2639
|
-
* Warns for each context-menu command with no matching handler, which would fall through to UnhandledRepliable
|
|
2640
|
-
* at runtime. Checked per kind since a user and a message command can share a name.
|
|
2641
|
-
*
|
|
2642
|
-
* @internal
|
|
2643
|
-
*/
|
|
2644
1942
|
warnUnhandledContextMenuRoutes(leaves) {
|
|
2645
1943
|
for (const name of leaves.user) if (!this.userContextMenuMap.has(name)) this.logger.warn(`User context menu ${_seedcord_logger.paint.sky.bold(name)} has no registered ${chalk.default.bold("@ContextMenuRoute")} handler.`);
|
|
2646
1944
|
for (const name of leaves.message) if (!this.messageContextMenuMap.has(name)) this.logger.warn(`Message context menu ${_seedcord_logger.paint.sky.bold(name)} has no registered ${chalk.default.bold("@ContextMenuRoute")} handler.`);
|
|
@@ -2981,29 +2279,39 @@ var Bot = class {
|
|
|
2981
2279
|
interactions;
|
|
2982
2280
|
events;
|
|
2983
2281
|
emojiInjector;
|
|
2984
|
-
|
|
2985
|
-
emojis = Emojis;
|
|
2986
|
-
mentions = CommandMentions;
|
|
2282
|
+
commandRegistry;
|
|
2987
2283
|
/** @internal */
|
|
2988
2284
|
async onHmr(event) {
|
|
2989
2285
|
if (this.interactions) await this.interactions.onHmr(event);
|
|
2990
2286
|
if (this.events) await this.events.onHmr(event);
|
|
2991
|
-
if (this.
|
|
2287
|
+
if (this.commandRegistry) await this.commandRegistry.onHmr(event);
|
|
2992
2288
|
}
|
|
2993
2289
|
/** @internal */
|
|
2994
2290
|
constructor(core) {
|
|
2995
2291
|
this._client = new discord_js.Client(core.config.bot.clientOptions);
|
|
2996
2292
|
if (core.config.bot.interactions.path) this.interactions = new InteractionDispatcher(core);
|
|
2997
2293
|
if (core.config.bot.events.path) this.events = new EventDispatcher(core);
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
2294
|
+
const commandsDir = core.config.bot.commands.path;
|
|
2295
|
+
if (commandsDir) {
|
|
2296
|
+
const injector = new _seedcord_core_internal.CommandInjector();
|
|
2297
|
+
const registry = new _seedcord_core_node_internal.CommandRegistry({
|
|
2298
|
+
dir: commandsDir,
|
|
2299
|
+
rest: this._client.rest,
|
|
2300
|
+
applicationId: () => this.applicationId(),
|
|
2301
|
+
onDeployed: (result) => {
|
|
2302
|
+
injector.inject(result, registry.allCommands());
|
|
2303
|
+
}
|
|
3002
2304
|
});
|
|
2305
|
+
this.commandRegistry = registry;
|
|
3003
2306
|
}
|
|
3004
2307
|
this.emojiInjector = new EmojiInjector(core);
|
|
3005
2308
|
this.registerShutdownTasks(core);
|
|
3006
2309
|
}
|
|
2310
|
+
applicationId() {
|
|
2311
|
+
const { application } = this._client;
|
|
2312
|
+
if (!application) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.CoreApplicationUnavailable);
|
|
2313
|
+
return application.id;
|
|
2314
|
+
}
|
|
3007
2315
|
registerShutdownTasks(core) {
|
|
3008
2316
|
core.shutdown.addTask(_seedcord_core_node_internal.ShutdownPhase.Unbind, "stop-dispatch", () => {
|
|
3009
2317
|
this.stopAccepting();
|
|
@@ -3028,11 +2336,11 @@ var Bot = class {
|
|
|
3028
2336
|
if (this.events) await this.events.init();
|
|
3029
2337
|
await this.login(token);
|
|
3030
2338
|
await this.emojiInjector.init();
|
|
3031
|
-
if (this.
|
|
3032
|
-
await this.
|
|
3033
|
-
await this.
|
|
3034
|
-
this.interactions?.warnUnhandledRoutes(this.
|
|
3035
|
-
this.interactions?.warnUnhandledContextMenuRoutes(this.
|
|
2339
|
+
if (this.commandRegistry) {
|
|
2340
|
+
await this.commandRegistry.init();
|
|
2341
|
+
await this.commandRegistry.setCommands();
|
|
2342
|
+
this.interactions?.warnUnhandledRoutes(this.commandRegistry.routeLeaves());
|
|
2343
|
+
this.interactions?.warnUnhandledContextMenuRoutes(this.commandRegistry.contextMenuLeaves());
|
|
3036
2344
|
}
|
|
3037
2345
|
}
|
|
3038
2346
|
/** @internal */
|
|
@@ -3061,7 +2369,7 @@ __decorate([(0, envapt_legacy.Envapt)("DISCORD_BOT_TOKEN", { converter: (raw) =>
|
|
|
3061
2369
|
//#endregion
|
|
3062
2370
|
//#region src/version.ts
|
|
3063
2371
|
/** Package version */
|
|
3064
|
-
const version = "0.1.0-next.
|
|
2372
|
+
const version = "0.1.0-next.6";
|
|
3065
2373
|
|
|
3066
2374
|
//#endregion
|
|
3067
2375
|
//#region src/Seedcord.ts
|
|
@@ -3084,6 +2392,8 @@ var Seedcord = class Seedcord extends _seedcord_core_node_internal.Pluggable {
|
|
|
3084
2392
|
subscribers;
|
|
3085
2393
|
/** @see {@link Bot} */
|
|
3086
2394
|
bot;
|
|
2395
|
+
/** The discord.js client's REST client. Its token is set during the Login phase. */
|
|
2396
|
+
rest;
|
|
3087
2397
|
/** @see {@link IRateLimiter} */
|
|
3088
2398
|
rateLimiter;
|
|
3089
2399
|
/**
|
|
@@ -3100,6 +2410,7 @@ var Seedcord = class Seedcord extends _seedcord_core_node_internal.Pluggable {
|
|
|
3100
2410
|
this.bus = new _seedcord_core.Bus(this);
|
|
3101
2411
|
this.subscribers = new _seedcord_core_node_internal.SubscriberLoader(this.bus, config.subscribers.path);
|
|
3102
2412
|
this.bot = new Bot(this);
|
|
2413
|
+
this.rest = this.bot.client.rest;
|
|
3103
2414
|
this.rateLimiter = config.store ?? new _seedcord_rate_limiter.MemoryRateLimiter();
|
|
3104
2415
|
this.healthCheck = _seedcord_core_node_internal.HealthCheck.fromOption(this.shutdown, config.healthCheck);
|
|
3105
2416
|
this.registerStartupTasks();
|
|
@@ -3108,13 +2419,6 @@ var Seedcord = class Seedcord extends _seedcord_core_node_internal.Pluggable {
|
|
|
3108
2419
|
get username() {
|
|
3109
2420
|
return this.bot.client.user?.username;
|
|
3110
2421
|
}
|
|
3111
|
-
pluginCapabilities() {
|
|
3112
|
-
return {
|
|
3113
|
-
client: this.bot.client,
|
|
3114
|
-
token: this.bot.botToken,
|
|
3115
|
-
rest: this.bot.client.rest
|
|
3116
|
-
};
|
|
3117
|
-
}
|
|
3118
2422
|
static reset() {
|
|
3119
2423
|
super.reset();
|
|
3120
2424
|
_seedcord_logger.LoggerChannelRegistry.instance.reset();
|
|
@@ -3167,7 +2471,6 @@ var Seedcord = class Seedcord extends _seedcord_core_node_internal.Pluggable {
|
|
|
3167
2471
|
exports.ArraySource = ArraySource;
|
|
3168
2472
|
exports.AutocompleteHandler = AutocompleteHandler;
|
|
3169
2473
|
exports.ButtonHandler = ButtonHandler;
|
|
3170
|
-
exports.CommandMentions = CommandMentions;
|
|
3171
2474
|
exports.ContextMenuHandler = ContextMenuHandler;
|
|
3172
2475
|
exports.CursorSource = CursorSource;
|
|
3173
2476
|
exports.Emojis = Emojis;
|
|
@@ -3192,16 +2495,8 @@ exports.Subscriber = Subscriber;
|
|
|
3192
2495
|
exports.WebhookLog = WebhookLog;
|
|
3193
2496
|
exports.checkBotPermissions = checkBotPermissions;
|
|
3194
2497
|
exports.checkPermissions = checkPermissions;
|
|
3195
|
-
exports.fetchGuildMember = fetchGuildMember;
|
|
3196
|
-
exports.fetchManyGuildMembers = fetchManyGuildMembers;
|
|
3197
|
-
exports.fetchManyUsers = fetchManyUsers;
|
|
3198
|
-
exports.fetchRole = fetchRole;
|
|
3199
|
-
exports.fetchText = fetchText;
|
|
3200
|
-
exports.fetchUser = fetchUser;
|
|
3201
|
-
exports.getBotRole = getBotRole;
|
|
3202
2498
|
exports.getConfirmation = getConfirmation;
|
|
3203
2499
|
exports.hasPermsToAssign = hasPermsToAssign;
|
|
3204
|
-
exports.updateMemberRoles = updateMemberRoles;
|
|
3205
2500
|
exports.version = version;
|
|
3206
2501
|
Object.keys(_seedcord_core).forEach(function (k) {
|
|
3207
2502
|
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|