seedcord 0.13.0-next.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +282 -410
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +118 -241
- package/dist/index.mjs +282 -399
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -7
package/dist/index.cjs
CHANGED
|
@@ -45,13 +45,36 @@ let envapt = require("envapt");
|
|
|
45
45
|
let node_crypto = require("node:crypto");
|
|
46
46
|
node_crypto = __toESM(node_crypto, 1);
|
|
47
47
|
|
|
48
|
+
//#region src/metadataKeys.ts
|
|
49
|
+
/** @internal */
|
|
50
|
+
const InteractionMetadataKey = Symbol("seedcord:interaction:metadata");
|
|
51
|
+
/** @internal */
|
|
52
|
+
const InteractionRouteKeys = {
|
|
53
|
+
["slash"]: Symbol("seedcord:interaction:slash"),
|
|
54
|
+
["button"]: Symbol("seedcord:interaction:button"),
|
|
55
|
+
["modal"]: Symbol("seedcord:interaction:modal"),
|
|
56
|
+
["stringMenu"]: Symbol("seedcord:interaction:stringMenu"),
|
|
57
|
+
["userMenu"]: Symbol("seedcord:interaction:userMenu"),
|
|
58
|
+
["roleMenu"]: Symbol("seedcord:interaction:roleMenu"),
|
|
59
|
+
["channelMenu"]: Symbol("seedcord:interaction:channelMenu"),
|
|
60
|
+
["mentionableMenu"]: Symbol("seedcord:interaction:mentionableMenu"),
|
|
61
|
+
["messageContextMenu"]: Symbol("seedcord:interaction:messageContextMenu"),
|
|
62
|
+
["userContextMenu"]: Symbol("seedcord:interaction:userContextMenu"),
|
|
63
|
+
["autocomplete"]: Symbol("seedcord:interaction:autocomplete")
|
|
64
|
+
};
|
|
65
|
+
/** @internal */
|
|
66
|
+
const MiddlewareMetadataKey = Symbol("seedcord:middleware:metadata");
|
|
67
|
+
/** @internal */
|
|
68
|
+
const EventMetadataKey = Symbol("seedcord:event:metadata");
|
|
69
|
+
/** @internal */
|
|
70
|
+
const CommandMetadataKey = Symbol("seedcord:command:metadata");
|
|
71
|
+
/** @internal */
|
|
72
|
+
const GatedMetadataKey = Symbol("seedcord:gated:metadata");
|
|
73
|
+
/** @internal */
|
|
74
|
+
const SubscribeMetadataKey = Symbol("seedcord:subscribe:metadata");
|
|
75
|
+
|
|
76
|
+
//#endregion
|
|
48
77
|
//#region src/bot/decorators/Command.ts
|
|
49
|
-
/**
|
|
50
|
-
* Metadata key for command registration information.
|
|
51
|
-
*
|
|
52
|
-
* @internal
|
|
53
|
-
*/
|
|
54
|
-
const CommandMetadataKey = Symbol("command:metadata");
|
|
55
78
|
function RegisterCommand(scope, guilds = []) {
|
|
56
79
|
return (ctor) => {
|
|
57
80
|
const meta = scope === "global" ? { scope } : {
|
|
@@ -73,12 +96,6 @@ function RegisterCommand(scope, guilds = []) {
|
|
|
73
96
|
//#endregion
|
|
74
97
|
//#region src/bot/decorators/Events.ts
|
|
75
98
|
/**
|
|
76
|
-
* Metadata key used to store event handler information
|
|
77
|
-
*
|
|
78
|
-
* @internal
|
|
79
|
-
*/
|
|
80
|
-
const EventMetadataKey = Symbol("event:metadata");
|
|
81
|
-
/**
|
|
82
99
|
* Registers an event handler class with one or more Discord.js events.
|
|
83
100
|
*
|
|
84
101
|
* Associates the decorated class with the provided Discord client events for automatic registration and execution when those events are emitted.
|
|
@@ -245,8 +262,6 @@ function eventGateContext(eventName, args, core) {
|
|
|
245
262
|
|
|
246
263
|
//#endregion
|
|
247
264
|
//#region src/bot/decorators/Gated.ts
|
|
248
|
-
/** @internal */
|
|
249
|
-
const GatedMetadataKey = Symbol("gated:metadata");
|
|
250
265
|
/**
|
|
251
266
|
* Attaches gates to a handler. The gates run before `execute`, and a gate refusing stops the handler with
|
|
252
267
|
* the reply or drop the gate threw. Multiple gates are ANDed, so each must pass. A gate that requires a
|
|
@@ -275,6 +290,8 @@ const GatedMetadataKey = Symbol("gated:metadata");
|
|
|
275
290
|
* @param ctor - The handler class being decorated, rejected at compile time when a gate needs a context it lacks.
|
|
276
291
|
*
|
|
277
292
|
* @see {@link defineGate}
|
|
293
|
+
* @see {@link defineEffectGate}
|
|
294
|
+
* @decorator
|
|
278
295
|
*/
|
|
279
296
|
function Gated(...gates) {
|
|
280
297
|
return function(ctor) {
|
|
@@ -311,16 +328,14 @@ function areRoutes(routes) {
|
|
|
311
328
|
/**
|
|
312
329
|
* Types of select menus supported for routing
|
|
313
330
|
*/
|
|
314
|
-
let
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
return
|
|
331
|
+
let SelectMenuKind = /* @__PURE__ */ function(SelectMenuKind) {
|
|
332
|
+
SelectMenuKind["String"] = "string";
|
|
333
|
+
SelectMenuKind["User"] = "user";
|
|
334
|
+
SelectMenuKind["Role"] = "role";
|
|
335
|
+
SelectMenuKind["Channel"] = "channel";
|
|
336
|
+
SelectMenuKind["Mentionable"] = "mentionable";
|
|
337
|
+
return SelectMenuKind;
|
|
321
338
|
}({});
|
|
322
|
-
/** @internal */
|
|
323
|
-
const InteractionMetadataKey = Symbol("interaction:metadata");
|
|
324
339
|
/**
|
|
325
340
|
* Routes one or more slash commands to a {@link SlashHandler}.
|
|
326
341
|
*
|
|
@@ -352,7 +367,7 @@ const InteractionMetadataKey = Symbol("interaction:metadata");
|
|
|
352
367
|
*/
|
|
353
368
|
function SlashRoute(...routes) {
|
|
354
369
|
return function(constructor) {
|
|
355
|
-
storeMetadata("
|
|
370
|
+
storeMetadata("slash", routes, constructor);
|
|
356
371
|
};
|
|
357
372
|
}
|
|
358
373
|
/**
|
|
@@ -368,7 +383,7 @@ function SlashRoute(...routes) {
|
|
|
368
383
|
*/
|
|
369
384
|
function ButtonRoute(...defs) {
|
|
370
385
|
return function(constructor) {
|
|
371
|
-
storeComponentRoute("
|
|
386
|
+
storeComponentRoute("button", defs, constructor);
|
|
372
387
|
};
|
|
373
388
|
}
|
|
374
389
|
/**
|
|
@@ -384,7 +399,7 @@ function ButtonRoute(...defs) {
|
|
|
384
399
|
*/
|
|
385
400
|
function ModalRoute(...defs) {
|
|
386
401
|
return function(constructor) {
|
|
387
|
-
storeComponentRoute("
|
|
402
|
+
storeComponentRoute("modal", defs, constructor);
|
|
388
403
|
};
|
|
389
404
|
}
|
|
390
405
|
/**
|
|
@@ -410,7 +425,7 @@ function ModalRoute(...defs) {
|
|
|
410
425
|
*/
|
|
411
426
|
function ContextMenuRoute(kind, ...names) {
|
|
412
427
|
return function(constructor) {
|
|
413
|
-
storeMetadata(kind === discord_js.ApplicationCommandType.User ? "
|
|
428
|
+
storeMetadata(kind === discord_js.ApplicationCommandType.User ? "userContextMenu" : "messageContextMenu", names, constructor);
|
|
414
429
|
};
|
|
415
430
|
}
|
|
416
431
|
/**
|
|
@@ -435,7 +450,7 @@ function ContextMenuRoute(kind, ...names) {
|
|
|
435
450
|
*/
|
|
436
451
|
function AutocompleteRoute(...routes) {
|
|
437
452
|
return function(constructor) {
|
|
438
|
-
storeMetadata("
|
|
453
|
+
storeMetadata("autocomplete", routes, constructor);
|
|
439
454
|
};
|
|
440
455
|
}
|
|
441
456
|
/**
|
|
@@ -445,14 +460,14 @@ function AutocompleteRoute(...routes) {
|
|
|
445
460
|
* generic must list the same definitions, and its second generic argument must be the matching select
|
|
446
461
|
* interaction type, or it is a compile error.
|
|
447
462
|
*
|
|
448
|
-
* @param type - Select menu kind from {@link
|
|
463
|
+
* @param type - Select menu kind from {@link SelectMenuKind}.
|
|
449
464
|
* @param defs - The customId definition(s) this handler decodes, one per route.
|
|
450
465
|
* @decorator
|
|
451
466
|
*
|
|
452
467
|
* @example
|
|
453
468
|
* ```typescript
|
|
454
|
-
* \@SelectMenuRoute(
|
|
455
|
-
* class AssignSelect extends SelectHandler<
|
|
469
|
+
* \@SelectMenuRoute(SelectMenuKind.User, AssignId)
|
|
470
|
+
* class AssignSelect extends SelectHandler<SelectMenuKind.User, [typeof AssignId]> {
|
|
456
471
|
* // handles user select menus minted from AssignId
|
|
457
472
|
* }
|
|
458
473
|
* ```
|
|
@@ -460,23 +475,24 @@ function AutocompleteRoute(...routes) {
|
|
|
460
475
|
function SelectMenuRoute(type, ...defs) {
|
|
461
476
|
return function(constructor) {
|
|
462
477
|
storeComponentRoute({
|
|
463
|
-
["string"]: "
|
|
464
|
-
["user"]: "
|
|
465
|
-
["role"]: "
|
|
466
|
-
["channel"]: "
|
|
467
|
-
["mentionable"]: "
|
|
478
|
+
["string"]: "stringMenu",
|
|
479
|
+
["user"]: "userMenu",
|
|
480
|
+
["role"]: "roleMenu",
|
|
481
|
+
["channel"]: "channelMenu",
|
|
482
|
+
["mentionable"]: "mentionableMenu"
|
|
468
483
|
}[type], defs, constructor);
|
|
469
484
|
};
|
|
470
485
|
}
|
|
471
|
-
function storeComponentRoute(
|
|
472
|
-
storeMetadata(
|
|
486
|
+
function storeComponentRoute(route, defs, constructor) {
|
|
487
|
+
storeMetadata(route, defs.map((def) => def.prefix), constructor);
|
|
473
488
|
Reflect.defineMetadata(_seedcord_kit_internal.ComponentDefsKey, defs, constructor);
|
|
474
489
|
}
|
|
475
|
-
function storeMetadata(
|
|
476
|
-
const
|
|
490
|
+
function storeMetadata(route, routes, constructor) {
|
|
491
|
+
const key = InteractionRouteKeys[route];
|
|
492
|
+
const savedRoutes = Reflect.getMetadata(key, constructor);
|
|
477
493
|
const existing = areRoutes(savedRoutes) ? savedRoutes : [];
|
|
478
494
|
const toStore = Array.isArray(routes) ? routes : [routes];
|
|
479
|
-
Reflect.defineMetadata(
|
|
495
|
+
Reflect.defineMetadata(key, [...existing, ...toStore], constructor);
|
|
480
496
|
Reflect.defineMetadata(InteractionMetadataKey, true, constructor);
|
|
481
497
|
}
|
|
482
498
|
|
|
@@ -486,17 +502,11 @@ function storeMetadata(symbol, routes, constructor) {
|
|
|
486
502
|
* Middleware types supported by Seedcord
|
|
487
503
|
*/
|
|
488
504
|
let MiddlewareType = /* @__PURE__ */ function(MiddlewareType) {
|
|
489
|
-
MiddlewareType["Interaction"] = "
|
|
490
|
-
MiddlewareType["Event"] = "
|
|
505
|
+
MiddlewareType["Interaction"] = "interaction";
|
|
506
|
+
MiddlewareType["Event"] = "event";
|
|
491
507
|
return MiddlewareType;
|
|
492
508
|
}({});
|
|
493
509
|
/**
|
|
494
|
-
* Metadata key for middleware configuration
|
|
495
|
-
*
|
|
496
|
-
* @internal
|
|
497
|
-
*/
|
|
498
|
-
const MiddlewareMetadataKey = Symbol("middleware:metadata");
|
|
499
|
-
/**
|
|
500
510
|
* Decorator used to register middleware with priority ordering. The lower the priority number, the earlier it runs.
|
|
501
511
|
*
|
|
502
512
|
* Interaction middleware cannot specify event filters.
|
|
@@ -512,14 +522,14 @@ const MiddlewareMetadataKey = Symbol("middleware:metadata");
|
|
|
512
522
|
* \@Middleware(MiddlewareType.Event, 10, { events: [Events.MessageCreate, Events.MessageUpdate] })
|
|
513
523
|
* class MyEventMiddleware extends EventMiddleware {}
|
|
514
524
|
* ```
|
|
515
|
-
* @throws A
|
|
516
|
-
* @throws A
|
|
525
|
+
* @throws A **SeedcordTypeError** If priority is not a finite number
|
|
526
|
+
* @throws A **SeedcordError** If interaction middleware specifies event filters
|
|
517
527
|
*/
|
|
518
528
|
function Middleware(type, priority = 0, options = {}) {
|
|
519
529
|
return (ctor) => {
|
|
520
530
|
const normalizedPriority = Number(priority);
|
|
521
531
|
if (!Number.isFinite(normalizedPriority)) throw new _seedcord_errors_internal.SeedcordTypeError(_seedcord_errors.SeedcordErrorCode.DecoratorInvalidMiddlewarePriority);
|
|
522
|
-
if (type === "
|
|
532
|
+
if (type === "interaction" && Array.isArray(options.events) && options.events.length > 0) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.DecoratorInteractionEventFilter);
|
|
523
533
|
const metadata = {
|
|
524
534
|
priority: normalizedPriority,
|
|
525
535
|
type,
|
|
@@ -632,7 +642,61 @@ function defineEffectGate(name, check, commit) {
|
|
|
632
642
|
}
|
|
633
643
|
|
|
634
644
|
//#endregion
|
|
635
|
-
//#region src/bot/
|
|
645
|
+
//#region src/bot/notices/utils.ts
|
|
646
|
+
function mentionFor(subject) {
|
|
647
|
+
if (subject instanceof discord_js.Role) return `<@&${subject.id}>`;
|
|
648
|
+
if (subject instanceof discord_js.TextChannel) return `<#${subject.id}>`;
|
|
649
|
+
if (subject instanceof discord_js.GuildMember) return `<@${subject.id}>`;
|
|
650
|
+
return `\`${subject.name}\``;
|
|
651
|
+
}
|
|
652
|
+
function labelFor(subject) {
|
|
653
|
+
if (subject instanceof discord_js.Role) return "role";
|
|
654
|
+
if (subject instanceof discord_js.TextChannel) return "channel";
|
|
655
|
+
if (subject instanceof discord_js.GuildMember) return "member";
|
|
656
|
+
return "guild";
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
//#endregion
|
|
660
|
+
//#region src/bot/notices/index.ts
|
|
661
|
+
var GateNotice = class extends _seedcord_kit.Notice {
|
|
662
|
+
render() {
|
|
663
|
+
return { components: [new _seedcord_kit_internal.NoticeCard(this.message).component] };
|
|
664
|
+
}
|
|
665
|
+
};
|
|
666
|
+
var NotOwner = class extends GateNotice {
|
|
667
|
+
constructor(message = "Only the bot owner can use this.") {
|
|
668
|
+
super(message);
|
|
669
|
+
}
|
|
670
|
+
};
|
|
671
|
+
var NotInGuild = class extends GateNotice {
|
|
672
|
+
constructor(message = "This can only be used in a server.") {
|
|
673
|
+
super(message);
|
|
674
|
+
}
|
|
675
|
+
};
|
|
676
|
+
var NotInDm = class extends GateNotice {
|
|
677
|
+
constructor(message = "This can only be used in a direct message.") {
|
|
678
|
+
super(message);
|
|
679
|
+
}
|
|
680
|
+
};
|
|
681
|
+
var NotNsfw = class extends GateNotice {
|
|
682
|
+
constructor(message = "This can only be used in an age-restricted channel.") {
|
|
683
|
+
super(message);
|
|
684
|
+
}
|
|
685
|
+
};
|
|
686
|
+
var OnCooldown = class extends GateNotice {
|
|
687
|
+
expires;
|
|
688
|
+
constructor(expires, message) {
|
|
689
|
+
super(message ?? `You are doing that too fast. Try again <t:${(0, _seedcord_utils.toEpochSeconds)(expires)}:R>.`);
|
|
690
|
+
this.expires = expires;
|
|
691
|
+
}
|
|
692
|
+
};
|
|
693
|
+
var MissingRole = class extends GateNotice {
|
|
694
|
+
role;
|
|
695
|
+
constructor(message, role) {
|
|
696
|
+
super(message ?? (role ? `You need the ${role.name} role to use this.` : "You do not have the required role."));
|
|
697
|
+
this.role = role;
|
|
698
|
+
}
|
|
699
|
+
};
|
|
636
700
|
var NotAllowed = class extends _seedcord_kit.Notice {
|
|
637
701
|
constructor() {
|
|
638
702
|
super("not allowed");
|
|
@@ -651,6 +715,93 @@ var NeedsAny = class extends _seedcord_kit.Notice {
|
|
|
651
715
|
return { components: [new _seedcord_kit_internal.NoticeCard(`You need any of:\n${this.summaries.map((summary) => `• ${summary}`).join("\n")}`).component] };
|
|
652
716
|
}
|
|
653
717
|
};
|
|
718
|
+
var RoleHigherThanMe = class extends _seedcord_kit.Notice {
|
|
719
|
+
role;
|
|
720
|
+
botRole;
|
|
721
|
+
constructor(message, role, botRole) {
|
|
722
|
+
super(message);
|
|
723
|
+
this.role = role;
|
|
724
|
+
this.botRole = botRole;
|
|
725
|
+
}
|
|
726
|
+
render() {
|
|
727
|
+
return { components: [new _seedcord_kit_internal.NoticeCard(`I cannot assign a role that is higher than me.\n\nThe role <@&${this.role.id}> is higher than my role <@&${this.botRole.id}> in the hierarchy.`).component] };
|
|
728
|
+
}
|
|
729
|
+
};
|
|
730
|
+
var CannotAssignBotRole = class extends _seedcord_kit.Notice {
|
|
731
|
+
constructor(message = "I cannot assign a managed role.") {
|
|
732
|
+
super(message);
|
|
733
|
+
}
|
|
734
|
+
render() {
|
|
735
|
+
return { components: [new _seedcord_kit_internal.NoticeCard("I cannot assign a managed role.").component] };
|
|
736
|
+
}
|
|
737
|
+
};
|
|
738
|
+
var MissingPermissions = class extends _seedcord_kit.Notice {
|
|
739
|
+
where;
|
|
740
|
+
missingPerms;
|
|
741
|
+
constructor(message, where, missingPerms) {
|
|
742
|
+
super(message);
|
|
743
|
+
this.where = where;
|
|
744
|
+
this.missingPerms = missingPerms;
|
|
745
|
+
}
|
|
746
|
+
render() {
|
|
747
|
+
const bullets = this.missingPerms.map((perm) => `• ${perm}`).join("\n");
|
|
748
|
+
return { components: [new _seedcord_kit_internal.NoticeCard(`The ${labelFor(this.where)} ${mentionFor(this.where)} is missing the following permission entries:\n\n${bullets}`).component] };
|
|
749
|
+
}
|
|
750
|
+
};
|
|
751
|
+
var HasDangerousPermissions = class extends _seedcord_kit.Notice {
|
|
752
|
+
target;
|
|
753
|
+
dangerousPerms;
|
|
754
|
+
constructor(message, target, dangerousPerms) {
|
|
755
|
+
super(message);
|
|
756
|
+
this.target = target;
|
|
757
|
+
this.dangerousPerms = dangerousPerms;
|
|
758
|
+
}
|
|
759
|
+
render() {
|
|
760
|
+
const bullets = this.dangerousPerms.map((perm) => `• ${perm}`).join("\n");
|
|
761
|
+
return { components: [new _seedcord_kit_internal.NoticeCard(`The ${labelFor(this.target)} ${mentionFor(this.target)} has the following permission entries that must not be enabled:\n\n${bullets}`).component] };
|
|
762
|
+
}
|
|
763
|
+
};
|
|
764
|
+
var UserNotFound = class extends _seedcord_kit.Notice {
|
|
765
|
+
userArg;
|
|
766
|
+
constructor(userArg) {
|
|
767
|
+
super(`User not found: ${userArg}`);
|
|
768
|
+
this.userArg = userArg;
|
|
769
|
+
}
|
|
770
|
+
render() {
|
|
771
|
+
return { components: [new _seedcord_kit_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] };
|
|
772
|
+
}
|
|
773
|
+
};
|
|
774
|
+
var UserNotInGuild = class extends _seedcord_kit.Notice {
|
|
775
|
+
constructor(message = "User is not in the guild.") {
|
|
776
|
+
super(message);
|
|
777
|
+
}
|
|
778
|
+
render() {
|
|
779
|
+
return { components: [new _seedcord_kit_internal.NoticeCard(this.message).component] };
|
|
780
|
+
}
|
|
781
|
+
};
|
|
782
|
+
var RoleDoesNotExist = class extends _seedcord_kit.Notice {
|
|
783
|
+
roleId;
|
|
784
|
+
constructor(message, roleId) {
|
|
785
|
+
super(message);
|
|
786
|
+
this.roleId = roleId;
|
|
787
|
+
}
|
|
788
|
+
render() {
|
|
789
|
+
return { components: [new _seedcord_kit_internal.NoticeCard(`The role with ID \`${this.roleId}\` does not exist.`).component] };
|
|
790
|
+
}
|
|
791
|
+
};
|
|
792
|
+
var CouldNotFindChannel = class extends _seedcord_kit.Notice {
|
|
793
|
+
channelId;
|
|
794
|
+
constructor(message, channelId) {
|
|
795
|
+
super(message);
|
|
796
|
+
this.channelId = channelId;
|
|
797
|
+
}
|
|
798
|
+
render() {
|
|
799
|
+
return { components: [new _seedcord_kit_internal.NoticeCard(`Could not find channel with ID \`${this.channelId}\`. It could also be that the channel is not a text channel.`).component] };
|
|
800
|
+
}
|
|
801
|
+
};
|
|
802
|
+
|
|
803
|
+
//#endregion
|
|
804
|
+
//#region src/bot/gates/combinators.ts
|
|
654
805
|
function and(...gates) {
|
|
655
806
|
return defineGate("and", async (ctx) => {
|
|
656
807
|
for (const gate of gates) await runCheck(gate, ctx);
|
|
@@ -687,43 +838,8 @@ function or(...args) {
|
|
|
687
838
|
});
|
|
688
839
|
}
|
|
689
840
|
|
|
690
|
-
//#endregion
|
|
691
|
-
//#region src/bot/gates/catalog/GateNotice.ts
|
|
692
|
-
/**
|
|
693
|
-
* Base for the catalog refusals. Renders the message as the bot's standard notice card, so matching this
|
|
694
|
-
* one type in the boundary restyles or translates every built-in gate refusal in one place. A refusal reply,
|
|
695
|
-
* distinct from a {@link Silence} quiet drop.
|
|
696
|
-
*
|
|
697
|
-
* @example
|
|
698
|
-
* ```ts
|
|
699
|
-
* import { GateNotice } from 'seedcord';
|
|
700
|
-
*
|
|
701
|
-
* class NotPremium extends GateNotice {
|
|
702
|
-
* public constructor() {
|
|
703
|
-
* super('This command is for premium members only.');
|
|
704
|
-
* }
|
|
705
|
-
* }
|
|
706
|
-
*
|
|
707
|
-
* // thrown from a custom gate's check, rendered as the standard notice card
|
|
708
|
-
* defineGate('Premium', (ctx) => {
|
|
709
|
-
* if (!isPremium(ctx.user)) throw new NotPremium();
|
|
710
|
-
* });
|
|
711
|
-
* ```
|
|
712
|
-
*/
|
|
713
|
-
var GateNotice = class extends _seedcord_kit.Notice {
|
|
714
|
-
render() {
|
|
715
|
-
return { components: [new _seedcord_kit_internal.NoticeCard(this.message).component] };
|
|
716
|
-
}
|
|
717
|
-
};
|
|
718
|
-
|
|
719
841
|
//#endregion
|
|
720
842
|
//#region src/bot/gates/catalog/options.ts
|
|
721
|
-
/**
|
|
722
|
-
* Picks the refusal a catalog gate throws, the author override when given, else the gate's default.
|
|
723
|
-
*
|
|
724
|
-
* @param options - The override a catalog gate received, or undefined when the author passed none.
|
|
725
|
-
* @param makeDefault - Builds the gate's default refusal, given the optional reworded message.
|
|
726
|
-
*/
|
|
727
843
|
function pickNotice(options, makeDefault) {
|
|
728
844
|
return options?.notice ?? makeDefault(options?.message);
|
|
729
845
|
}
|
|
@@ -731,41 +847,11 @@ function pickNotice(options, makeDefault) {
|
|
|
731
847
|
//#endregion
|
|
732
848
|
//#region src/bot/gates/catalog/access.ts
|
|
733
849
|
/**
|
|
734
|
-
*
|
|
735
|
-
*
|
|
736
|
-
* @param message - Text shown in the refusal, defaulting to a bot-owner-only line.
|
|
737
|
-
*/
|
|
738
|
-
var NotOwner = class extends GateNotice {
|
|
739
|
-
constructor(message = "Only the bot owner can use this.") {
|
|
740
|
-
super(message);
|
|
741
|
-
}
|
|
742
|
-
};
|
|
743
|
-
/**
|
|
744
|
-
* Refusal shown when a guild-only command runs in a DM.
|
|
745
|
-
*
|
|
746
|
-
* @param message - Text shown in the refusal, defaulting to a server-only line.
|
|
747
|
-
*/
|
|
748
|
-
var NotInGuild = class extends GateNotice {
|
|
749
|
-
constructor(message = "This can only be used in a server.") {
|
|
750
|
-
super(message);
|
|
751
|
-
}
|
|
752
|
-
};
|
|
753
|
-
/**
|
|
754
|
-
* Refusal shown when a DM-only command runs in a guild.
|
|
755
|
-
*
|
|
756
|
-
* @param message - Text shown in the refusal, defaulting to a direct-message-only line.
|
|
757
|
-
*/
|
|
758
|
-
var NotInDm = class extends GateNotice {
|
|
759
|
-
constructor(message = "This can only be used in a direct message.") {
|
|
760
|
-
super(message);
|
|
761
|
-
}
|
|
762
|
-
};
|
|
763
|
-
/**
|
|
764
|
-
* Passes only for a user id listed in `config.ownerIds`, else refuses with an ephemeral {@link NotOwner}.
|
|
850
|
+
* Passes only for a user id listed in `config.ownerIds`, else refuses.
|
|
765
851
|
*
|
|
766
852
|
* Agnostic, so it attaches to interaction and event handlers alike. With no `ownerIds` configured it refuses every caller. Pass {@link GateNoticeOptions} to reword or replace the refusal.
|
|
767
853
|
*
|
|
768
|
-
* @param options - Reword
|
|
854
|
+
* @param options - Reword the default refusal with `message`, or replace it with `notice`.
|
|
769
855
|
*
|
|
770
856
|
* @see {@link Gated}
|
|
771
857
|
*
|
|
@@ -794,11 +880,11 @@ function OwnerOnly(options) {
|
|
|
794
880
|
});
|
|
795
881
|
}
|
|
796
882
|
/**
|
|
797
|
-
* Passes inside a guild, else refuses
|
|
883
|
+
* Passes inside a guild, else refuses.
|
|
798
884
|
*
|
|
799
885
|
* Agnostic, so it attaches to any handler kind. Often paired with {@link RequirePermissions} in an {@link and}.
|
|
800
886
|
*
|
|
801
|
-
* @param options - Reword
|
|
887
|
+
* @param options - Reword the default refusal with `message`, or replace it with `notice`.
|
|
802
888
|
*
|
|
803
889
|
* @see {@link Gated}
|
|
804
890
|
*
|
|
@@ -822,11 +908,11 @@ function GuildOnly(options) {
|
|
|
822
908
|
});
|
|
823
909
|
}
|
|
824
910
|
/**
|
|
825
|
-
* Passes in a direct message, else refuses
|
|
911
|
+
* Passes in a direct message, else refuses.
|
|
826
912
|
*
|
|
827
913
|
* Agnostic, so it attaches to any handler kind. The inverse of {@link GuildOnly}, so combining the two in an {@link and} can never pass.
|
|
828
914
|
*
|
|
829
|
-
* @param options - Reword
|
|
915
|
+
* @param options - Reword the default refusal with `message`, or replace it with `notice`.
|
|
830
916
|
*
|
|
831
917
|
* @see {@link Gated}
|
|
832
918
|
*
|
|
@@ -850,28 +936,6 @@ function DmOnly(options) {
|
|
|
850
936
|
|
|
851
937
|
//#endregion
|
|
852
938
|
//#region src/bot/gates/catalog/Cooldown.ts
|
|
853
|
-
/**
|
|
854
|
-
* Refusal shown while a Cooldown is still cooling down. Carries the epoch ms the key frees up, which the default
|
|
855
|
-
* message renders as a relative timestamp.
|
|
856
|
-
*
|
|
857
|
-
* @param expires - Epoch ms at which the key frees up, rendered as a relative timestamp by the default message.
|
|
858
|
-
* @param message - Optional text that replaces the default refusal.
|
|
859
|
-
*
|
|
860
|
-
* @example
|
|
861
|
-
* ```ts
|
|
862
|
-
* // refuse from a custom check, freeing up one minute from now
|
|
863
|
-
* defineGate('SlowDown', (ctx) => {
|
|
864
|
-
* if (tooFast(ctx.user)) throw new OnCooldown((Date.now() + 60_000) as EpochMs);
|
|
865
|
-
* });
|
|
866
|
-
* ```
|
|
867
|
-
*/
|
|
868
|
-
var OnCooldown = class extends GateNotice {
|
|
869
|
-
expires;
|
|
870
|
-
constructor(expires, message) {
|
|
871
|
-
super(message ?? `You are doing that too fast. Try again <t:${(0, _seedcord_utils.toEpochSeconds)(expires)}:R>.`);
|
|
872
|
-
this.expires = expires;
|
|
873
|
-
}
|
|
874
|
-
};
|
|
875
939
|
let bucketSeq = 0;
|
|
876
940
|
function scopeValue(ctx, per) {
|
|
877
941
|
if (per === "guild") return ctx.guildId ?? "global";
|
|
@@ -880,16 +944,16 @@ function scopeValue(ctx, per) {
|
|
|
880
944
|
}
|
|
881
945
|
/**
|
|
882
946
|
* Allows `limit` uses per window, scoped by `per`. A number `duration` is **seconds**, a string is
|
|
883
|
-
* a duration like `30m` or `24h`. An unparseable string throws a
|
|
947
|
+
* a duration like `30m` or `24h`. An unparseable string throws a **SeedcordTypeError** at construction. The
|
|
884
948
|
* slot is charged in commit, only after the whole gate set passes, so a later refusal never burns the cooldown.
|
|
885
|
-
* Each call gets its own bucket, so two handlers never share a window.
|
|
949
|
+
* Each call gets its own bucket, so two handlers never share a window. Reword the refusal with {@link CooldownOptions.message}
|
|
950
|
+
* or replace it with {@link CooldownOptions.notice}.
|
|
886
951
|
*
|
|
887
|
-
* @param duration - A number is seconds, a string is a duration like `30m` or `24h`. An unparseable string throws a
|
|
952
|
+
* @param duration - A number is seconds, a string is a duration like `30m` or `24h`. An unparseable string throws a **SeedcordTypeError**.
|
|
888
953
|
* @param options - Sets the scope with `per`, the uses per window with `limit`, and the refusal text with `message` or `notice`.
|
|
889
954
|
*
|
|
890
955
|
* @see {@link Gated}
|
|
891
956
|
* @see {@link RateLimiter}
|
|
892
|
-
* @see {@link OnCooldown}
|
|
893
957
|
*
|
|
894
958
|
* @example
|
|
895
959
|
* ```ts
|
|
@@ -951,7 +1015,7 @@ function Cooldown(duration, options) {
|
|
|
951
1015
|
/**
|
|
952
1016
|
* Drops a client event whose actor is a bot, with a {@link Silence} so nothing is replied. Event-only, because a
|
|
953
1017
|
* Silence on an interaction would leave Discord's failed-interaction state. It rejects an interaction handler at
|
|
954
|
-
* the decorator line.
|
|
1018
|
+
* the decorator line. Takes no options, so attach it directly without calling it.
|
|
955
1019
|
*
|
|
956
1020
|
* @see {@link Gated}
|
|
957
1021
|
*
|
|
@@ -959,7 +1023,7 @@ function Cooldown(duration, options) {
|
|
|
959
1023
|
* ```ts
|
|
960
1024
|
* import { Events } from 'discord.js';
|
|
961
1025
|
*
|
|
962
|
-
* \@Gated(IgnoreBots
|
|
1026
|
+
* \@Gated(IgnoreBots)
|
|
963
1027
|
* class OnMessage extends EventHandler<Events.MessageCreate> {
|
|
964
1028
|
* async execute() {
|
|
965
1029
|
* // ...
|
|
@@ -967,41 +1031,25 @@ function Cooldown(duration, options) {
|
|
|
967
1031
|
* }
|
|
968
1032
|
* ```
|
|
969
1033
|
*/
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
});
|
|
974
|
-
}
|
|
1034
|
+
const IgnoreBots = defineGate("IgnoreBots", (ctx) => {
|
|
1035
|
+
if (ctx.user?.bot) throw new _seedcord_kit.Silence("actor is a bot");
|
|
1036
|
+
});
|
|
975
1037
|
|
|
976
1038
|
//#endregion
|
|
977
1039
|
//#region src/bot/gates/catalog/Nsfw.ts
|
|
978
|
-
/**
|
|
979
|
-
* Refusal shown when a command marked NSFW runs in a channel that is not age-restricted.
|
|
980
|
-
*
|
|
981
|
-
* @example
|
|
982
|
-
* ```ts
|
|
983
|
-
* // refuse from a custom check with a reworded message
|
|
984
|
-
* defineGate('AdultOnly', (ctx: InteractionGateContext<NonModalInteraction>) => {
|
|
985
|
-
* if (!isAgeRestricted(ctx.interaction.channel)) throw new NotNsfw('Adults only here.');
|
|
986
|
-
* });
|
|
987
|
-
* ```
|
|
988
|
-
*
|
|
989
|
-
* @param message - The text rendered in the refusal embed, defaulting to the age-restricted notice.
|
|
990
|
-
*/
|
|
991
|
-
var NotNsfw = class extends GateNotice {
|
|
992
|
-
constructor(message = "This can only be used in an age-restricted channel.") {
|
|
993
|
-
super(message);
|
|
994
|
-
}
|
|
995
|
-
};
|
|
996
1040
|
function channelIsNsfw(channel) {
|
|
997
1041
|
if (!channel) return false;
|
|
998
1042
|
if (channel.isThread()) return channel.parent?.nsfw ?? false;
|
|
999
1043
|
return "nsfw" in channel ? channel.nsfw : false;
|
|
1000
1044
|
}
|
|
1001
1045
|
/**
|
|
1002
|
-
* Requires an age-restricted channel, else refuses
|
|
1003
|
-
*
|
|
1004
|
-
*
|
|
1046
|
+
* Requires an age-restricted channel, else refuses. Pass {@link GateNoticeOptions} to reword or replace the refusal.
|
|
1047
|
+
*
|
|
1048
|
+
* A thread inherits its parent channel's nsfw flag. ModalSubmit has no reliable channel, so it is excluded.
|
|
1049
|
+
*
|
|
1050
|
+
* @param options - Reword the default refusal with `message`, or replace it with `notice`.
|
|
1051
|
+
*
|
|
1052
|
+
* @see {@link Gated}
|
|
1005
1053
|
*
|
|
1006
1054
|
* @example
|
|
1007
1055
|
* ```ts
|
|
@@ -1013,10 +1061,6 @@ function channelIsNsfw(channel) {
|
|
|
1013
1061
|
* }
|
|
1014
1062
|
* }
|
|
1015
1063
|
* ```
|
|
1016
|
-
*
|
|
1017
|
-
* @param options - Reword or replace the refusal. Omit to throw the default {@link NotNsfw}.
|
|
1018
|
-
*
|
|
1019
|
-
* @see {@link Gated}
|
|
1020
1064
|
*/
|
|
1021
1065
|
function Nsfw(options) {
|
|
1022
1066
|
return defineGate("Nsfw", (ctx) => {
|
|
@@ -1025,79 +1069,6 @@ function Nsfw(options) {
|
|
|
1025
1069
|
});
|
|
1026
1070
|
}
|
|
1027
1071
|
|
|
1028
|
-
//#endregion
|
|
1029
|
-
//#region src/bot/utilities/permissions/notices.ts
|
|
1030
|
-
function mentionFor(subject) {
|
|
1031
|
-
if (subject instanceof discord_js.Role) return `<@&${subject.id}>`;
|
|
1032
|
-
if (subject instanceof discord_js.TextChannel) return `<#${subject.id}>`;
|
|
1033
|
-
if (subject instanceof discord_js.GuildMember) return `<@${subject.id}>`;
|
|
1034
|
-
return `\`${subject.name}\``;
|
|
1035
|
-
}
|
|
1036
|
-
function labelFor(subject) {
|
|
1037
|
-
if (subject instanceof discord_js.Role) return "role";
|
|
1038
|
-
if (subject instanceof discord_js.TextChannel) return "channel";
|
|
1039
|
-
if (subject instanceof discord_js.GuildMember) return "member";
|
|
1040
|
-
return "guild";
|
|
1041
|
-
}
|
|
1042
|
-
/**
|
|
1043
|
-
* Error thrown when attempting to modify a role higher than the bot's highest role.
|
|
1044
|
-
*/
|
|
1045
|
-
var RoleHigherThanMe = class extends _seedcord_kit.Notice {
|
|
1046
|
-
role;
|
|
1047
|
-
botRole;
|
|
1048
|
-
constructor(message, role, botRole) {
|
|
1049
|
-
super(message);
|
|
1050
|
-
this.role = role;
|
|
1051
|
-
this.botRole = botRole;
|
|
1052
|
-
}
|
|
1053
|
-
render() {
|
|
1054
|
-
return { components: [new _seedcord_kit_internal.NoticeCard(`I cannot assign a role that is higher than me.\n\nThe role <@&${this.role.id}> is higher than my role <@&${this.botRole.id}> in the hierarchy.`).component] };
|
|
1055
|
-
}
|
|
1056
|
-
};
|
|
1057
|
-
/**
|
|
1058
|
-
* Error thrown when attempting to assign a managed/bot role.
|
|
1059
|
-
*/
|
|
1060
|
-
var CannotAssignBotRole = class extends _seedcord_kit.Notice {
|
|
1061
|
-
constructor(message = "I cannot assign a managed role.") {
|
|
1062
|
-
super(message);
|
|
1063
|
-
}
|
|
1064
|
-
render() {
|
|
1065
|
-
return { components: [new _seedcord_kit_internal.NoticeCard("I cannot assign a managed role.").component] };
|
|
1066
|
-
}
|
|
1067
|
-
};
|
|
1068
|
-
/**
|
|
1069
|
-
* Error thrown when required permissions are missing.
|
|
1070
|
-
*/
|
|
1071
|
-
var MissingPermissions = class extends _seedcord_kit.Notice {
|
|
1072
|
-
where;
|
|
1073
|
-
missingPerms;
|
|
1074
|
-
constructor(message, where, missingPerms) {
|
|
1075
|
-
super(message);
|
|
1076
|
-
this.where = where;
|
|
1077
|
-
this.missingPerms = missingPerms;
|
|
1078
|
-
}
|
|
1079
|
-
render() {
|
|
1080
|
-
const bullets = this.missingPerms.map((perm) => `• ${perm}`).join("\n");
|
|
1081
|
-
return { components: [new _seedcord_kit_internal.NoticeCard(`The ${labelFor(this.where)} ${mentionFor(this.where)} is missing the following permission entries:\n\n${bullets}`).component] };
|
|
1082
|
-
}
|
|
1083
|
-
};
|
|
1084
|
-
/**
|
|
1085
|
-
* Error thrown when a target has permissions that must not be present.
|
|
1086
|
-
*/
|
|
1087
|
-
var HasDangerousPermissions = class extends _seedcord_kit.Notice {
|
|
1088
|
-
target;
|
|
1089
|
-
dangerousPerms;
|
|
1090
|
-
constructor(message, target, dangerousPerms) {
|
|
1091
|
-
super(message);
|
|
1092
|
-
this.target = target;
|
|
1093
|
-
this.dangerousPerms = dangerousPerms;
|
|
1094
|
-
}
|
|
1095
|
-
render() {
|
|
1096
|
-
const bullets = this.dangerousPerms.map((perm) => `• ${perm}`).join("\n");
|
|
1097
|
-
return { components: [new _seedcord_kit_internal.NoticeCard(`The ${labelFor(this.target)} ${mentionFor(this.target)} has the following permission entries that must not be enabled:\n\n${bullets}`).component] };
|
|
1098
|
-
}
|
|
1099
|
-
};
|
|
1100
|
-
|
|
1101
1072
|
//#endregion
|
|
1102
1073
|
//#region src/bot/utilities/permissions/checkPermissions.ts
|
|
1103
1074
|
/**
|
|
@@ -1129,36 +1100,15 @@ function checkPermissions(a, b, c, d, e) {
|
|
|
1129
1100
|
//#endregion
|
|
1130
1101
|
//#region src/bot/gates/catalog/permissions.ts
|
|
1131
1102
|
/**
|
|
1132
|
-
* Refusal shown when the caller is missing a required role. {@link RequireRole} throws it.
|
|
1133
|
-
*
|
|
1134
|
-
* The default message reads from `role.name` when the role resolved, otherwise a generic line.
|
|
1135
|
-
*
|
|
1136
|
-
* @example
|
|
1137
|
-
* ```ts
|
|
1138
|
-
* import type { Role } from 'discord.js';
|
|
1139
|
-
*
|
|
1140
|
-
* // throw it from a custom gate when the resolved role is missing
|
|
1141
|
-
* defineGate('NeedsStaff', (ctx: InteractionGateContext<NonModalInteraction>) => {
|
|
1142
|
-
* const role = ctx.guild?.roles.cache.get(STAFF_ROLE_ID) ?? null;
|
|
1143
|
-
* if (!ctx.member?.roles.cache.has(STAFF_ROLE_ID)) throw new MissingRole(undefined, role);
|
|
1144
|
-
* });
|
|
1145
|
-
* ```
|
|
1146
|
-
*
|
|
1147
|
-
* @param message - Custom refusal text, or undefined to use the default that reads from `role.name`.
|
|
1148
|
-
* @param role - The role the caller is missing, or null when it could not be resolved.
|
|
1149
|
-
*/
|
|
1150
|
-
var MissingRole = class extends GateNotice {
|
|
1151
|
-
role;
|
|
1152
|
-
constructor(message, role) {
|
|
1153
|
-
super(message ?? (role ? `You need the ${role.name} role to use this.` : "You do not have the required role."));
|
|
1154
|
-
this.role = role;
|
|
1155
|
-
}
|
|
1156
|
-
};
|
|
1157
|
-
/**
|
|
1158
1103
|
* Requires the caller to hold every permission in `scope`, via `checkPermissions`. Refuses outside a guild.
|
|
1159
1104
|
*
|
|
1160
1105
|
* Interaction-only and excludes ModalSubmit, since a modal lacks a reliable cached caller member.
|
|
1161
1106
|
*
|
|
1107
|
+
* @param scope - The permission flag bits the caller must all hold.
|
|
1108
|
+
* @param options - Override each refusal, the outside-guild one with `notInGuild` and the missing-permissions one with `missing`.
|
|
1109
|
+
*
|
|
1110
|
+
* @see {@link Gated}
|
|
1111
|
+
*
|
|
1162
1112
|
* @example
|
|
1163
1113
|
* ```ts
|
|
1164
1114
|
* import { PermissionFlagsBits } from 'discord.js';
|
|
@@ -1171,16 +1121,11 @@ var MissingRole = class extends GateNotice {
|
|
|
1171
1121
|
* }
|
|
1172
1122
|
* }
|
|
1173
1123
|
* ```
|
|
1174
|
-
*
|
|
1175
|
-
* @param scope - The permission flag bits the caller must all hold.
|
|
1176
|
-
* @param options - Override the refusal Notice with a ctor that receives the where and the missing permission names.
|
|
1177
|
-
*
|
|
1178
|
-
* @see {@link Gated}
|
|
1179
1124
|
*/
|
|
1180
1125
|
function RequirePermissions(scope, options) {
|
|
1181
1126
|
return defineGate("RequirePermissions", (ctx) => {
|
|
1182
|
-
if (!ctx.member || !ctx.guild) throw new NotInGuild();
|
|
1183
|
-
checkPermissions(ctx.member, ctx.guild, scope, false, options?.
|
|
1127
|
+
if (!ctx.member || !ctx.guild) throw pickNotice(options?.notInGuild, (message) => new NotInGuild(message));
|
|
1128
|
+
checkPermissions(ctx.member, ctx.guild, scope, false, options?.missing ? { missing: options.missing } : void 0);
|
|
1184
1129
|
});
|
|
1185
1130
|
}
|
|
1186
1131
|
/**
|
|
@@ -1188,6 +1133,11 @@ function RequirePermissions(scope, options) {
|
|
|
1188
1133
|
*
|
|
1189
1134
|
* Checks the bot's own member, so unlike {@link RequirePermissions} it attaches to a modal handler too.
|
|
1190
1135
|
*
|
|
1136
|
+
* @param scope - The permission flag bits the bot must all hold.
|
|
1137
|
+
* @param options - Override each refusal, the outside-guild one with `notInGuild` and the missing-permissions one with `missing`.
|
|
1138
|
+
*
|
|
1139
|
+
* @see {@link Gated}
|
|
1140
|
+
*
|
|
1191
1141
|
* @example
|
|
1192
1142
|
* ```ts
|
|
1193
1143
|
* import { PermissionFlagsBits } from 'discord.js';
|
|
@@ -1200,23 +1150,24 @@ function RequirePermissions(scope, options) {
|
|
|
1200
1150
|
* }
|
|
1201
1151
|
* }
|
|
1202
1152
|
* ```
|
|
1203
|
-
*
|
|
1204
|
-
* @param scope - The permission flag bits the bot must all hold.
|
|
1205
|
-
* @param options - Override the refusal Notice with a ctor that receives the where and the missing permission names.
|
|
1206
|
-
*
|
|
1207
|
-
* @see {@link Gated}
|
|
1208
1153
|
*/
|
|
1209
1154
|
function RequireBotPermissions(scope, options) {
|
|
1210
1155
|
return defineGate("RequireBotPermissions", (ctx) => {
|
|
1211
1156
|
const botMember = ctx.guild?.members.me;
|
|
1212
|
-
if (!ctx.guild || !botMember) throw new NotInGuild();
|
|
1213
|
-
checkPermissions(botMember, ctx.guild, scope, false, options?.
|
|
1157
|
+
if (!ctx.guild || !botMember) throw pickNotice(options?.notInGuild, (message) => new NotInGuild(message));
|
|
1158
|
+
checkPermissions(botMember, ctx.guild, scope, false, options?.missing ? { missing: options.missing } : void 0);
|
|
1214
1159
|
});
|
|
1215
1160
|
}
|
|
1216
1161
|
/**
|
|
1217
|
-
* Requires the caller to have `roleId`, read from the member's role cache. Refuses
|
|
1162
|
+
* Requires the caller to have `roleId`, read from the member's role cache. Refuses outside a guild first, then
|
|
1163
|
+
* when the role is missing.
|
|
1164
|
+
*
|
|
1165
|
+
* Interaction-only and excludes ModalSubmit.
|
|
1218
1166
|
*
|
|
1219
|
-
*
|
|
1167
|
+
* @param roleId - The role snowflake the caller must hold, read from the member's role cache.
|
|
1168
|
+
* @param options - Override each refusal, the outside-guild one with `notInGuild` and the missing-role one with `missingRole`.
|
|
1169
|
+
*
|
|
1170
|
+
* @see {@link Gated}
|
|
1220
1171
|
*
|
|
1221
1172
|
* @example
|
|
1222
1173
|
* ```ts
|
|
@@ -1228,18 +1179,13 @@ function RequireBotPermissions(scope, options) {
|
|
|
1228
1179
|
* }
|
|
1229
1180
|
* }
|
|
1230
1181
|
* ```
|
|
1231
|
-
*
|
|
1232
|
-
* @param roleId - The role snowflake the caller must hold, read from the member's role cache.
|
|
1233
|
-
* @param options - Reword the refusal message or replace the {@link MissingRole} Notice entirely.
|
|
1234
|
-
*
|
|
1235
|
-
* @see {@link Gated}
|
|
1236
1182
|
*/
|
|
1237
1183
|
function RequireRole(roleId, options) {
|
|
1238
1184
|
return defineGate("RequireRole", (ctx) => {
|
|
1239
|
-
if (!ctx.member || !ctx.guild) throw new NotInGuild();
|
|
1185
|
+
if (!ctx.member || !ctx.guild) throw pickNotice(options?.notInGuild, (message) => new NotInGuild(message));
|
|
1240
1186
|
if (ctx.member.roles.cache.has(roleId)) return;
|
|
1241
1187
|
const role = ctx.guild.roles.cache.get(roleId) ?? null;
|
|
1242
|
-
throw pickNotice(options, (message) => new MissingRole(message, role));
|
|
1188
|
+
throw pickNotice(options?.missingRole, (message) => new MissingRole(message, role));
|
|
1243
1189
|
});
|
|
1244
1190
|
}
|
|
1245
1191
|
|
|
@@ -1335,25 +1281,11 @@ var EmojiInjector = class {
|
|
|
1335
1281
|
//#endregion
|
|
1336
1282
|
//#region src/bot/utilities/channels/fetchText.ts
|
|
1337
1283
|
/**
|
|
1338
|
-
*
|
|
1339
|
-
*/
|
|
1340
|
-
var CouldNotFindChannel = class extends _seedcord_kit.Notice {
|
|
1341
|
-
channelId;
|
|
1342
|
-
constructor(message, channelId) {
|
|
1343
|
-
super(message);
|
|
1344
|
-
this.channelId = channelId;
|
|
1345
|
-
}
|
|
1346
|
-
render() {
|
|
1347
|
-
return { components: [new _seedcord_kit_internal.NoticeCard(`Could not find channel with ID \`${this.channelId}\`. It could also be that the channel is not a text channel.`).component] };
|
|
1348
|
-
}
|
|
1349
|
-
};
|
|
1350
|
-
/**
|
|
1351
|
-
* Fetches and validates a text channel.
|
|
1284
|
+
* Fetches and validates a text channel. Refuses when the channel doesn't exist or isn't a text channel.
|
|
1352
1285
|
*
|
|
1353
1286
|
* @param client - The Discord client instance
|
|
1354
1287
|
* @param channelId - Channel ID or TextChannel instance
|
|
1355
1288
|
* @returns Promise resolving to the text channel
|
|
1356
|
-
* @throws A {@link CouldNotFindChannel} When the channel doesn't exist or isn't text-based
|
|
1357
1289
|
*/
|
|
1358
1290
|
async function fetchText(client, channelId) {
|
|
1359
1291
|
if (channelId instanceof discord_js.TextChannel) return channelId;
|
|
@@ -1371,13 +1303,13 @@ async function fetchText(client, channelId) {
|
|
|
1371
1303
|
//#endregion
|
|
1372
1304
|
//#region src/bot/utilities/permissions/checkBotPermissions.ts
|
|
1373
1305
|
/**
|
|
1374
|
-
* Checks if the bot has required permissions in a {@link Guild} or {@link TextChannel}.
|
|
1306
|
+
* Checks if the bot has required permissions in a {@link Guild} or {@link TextChannel}. Refuses when a
|
|
1307
|
+
* required permission is missing.
|
|
1375
1308
|
*
|
|
1376
1309
|
* @param target - Guild or text channel to check in
|
|
1377
1310
|
* @param scope - Permission bits to validate
|
|
1378
1311
|
* @param inverse - Whether to check for absence of the given permissions
|
|
1379
1312
|
* @param errors - Optional custom error constructors
|
|
1380
|
-
* @throws A {@link MissingPermissions} error when required permissions are missing or when bot member is unavailable/uncached
|
|
1381
1313
|
*
|
|
1382
1314
|
* @example
|
|
1383
1315
|
* ```ts
|
|
@@ -1417,7 +1349,7 @@ function checkBotPermissions(target, scope, inverse = false, errors) {
|
|
|
1417
1349
|
*
|
|
1418
1350
|
* @param guild - The guild to get the bot role from
|
|
1419
1351
|
* @returns The bot's managed role in the guild
|
|
1420
|
-
* @throws A
|
|
1352
|
+
* @throws A **SeedcordError** if the client user is unavailable or if the bot role is missing
|
|
1421
1353
|
*/
|
|
1422
1354
|
function getBotRole(guild) {
|
|
1423
1355
|
const botRole = guild.roles.botRoleFor(guild.client.user);
|
|
@@ -1428,12 +1360,9 @@ function getBotRole(guild) {
|
|
|
1428
1360
|
//#endregion
|
|
1429
1361
|
//#region src/bot/utilities/permissions/hasPermsToAssign.ts
|
|
1430
1362
|
/**
|
|
1431
|
-
* Validates if the bot can assign a target role.
|
|
1363
|
+
* Validates if 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.
|
|
1432
1364
|
*
|
|
1433
1365
|
* @param roleOrOptions - Target role or complete options for the check
|
|
1434
|
-
* @throws A {@link RoleHigherThanMe} When the target role is higher than the bot role
|
|
1435
|
-
* @throws A {@link CannotAssignBotRole} When the target role is managed
|
|
1436
|
-
* @throws A {@link MissingPermissions} When the bot lacks Manage Roles permission
|
|
1437
1366
|
*
|
|
1438
1367
|
* @example
|
|
1439
1368
|
* ```ts
|
|
@@ -1469,19 +1398,6 @@ function hasPermsToAssign(roleOrOptions) {
|
|
|
1469
1398
|
|
|
1470
1399
|
//#endregion
|
|
1471
1400
|
//#region src/bot/utilities/roles/fetchRole.ts
|
|
1472
|
-
/**
|
|
1473
|
-
* Error thrown when a requested role does not exist.
|
|
1474
|
-
*/
|
|
1475
|
-
var RoleDoesNotExist = class extends _seedcord_kit.Notice {
|
|
1476
|
-
roleId;
|
|
1477
|
-
constructor(message, roleId) {
|
|
1478
|
-
super(message);
|
|
1479
|
-
this.roleId = roleId;
|
|
1480
|
-
}
|
|
1481
|
-
render() {
|
|
1482
|
-
return { components: [new _seedcord_kit_internal.NoticeCard(`The role with ID \`${this.roleId}\` does not exist.`).component] };
|
|
1483
|
-
}
|
|
1484
|
-
};
|
|
1485
1401
|
function isUnknownRole(err) {
|
|
1486
1402
|
return err instanceof discord_js.DiscordAPIError && err.code === discord_js.RESTJSONErrorCodes.UnknownRole;
|
|
1487
1403
|
}
|
|
@@ -1508,12 +1424,11 @@ async function scanGuildsForRole(client, roleId) {
|
|
|
1508
1424
|
return null;
|
|
1509
1425
|
}
|
|
1510
1426
|
/**
|
|
1511
|
-
* Fetches a role by ID from a client or guild.
|
|
1427
|
+
* Fetches a role by ID from a client or guild. Refuses when the role doesn't exist.
|
|
1512
1428
|
*
|
|
1513
1429
|
* @param clientOrGuild - Discord client or guild instance
|
|
1514
1430
|
* @param roleId - The role ID to fetch
|
|
1515
1431
|
* @returns Promise resolving to the role
|
|
1516
|
-
* @throws A {@link RoleDoesNotExist} When the role doesn't exist
|
|
1517
1432
|
*/
|
|
1518
1433
|
async function fetchRole(clientOrGuild, roleId) {
|
|
1519
1434
|
if (!roleId) throw new RoleDoesNotExist("Role ID is null or undefined", roleId);
|
|
@@ -1525,23 +1440,11 @@ async function fetchRole(clientOrGuild, roleId) {
|
|
|
1525
1440
|
//#endregion
|
|
1526
1441
|
//#region src/bot/utilities/users/fetchGuildMember.ts
|
|
1527
1442
|
/**
|
|
1528
|
-
*
|
|
1529
|
-
*/
|
|
1530
|
-
var UserNotInGuild = class extends _seedcord_kit.Notice {
|
|
1531
|
-
constructor(message = "User is not in the guild.") {
|
|
1532
|
-
super(message);
|
|
1533
|
-
}
|
|
1534
|
-
render() {
|
|
1535
|
-
return { components: [new _seedcord_kit_internal.NoticeCard(this.message).component] };
|
|
1536
|
-
}
|
|
1537
|
-
};
|
|
1538
|
-
/**
|
|
1539
|
-
* Fetches a guild member by user ID with error handling.
|
|
1443
|
+
* Fetches a guild member by user ID. Refuses when the user is not in the guild.
|
|
1540
1444
|
*
|
|
1541
1445
|
* @param guild - The guild to fetch the member from
|
|
1542
1446
|
* @param userId - The Discord user ID
|
|
1543
1447
|
* @returns Promise resolving to the guild member
|
|
1544
|
-
* @throws A {@link UserNotInGuild} When the user is not found in the guild
|
|
1545
1448
|
*/
|
|
1546
1449
|
async function fetchGuildMember(guild, userId) {
|
|
1547
1450
|
let user = guild.members.cache.get(userId);
|
|
@@ -1568,26 +1471,12 @@ async function fetchManyGuildMembers(guild, userIds) {
|
|
|
1568
1471
|
//#endregion
|
|
1569
1472
|
//#region src/bot/utilities/users/fetchUser.ts
|
|
1570
1473
|
/**
|
|
1571
|
-
*
|
|
1572
|
-
*/
|
|
1573
|
-
var UserNotFound = class extends _seedcord_kit.Notice {
|
|
1574
|
-
userArg;
|
|
1575
|
-
constructor(userArg) {
|
|
1576
|
-
super(`User not found: ${userArg}`);
|
|
1577
|
-
this.userArg = userArg;
|
|
1578
|
-
}
|
|
1579
|
-
render() {
|
|
1580
|
-
return { components: [new _seedcord_kit_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] };
|
|
1581
|
-
}
|
|
1582
|
-
};
|
|
1583
|
-
/**
|
|
1584
|
-
* Fetches a Discord user by ID with error handling.
|
|
1474
|
+
* Fetches a Discord user by ID. Refuses when the user doesn't exist, with `options.throwAs` or the default.
|
|
1585
1475
|
*
|
|
1586
1476
|
* @param client - The Discord client instance
|
|
1587
1477
|
* @param userId - The Discord user ID
|
|
1588
|
-
* @param options - Optional overrides, including the {@link FetchUserOptions.throwAs}
|
|
1478
|
+
* @param options - Optional overrides, including the {@link FetchUserOptions.throwAs} notice
|
|
1589
1479
|
* @returns Promise resolving to the user
|
|
1590
|
-
* @throws The {@link FetchUserOptions.throwAs} denial when the user doesn't exist
|
|
1591
1480
|
*/
|
|
1592
1481
|
async function fetchUser(client, userId, options) {
|
|
1593
1482
|
const Throw = options?.throwAs ?? UserNotFound;
|
|
@@ -1898,7 +1787,7 @@ var Pluggable = class Pluggable extends _seedcord_services.StrictEventEmitter {
|
|
|
1898
1787
|
* @param startupPhase - When during startup to initialize this plugin ({@link StartupPhase})
|
|
1899
1788
|
* @param args - Additional arguments to pass to the plugin constructor
|
|
1900
1789
|
* @returns This instance with the plugin attached as a typed property
|
|
1901
|
-
* @throws A
|
|
1790
|
+
* @throws A **SeedcordError** When called after initialization or if key already exists
|
|
1902
1791
|
* @example
|
|
1903
1792
|
* ```typescript
|
|
1904
1793
|
* seedcord.attach('db', Mongo, StartupPhase.Configuration, { uri: 'mongodb://...', name: 'seedcord', dir: ... })
|
|
@@ -2318,14 +2207,14 @@ var ModalHandler = class extends ComponentHandler {};
|
|
|
2318
2207
|
* so `this.event` and `this.event.values` are narrowed to that kind. Read `this.params` for a single
|
|
2319
2208
|
* route or `this.match` for several.
|
|
2320
2209
|
*
|
|
2321
|
-
* @typeParam Kind - The select kind from {@link
|
|
2210
|
+
* @typeParam Kind - The select kind from {@link SelectMenuKind}, e.g. `SelectMenuKind.User`.
|
|
2322
2211
|
* @typeParam Defs - The customId definitions this handler decodes, e.g. `[typeof AssignId]`.
|
|
2323
2212
|
* @typeParam Cache - The interaction cache state, `'cached'` by default.
|
|
2324
2213
|
*
|
|
2325
2214
|
* @example
|
|
2326
2215
|
* ```ts
|
|
2327
|
-
* \@SelectMenuRoute(
|
|
2328
|
-
* class AssignSelect extends SelectHandler<
|
|
2216
|
+
* \@SelectMenuRoute(SelectMenuKind.User, AssignId)
|
|
2217
|
+
* class AssignSelect extends SelectHandler<SelectMenuKind.User, [typeof AssignId]> {
|
|
2329
2218
|
* async execute() {
|
|
2330
2219
|
* const { roleId } = this.params;
|
|
2331
2220
|
* await this.event.reply(`assigning ${this.event.values.length} member(s) to <@&${roleId}>`);
|
|
@@ -2459,12 +2348,6 @@ var Subscriber = class {
|
|
|
2459
2348
|
//#endregion
|
|
2460
2349
|
//#region src/subscribers/decorators/Subscribe.ts
|
|
2461
2350
|
/**
|
|
2462
|
-
* Metadata key used to store subscriber handler information
|
|
2463
|
-
*
|
|
2464
|
-
* @internal
|
|
2465
|
-
*/
|
|
2466
|
-
const SubscribeMetadataKey = Symbol("subscribe:metadata");
|
|
2467
|
-
/**
|
|
2468
2351
|
* Registers a subscriber handler class with a specific subscriber event.
|
|
2469
2352
|
*
|
|
2470
2353
|
* Associates the decorated class with a subscriber event type for automatic
|
|
@@ -3168,7 +3051,7 @@ var EventDispatcher = class {
|
|
|
3168
3051
|
}
|
|
3169
3052
|
registerMiddleware(middlewareCtor, relativePath) {
|
|
3170
3053
|
const metadata = Reflect.getMetadata(MiddlewareMetadataKey, middlewareCtor);
|
|
3171
|
-
if (metadata?.type !== "
|
|
3054
|
+
if (metadata?.type !== "event") return;
|
|
3172
3055
|
if (this.middlewares.some((entry) => entry.ctor === middlewareCtor)) return;
|
|
3173
3056
|
this.middlewares.push({
|
|
3174
3057
|
ctor: middlewareCtor,
|
|
@@ -3344,17 +3227,17 @@ var InteractionDispatcher = class {
|
|
|
3344
3227
|
middlewares = [];
|
|
3345
3228
|
hmrHandler;
|
|
3346
3229
|
routeTypes = [
|
|
3347
|
-
["
|
|
3348
|
-
["
|
|
3349
|
-
["
|
|
3350
|
-
["
|
|
3351
|
-
["
|
|
3352
|
-
["
|
|
3353
|
-
["
|
|
3354
|
-
["
|
|
3355
|
-
["
|
|
3356
|
-
["
|
|
3357
|
-
["
|
|
3230
|
+
["slash", this.slashMap],
|
|
3231
|
+
["button", this.buttonMap],
|
|
3232
|
+
["modal", this.modalMap],
|
|
3233
|
+
["stringMenu", this.stringSelectMap],
|
|
3234
|
+
["userMenu", this.userSelectMap],
|
|
3235
|
+
["roleMenu", this.roleSelectMap],
|
|
3236
|
+
["channelMenu", this.channelSelectMap],
|
|
3237
|
+
["mentionableMenu", this.mentionableSelectMap],
|
|
3238
|
+
["messageContextMenu", this.messageContextMenuMap],
|
|
3239
|
+
["userContextMenu", this.userContextMenuMap],
|
|
3240
|
+
["autocomplete", this.autocompleteMap]
|
|
3358
3241
|
];
|
|
3359
3242
|
constructor(core) {
|
|
3360
3243
|
this.core = core;
|
|
@@ -3400,7 +3283,7 @@ var InteractionDispatcher = class {
|
|
|
3400
3283
|
getArtifacts(handlerClass) {
|
|
3401
3284
|
const artifacts = [];
|
|
3402
3285
|
for (const [routeType] of this.routeTypes) {
|
|
3403
|
-
const meta = Reflect.getMetadata(routeType, handlerClass);
|
|
3286
|
+
const meta = Reflect.getMetadata(InteractionRouteKeys[routeType], handlerClass);
|
|
3404
3287
|
if (areRoutes(meta)) artifacts.push({
|
|
3405
3288
|
routeType,
|
|
3406
3289
|
routes: meta
|
|
@@ -3457,7 +3340,7 @@ var InteractionDispatcher = class {
|
|
|
3457
3340
|
}
|
|
3458
3341
|
registerMiddleware(middlewareCtor, relativePath) {
|
|
3459
3342
|
const metadata = Reflect.getMetadata(MiddlewareMetadataKey, middlewareCtor);
|
|
3460
|
-
if (metadata?.type !== "
|
|
3343
|
+
if (metadata?.type !== "interaction") return;
|
|
3461
3344
|
const existingIndex = this.middlewares.findIndex((entry) => entry.ctor.name === middlewareCtor.name);
|
|
3462
3345
|
if (existingIndex !== -1) this.middlewares[existingIndex] = {
|
|
3463
3346
|
ctor: middlewareCtor,
|
|
@@ -3480,7 +3363,7 @@ var InteractionDispatcher = class {
|
|
|
3480
3363
|
}
|
|
3481
3364
|
registerHandler(handlerClass, relativePath) {
|
|
3482
3365
|
for (const [routeType, map] of this.routeTypes) {
|
|
3483
|
-
const meta = Reflect.getMetadata(routeType, handlerClass);
|
|
3366
|
+
const meta = Reflect.getMetadata(InteractionRouteKeys[routeType], handlerClass);
|
|
3484
3367
|
if (!areRoutes(meta)) continue;
|
|
3485
3368
|
meta.forEach((route) => map.set(route, handlerClass));
|
|
3486
3369
|
this.logger.utils.registration(handlerClass.name, (0, _seedcord_utils.formatFilePath)(relativePath));
|
|
@@ -3499,7 +3382,7 @@ var InteractionDispatcher = class {
|
|
|
3499
3382
|
return;
|
|
3500
3383
|
}
|
|
3501
3384
|
for (const [routeType, map] of this.routeTypes) {
|
|
3502
|
-
const meta = Reflect.getMetadata(routeType, handlerClass);
|
|
3385
|
+
const meta = Reflect.getMetadata(InteractionRouteKeys[routeType], handlerClass);
|
|
3503
3386
|
if (!areRoutes(meta)) continue;
|
|
3504
3387
|
meta.forEach((route) => map.delete(route));
|
|
3505
3388
|
}
|
|
@@ -4095,7 +3978,7 @@ var Seedcord = class Seedcord extends Pluggable {
|
|
|
4095
3978
|
* Creates a new Seedcord instance
|
|
4096
3979
|
*
|
|
4097
3980
|
* @param config - Bot configuration including paths and Discord client options
|
|
4098
|
-
* @throws A
|
|
3981
|
+
* @throws A **SeedcordError** When attempting to create multiple instances (singleton)
|
|
4099
3982
|
*/
|
|
4100
3983
|
constructor(config) {
|
|
4101
3984
|
if (Seedcord.isInstantiated) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.CoreSingletonViolation);
|
|
@@ -4165,14 +4048,13 @@ var Seedcord = class Seedcord extends Pluggable {
|
|
|
4165
4048
|
//#endregion
|
|
4166
4049
|
//#region src/index.ts
|
|
4167
4050
|
/** Package version */
|
|
4168
|
-
const version = "0.13.0
|
|
4051
|
+
const version = "0.13.0";
|
|
4169
4052
|
|
|
4170
4053
|
//#endregion
|
|
4171
4054
|
exports.AutocompleteHandler = AutocompleteHandler;
|
|
4172
4055
|
exports.AutocompleteRoute = AutocompleteRoute;
|
|
4173
4056
|
exports.ButtonHandler = ButtonHandler;
|
|
4174
4057
|
exports.ButtonRoute = ButtonRoute;
|
|
4175
|
-
exports.CannotAssignBotRole = CannotAssignBotRole;
|
|
4176
4058
|
exports.ContextMenuHandler = ContextMenuHandler;
|
|
4177
4059
|
exports.ContextMenuRoute = ContextMenuRoute;
|
|
4178
4060
|
exports.Cooldown = Cooldown;
|
|
@@ -4180,26 +4062,17 @@ exports.DmOnly = DmOnly;
|
|
|
4180
4062
|
exports.Emojis = Emojis;
|
|
4181
4063
|
exports.EventHandler = EventHandler;
|
|
4182
4064
|
exports.EventMiddleware = EventMiddleware;
|
|
4183
|
-
exports.GateNotice = GateNotice;
|
|
4184
4065
|
exports.Gated = Gated;
|
|
4185
4066
|
exports.GuildOnly = GuildOnly;
|
|
4186
|
-
exports.HasDangerousPermissions = HasDangerousPermissions;
|
|
4187
4067
|
exports.HmrModuleHandler = HmrModuleHandler;
|
|
4188
4068
|
exports.IgnoreBots = IgnoreBots;
|
|
4189
4069
|
exports.InteractionHandler = InteractionHandler;
|
|
4190
4070
|
exports.InteractionMiddleware = InteractionMiddleware;
|
|
4191
4071
|
exports.Middleware = Middleware;
|
|
4192
4072
|
exports.MiddlewareType = MiddlewareType;
|
|
4193
|
-
exports.MissingPermissions = MissingPermissions;
|
|
4194
|
-
exports.MissingRole = MissingRole;
|
|
4195
4073
|
exports.ModalHandler = ModalHandler;
|
|
4196
4074
|
exports.ModalRoute = ModalRoute;
|
|
4197
|
-
exports.NotInDm = NotInDm;
|
|
4198
|
-
exports.NotInGuild = NotInGuild;
|
|
4199
|
-
exports.NotNsfw = NotNsfw;
|
|
4200
|
-
exports.NotOwner = NotOwner;
|
|
4201
4075
|
exports.Nsfw = Nsfw;
|
|
4202
|
-
exports.OnCooldown = OnCooldown;
|
|
4203
4076
|
exports.OwnerOnly = OwnerOnly;
|
|
4204
4077
|
exports.Pluggable = Pluggable;
|
|
4205
4078
|
exports.Plugin = Plugin;
|
|
@@ -4209,11 +4082,10 @@ exports.ReplySender = ReplySender;
|
|
|
4209
4082
|
exports.RequireBotPermissions = RequireBotPermissions;
|
|
4210
4083
|
exports.RequirePermissions = RequirePermissions;
|
|
4211
4084
|
exports.RequireRole = RequireRole;
|
|
4212
|
-
exports.RoleHigherThanMe = RoleHigherThanMe;
|
|
4213
4085
|
exports.Seedcord = Seedcord;
|
|
4214
4086
|
exports.SelectHandler = SelectHandler;
|
|
4087
|
+
exports.SelectMenuKind = SelectMenuKind;
|
|
4215
4088
|
exports.SelectMenuRoute = SelectMenuRoute;
|
|
4216
|
-
exports.SelectMenuType = SelectMenuType;
|
|
4217
4089
|
exports.SlashHandler = SlashHandler;
|
|
4218
4090
|
exports.SlashRoute = SlashRoute;
|
|
4219
4091
|
exports.Subscribe = Subscribe;
|