@seedcord/gateway 0.5.1 → 0.6.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.d.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  import "reflect-metadata";
2
2
  import { WinstonConsoleSink, WinstonFileSink } from "@seedcord/logger/node";
3
- import { AutocompletableNames, AutocompleteRouteBrand, CacheFor, ChoiceValueOf, ComponentDefsBrand, ComponentKindBrand, ContextMenuKindBrand, ContextMenuLeaves, ContextMenuNamesBrand, EntryFor, FocusedField, GateFitsWith, HasComponentDefs, Initializeable, MatchArms, MenuCacheFor, NamesFor, PageCursor, SingleParams, SlashOptions as SlashOptions$1, SlashRouteBrand } from "@seedcord/core/internal";
4
- import { ArraySourceBase, AutocompleteOptions, BaseHandler as BaseHandler$1, BaseReplySender, Bus, CoreBase, CursorSourceBase, DispatchContext, Gate, GateContextBase, GateNoticeOptions, GuildPermissionsContext, InteractionKind, ModalLike, Notice, PageSourceBase, PaginatorBase, PaginatorConfig, PermissionNoticeOverrides, PermissionScope, RepliableHandler as RepliableHandler$1, RequiredOf, ResolvedEmoji, SlashRegistry, Subscriber as Subscriber$1, SubscriptionKey, WebhookLog as WebhookLog$1 } from "@seedcord/core";
3
+ import { AutocompletableNames, AutocompleteRouteBrand, CacheFor, ChoiceValueOf, ComponentDefsBrand, ComponentKindBrand, ContextMenuKindBrand, ContextMenuLeaves, ContextMenuNamesBrand, EntryFor, FocusedField, GateFitsWith, HasComponentDefs, Initializeable, MatchArms, MenuCacheFor, MiddlewareKindsBrand, NamesFor, PageCursor, SingleParams, SlashOptions as SlashOptions$1, SlashRouteBrand } from "@seedcord/core/internal";
4
+ import { ArraySourceBase, AutocompleteOptions, BaseHandler as BaseHandler$1, BaseReplySender, Bus, CoreBase, CursorSourceBase, DispatchContext, DispatchOutcome, DispatchResult, EventDispatchResult, Gate, GateContextBase, GateNoticeOptions, GuildPermissionsContext, HandlerOutcome, InteractionKind, MiddlewareKind, ModalLike, Notice, PageSourceBase, PaginatorBase, PaginatorConfig, PermissionNoticeOverrides, PermissionScope, RepliableHandler as RepliableHandler$1, RequiredOf, ResolvedEmoji, SlashRegistry, Subscriber as Subscriber$1, SubscriptionKey, WebhookLog as WebhookLog$1 } from "@seedcord/core";
5
5
  import { Logger } from "@seedcord/logger";
6
6
  import { APIInteractionGuildMember, APIModalInteractionResponseCallbackData, AnySelectMenuInteraction, ApplicationCommandOptionChoiceData, ApplicationCommandType, ApplicationEmoji, AutocompleteInteraction, BaseMessageOptions, BooleanCache, ButtonInteraction, ButtonStyle, CacheType, CacheTypeReducer, ChannelSelectMenuInteraction, ChatInputCommandInteraction, Client, ClientEvents, ClientOptions, CommandInteractionOption, ContextMenuCommandInteraction, Events, Guild, GuildEmoji, GuildMember, Interaction, MentionableSelectMenuInteraction, Message, MessageContextMenuCommandInteraction, ModalSubmitFields, ModalSubmitInteraction, Role, RoleSelectMenuInteraction, StringSelectMenuInteraction, TextChannel, User, UserContextMenuCommandInteraction, UserSelectMenuInteraction } from "discord.js";
7
7
  import { Plugin as Plugin$1, PluginLifecycleSpec, PluginOptions, PluginOptions as PluginOptions$1 } from "@seedcord/core/plugin";
@@ -27,9 +27,25 @@ declare module '@seedcord/core/internal' {
27
27
  };
28
28
  /** Triggered before an event's handlers run. An event that no handler registered never triggers it. */
29
29
  eventDispatching: { [Name in keyof ClientEvents]: {
30
+ dispatchId: string;
30
31
  name: Name;
31
32
  args: ClientEvents[Name];
32
33
  }; }[keyof ClientEvents];
34
+ /**
35
+ * Triggered once an event's handlers settle, pairing with `eventDispatching`. A fire that runs no
36
+ * handler triggers neither.
37
+ */
38
+ eventDispatched: {
39
+ /** The same id `eventDispatching` published for this fire. */
40
+ dispatchId: string;
41
+ name: keyof ClientEvents;
42
+ /** How the middleware chain ended. A chain that refused the event leaves `handlers` empty. */
43
+ outcome: DispatchOutcome;
44
+ /** One entry per handler that ran, in the order they ran. */
45
+ handlers: readonly HandlerOutcome[];
46
+ /** Dispatch entry until every handler settles. A clock change never affects it. */
47
+ durationMs: number;
48
+ };
33
49
  /** Triggered for every interaction the bot receives, before routing. */
34
50
  anyInteraction: {
35
51
  interaction: Interaction;
@@ -159,6 +175,8 @@ declare class InteractionDispatcher implements Initializeable, HmrAware {
159
175
  private processInteraction;
160
176
  private answer;
161
177
  private buildHandler;
178
+ private reporterFor;
179
+ private refusalBeforeHandler;
162
180
  private runMiddlewares;
163
181
  private gateRefusal;
164
182
  private handleInteraction;
@@ -469,7 +487,7 @@ type EventMatchArms<Names extends ValidNonInteractionKeys, Ret> = { [Name in Nam
469
487
  */
470
488
  declare abstract class EventHandler<in out Names extends ValidNonInteractionKeys> extends BaseHandler<ClientEvents[Names]> {
471
489
  private readonly firedEvent;
472
- constructor(event: ClientEvents[Names], core: Core, eventName?: Names);
490
+ constructor(event: ClientEvents[Names], core: Core, dispatch: DispatchContext, eventName?: Names);
473
491
  protected readonly event: SingleEventPayload<Names>;
474
492
  /**
475
493
  * Run the arm for whichever event fired. Use this only when the handler is registered for several
@@ -513,7 +531,7 @@ declare abstract class EventHandler<in out Names extends ValidNonInteractionKeys
513
531
  */
514
532
  declare abstract class EventMiddleware<in out EventName extends ValidNonInteractionKeys = ValidNonInteractionKeys> extends BaseHandler<ClientEvents[EventName]> {
515
533
  private readonly firedEvent;
516
- constructor(event: ClientEvents[EventName], core: Core, eventName?: EventName);
534
+ constructor(event: ClientEvents[EventName], core: Core, dispatch: DispatchContext, eventName?: EventName);
517
535
  protected readonly event: SingleEventPayload<EventName>;
518
536
  /**
519
537
  * The event that fired this middleware. Read it on a catchall or multi-event middleware, where `this.event`
@@ -521,6 +539,23 @@ declare abstract class EventMiddleware<in out EventName extends ValidNonInteract
521
539
  * `this.event` directly and does not need this.
522
540
  */
523
541
  protected get eventName(): EventName;
542
+ /**
543
+ * Runs once the event finishes, newest middleware first. Every middleware whose `execute()`
544
+ * started gets the call, a stopped chain and a throw included. Implement it to release something
545
+ * this middleware took in `execute()`. A throw in here is logged and goes no further.
546
+ *
547
+ * @param result - `outcome` reports the chain alone. `handlers` holds one entry per handler that
548
+ * ran, empty exactly when the chain stopped the event.
549
+ *
550
+ * @example
551
+ * ```ts
552
+ * // the base declares it. an implementation carries `override`.
553
+ * override async after(result: EventDispatchResult) {
554
+ * this.span.end();
555
+ * }
556
+ * ```
557
+ */
558
+ after?(result: EventDispatchResult): Promise<void>;
524
559
  }
525
560
  //#endregion
526
561
  //#region src/bot/decorators/Events.d.ts
@@ -582,7 +617,7 @@ type SentMessage = Message;
582
617
  /** Writes interaction responses through discord.js. The repliable handler bases and the dispatcher construct it. */
583
618
  declare class ReplySender extends BaseReplySender<SentMessage, GatewayFile> {
584
619
  private readonly interaction;
585
- constructor(interaction: Repliables, routeId: string, bus: Bus);
620
+ constructor(interaction: Repliables, dispatch: DispatchContext, bus: Bus);
586
621
  protected writeReply(response: GatewayReplyResponse | string, opts?: SendOpts): Promise<SentMessage | undefined>;
587
622
  protected writeDefer(opts?: DeferOpts): Promise<void>;
588
623
  protected writeDeferUpdate(): Promise<void>;
@@ -615,7 +650,7 @@ declare class ReplySender extends BaseReplySender<SentMessage, GatewayFile> {
615
650
  declare abstract class RepliableHandler<Event extends Repliables> extends RepliableHandler$1<Event, Core, SentMessage, GatewayFile, ReplySender> {
616
651
  /** @internal */
617
652
  getEvent(): Event;
618
- protected buildSender(event: Event, core: Core, routeId: string): ReplySender;
653
+ protected buildSender(event: Event, core: Core, dispatch: DispatchContext): ReplySender;
619
654
  }
620
655
  //#endregion
621
656
  //#region src/handlers/interaction/InteractionHandler.d.ts
@@ -629,7 +664,7 @@ declare abstract class RepliableHandler<Event extends Repliables> extends Replia
629
664
  * @typeParam Repliable - The interaction type this handler processes
630
665
  */
631
666
  declare abstract class InteractionHandler<Repliable extends Repliables> extends RepliableHandler<Repliable> {
632
- constructor(event: Repliable, core: Core, dispatch?: DispatchContext);
667
+ constructor(event: Repliable, core: Core, dispatch: DispatchContext);
633
668
  /**
634
669
  * Open a modal. Must be the initial response to this interaction. The modal kind rejects this call at
635
670
  * compile time (Discord forbids a modal in response to a modal).
@@ -771,248 +806,649 @@ type FitAll<TCtor extends AnyHandlerCtor, Gates extends readonly Gate<GateContex
771
806
  */
772
807
  declare function Gated<const Gates extends NonEmptyTuple<Gate<GateContextBase>>>(...gates: Gates): <TCtor extends AnyHandlerCtor>(ctor: FitAll<TCtor, Gates> extends Gates ? TCtor : FitAll<TCtor, Gates>) => void;
773
808
  //#endregion
774
- //#region src/handlers/interaction/InteractionMiddleware.d.ts
775
- /**
776
- * Base class for interaction middleware
777
- *
778
- * Middleware runs before interaction handlers and can modify behavior or block execution.
779
- * Unlike handlers, middleware should not send responses directly.
780
- *
781
- * @typeParam Repliable - The interaction type this middleware processes
782
- */
783
- declare abstract class InteractionMiddleware<Repliable extends Repliables> extends BaseHandler<Repliable> {
784
- constructor(event: Repliable, core: Core);
785
- }
786
- //#endregion
787
- //#region src/inputs/SlashOptions.d.ts
788
- interface GatewayLens<Cache extends CacheType> {
789
- user: NonNullable<CommandInteractionOption<Cache>['user']>;
790
- member: NonNullable<CommandInteractionOption<Cache>['member']>;
791
- channel: NonNullable<CommandInteractionOption<Cache>['channel']>;
792
- role: NonNullable<CommandInteractionOption<Cache>['role']>;
793
- mentionable: NonNullable<CommandInteractionOption<Cache>['member' | 'role' | 'user']>;
794
- attachment: NonNullable<CommandInteractionOption<Cache>['attachment']>;
809
+ //#region src/bot/decorators/Middlewares.d.ts
810
+ /** Registration options for an event middleware. */
811
+ interface EventMiddlewareOptions<Events extends NonEmptyTuple<ValidNonInteractionKeys>> {
812
+ /**
813
+ * Restrict this middleware to certain Discord client events. The middleware's `EventMiddleware`
814
+ * generic must list the same events, or applying the decorator is a compile error. Omit it to run on
815
+ * every event.
816
+ */
817
+ readonly events?: Events;
818
+ /** Lower runs earlier. Two middleware sharing a priority run in registration order.
819
+ *
820
+ * @defaultValue 0
821
+ */
822
+ readonly priority?: number;
795
823
  }
796
824
  /**
797
- * The typed view over a chat-input command's options for one route. Each getter mirrors the discord.js
798
- * resolver method but restricts its `name` to that route's options of the matching kind, drops the null
799
- * on required options, narrows `choices` to their literal union, and only appears when the route has an
800
- * option of that kind. The rich kinds return exactly what the djs resolver returns.
801
- *
802
- * A channel option declared with `addChannelTypes` narrows `getChannel` to the matching channel subtype.
803
- * Pass the raw `this.event.options` resolver for anything this view does not cover.
804
- *
805
- * @typeParam Route - A route key from the generated {@link SlashRegistry}.
806
- * @typeParam Cache - The interaction cache state. The command's contexts set it.
807
- */
808
- type SlashOptions<Route extends keyof SlashRegistry, Cache extends CacheType = CacheFor<Route>> = SlashOptions$1<Route, GatewayLens<Cache>>;
809
- //#endregion
810
- //#region src/handlers/interaction/SlashHandler.d.ts
811
- type SlashMatchArms<Route extends keyof SlashRegistry, Cache extends CacheType, Ret> = { [Key in Route]: (options: SlashOptions<Key, Cache>) => Promisable<Ret>; };
812
- /**
813
- * Base class for a chat-input (slash) command handler.
814
- *
815
- * Pass the route from the generated registry as the generic, the same string as `@SlashRoute`, then read
816
- * `this.options` for a single command or `this.match` for several. Command authoring stays plain
817
- * discord.js, `seedcord codegen` reads its `toJSON()` to type these options.
825
+ * Registers an event middleware. It runs before the handlers for every event it lists.
818
826
  *
819
- * @typeParam Route - One or more route keys from {@link SlashRegistry}, e.g. `'ban'` or `'ban' | 'kick'`.
820
- * @typeParam Cache - The interaction cache state. The command's contexts set it.
827
+ * @param options - The events this middleware runs on and its ordering.
828
+ * @decorator
821
829
  *
822
830
  * @example
823
831
  * ```ts
824
- * \@SlashRoute('ban')
825
- * class BanHandler extends SlashHandler<'ban'> {
832
+ * \@RegisterEventMiddleware({ events: [Events.MessageCreate], priority: 10 })
833
+ * class Audit extends EventMiddleware<Events.MessageCreate> {
826
834
  * async execute() {
827
- * const target = this.options.getUser('target'); // User
828
- * const reason = this.options.getString('reason'); // string | null
835
+ * this.logger.info(this.event[0].id);
829
836
  * }
830
837
  * }
831
838
  * ```
839
+ *
840
+ * @throws A **SeedcordTypeError** If `priority` is not a finite number.
832
841
  */
833
- declare abstract class SlashHandler<Route extends keyof SlashRegistry, Cache extends CacheType = CacheFor<Route>> extends InteractionHandler<ChatInputCommandInteraction<Cache>> {
834
- /** @internal */
835
- readonly [SlashRouteBrand]?: Route;
836
- /**
837
- * The typed options for this command's route. Only the getters for kinds this command declares appear.
838
- * Each getter's name argument is restricted to that command's options of its kind.
839
- *
840
- * - A required option drops the null.
841
- * - Choices narrow to their literal union.
842
- * - A channel option declared with `addChannelTypes` narrows to those types.
843
- *
844
- * Use `this.event.options` directly for anything outside this view, such as `getSubcommand()`.
845
- * However, for the common case, the methods in this would suffice.
846
- */
847
- protected get options(): SlashOptions<Route, Cache>;
848
- /**
849
- * Run the arm for whichever command fired. Use this only when the handler is registered for several
850
- * routes.
851
- *
852
- * Provide one arm per registered route, keyed by its route string, and each arm receives that route's
853
- * own narrowed options. The arms must cover every route in the generic, a missing route or an unknown
854
- * key is a compile error.
855
- *
856
- * @param arms - One handler per registered route, keyed by route string.
857
- * @returns The result of the arm that ran.
858
- *
859
- * @example
860
- * ```ts
861
- * \@SlashRoute('ban', 'kick')
862
- * class ModHandler extends SlashHandler<'ban' | 'kick'> {
863
- * async execute() {
864
- * await this.match({
865
- * ban: (options) => this.event.guild?.members.ban(options.getUser('target').id),
866
- * kick: (options) => this.event.guild?.members.kick(options.getUser('member').id)
867
- * });
868
- * }
869
- * }
870
- * ```
871
- */
872
- protected match<Ret>(arms: SlashMatchArms<Route, Cache, Ret>): Promise<Ret>;
873
- }
842
+ declare function RegisterEventMiddleware<const Events extends NonEmptyTuple<ValidNonInteractionKeys> = NonEmptyTuple<ValidNonInteractionKeys>>(options?: EventMiddlewareOptions<Events>): (ctor: Constructor<EventMiddleware<Events[number]>>) => void;
874
843
  //#endregion
875
- //#region src/handlers/interaction/ContextMenuHandler.d.ts
876
- type UserMenuArms<Names extends NamesFor<ApplicationCommandType.User>, Ret> = { [Name in Names]: (target: User, member: CacheTypeReducer<MenuCacheFor<ApplicationCommandType.User, Name>, GuildMember, APIInteractionGuildMember> | null) => Promisable<Ret>; };
877
- type MessageMenuArms<Names extends NamesFor<ApplicationCommandType.Message>, Ret> = { [Name in Names]: (target: Message<BooleanCache<MenuCacheFor<ApplicationCommandType.Message, Name>>>) => Promisable<Ret>; };
844
+ //#region src/bot/gates/catalog/IgnoreBots.d.ts
878
845
  /**
879
- * Base class for a user context-menu command handler (right-click a user).
880
- *
881
- * Pass the command name(s) as the generic, the same as `@UserContextMenuRoute`. Read the right-clicked
882
- * user from `this.target` and that same user's guild member from `this.targetMember`.
846
+ * Drops a client event whose actor is a bot, with a {@link Silence} so nothing is replied. Event-only, because a
847
+ * Silence on an interaction would leave Discord's failed-interaction state. It rejects an interaction handler at
848
+ * the decorator line. Takes no options, so attach it directly without calling it.
883
849
  *
884
- * @typeParam Names - One or more command names from the generated user registry.
885
- * @typeParam Cache - The interaction cache state. The command's contexts set it.
850
+ * @see {@link Gated}
886
851
  *
887
852
  * @example
888
853
  * ```ts
889
- * \@UserContextMenuRoute('View Profile')
890
- * class ViewProfile extends UserContextMenuHandler<'View Profile'> {
854
+ * import { Events } from 'discord.js';
855
+ *
856
+ * \@Gated(IgnoreBots)
857
+ * class OnMessage extends EventHandler<Events.MessageCreate> {
891
858
  * async execute() {
892
- * const user = this.target;
859
+ * // ...
893
860
  * }
894
861
  * }
895
862
  * ```
896
863
  */
897
- declare abstract class UserContextMenuHandler<Names extends NamesFor<ApplicationCommandType.User>, Cache extends CacheType = MenuCacheFor<ApplicationCommandType.User, Names>> extends InteractionHandler<UserContextMenuCommandInteraction<Cache>> {
898
- /** @internal */
899
- readonly [ContextMenuKindBrand]?: ApplicationCommandType.User;
900
- /** @internal */
901
- readonly [ContextMenuNamesBrand]?: Names;
902
- protected get target(): User;
903
- /** The right-clicked user's guild member, null outside a guild. */
904
- protected get targetMember(): CacheTypeReducer<Cache, GuildMember, APIInteractionGuildMember> | null;
905
- /** Which of the registered commands fired. */
906
- protected get commandName(): Names;
907
- /**
908
- * Run the arm for whichever command fired. Use this only when the handler is registered for several
909
- * names.
910
- *
911
- * Provide one arm per registered name, keyed by the name Discord shows in the right-click menu. Each
912
- * arm receives the clicked user and that user's guild member, both narrowed to the cache state of that
913
- * one command. The arms must cover every name in the generic. A missing name or an unknown key is a
914
- * compile error.
915
- *
916
- * @param arms - One handler per registered command name.
917
- * @returns The result of the arm that ran.
918
- *
919
- * @example
920
- * ```ts
921
- * \@UserContextMenuRoute('View Profile', 'Warn')
922
- * class Moderation extends UserContextMenuHandler<'View Profile' | 'Warn'> {
923
- * async execute() {
924
- * await this.match({
925
- * 'View Profile': (user) => this.reply(`Profile for ${user.tag}.`),
926
- * Warn: (user, member) => this.reply(`Warned ${member?.displayName ?? user.tag}.`)
927
- * });
928
- * }
929
- * }
930
- * ```
931
- */
932
- protected match<Ret>(arms: UserMenuArms<Names, Ret>): Promise<Ret>;
933
- }
864
+ declare const IgnoreBots: import("@seedcord/core").Gate<EventGateContext<ValidNonInteractionKeys>, "IgnoreBots">;
865
+ //#endregion
866
+ //#region src/bot/gates/catalog/Nsfw.d.ts
934
867
  /**
935
- * Base class for a message context-menu command handler (right-click a message).
868
+ * Requires an age-restricted channel, else refuses. Pass {@link GateNoticeOptions} to reword or replace the refusal.
936
869
  *
937
- * Pass the command name(s) as the generic, the same as `@MessageContextMenuRoute`. Read the right-clicked
938
- * message from `this.target`.
870
+ * A thread inherits its parent channel's nsfw flag. ModalSubmit has no reliable channel, so it is excluded.
939
871
  *
940
- * @typeParam Names - One or more command names from the generated message registry.
941
- * @typeParam Cache - The interaction cache state. The command's contexts set it.
872
+ * @param options - Reword the default refusal with `message`, or replace it with `notice`.
873
+ *
874
+ * @see {@link Gated}
942
875
  *
943
876
  * @example
944
877
  * ```ts
945
- * \@MessageContextMenuRoute('Report Message')
946
- * class ReportMessage extends MessageContextMenuHandler<'Report Message'> {
878
+ * \@Gated(Nsfw())
879
+ * \@SlashRoute('nsfwcmd')
880
+ * class NsfwHandler extends SlashHandler<'nsfwcmd'> {
947
881
  * async execute() {
948
- * const message = this.target;
882
+ * // ...
949
883
  * }
950
884
  * }
951
885
  * ```
952
886
  */
953
- declare abstract class MessageContextMenuHandler<Names extends NamesFor<ApplicationCommandType.Message>, Cache extends CacheType = MenuCacheFor<ApplicationCommandType.Message, Names>> extends InteractionHandler<MessageContextMenuCommandInteraction<Cache>> {
954
- /** @internal */
955
- readonly [ContextMenuKindBrand]?: ApplicationCommandType.Message;
956
- /** @internal */
957
- readonly [ContextMenuNamesBrand]?: Names;
958
- protected get target(): Message<BooleanCache<Cache>>;
959
- /** Which of the registered commands fired. */
960
- protected get commandName(): Names;
961
- /**
962
- * Run the arm for whichever command fired. Use this only when the handler is registered for several
963
- * names.
964
- *
965
- * Provide one arm per registered name, keyed by the name Discord shows in the right-click menu. Each
966
- * arm receives the clicked message, narrowed to the cache state of that one command. The arms must
967
- * cover every name in the generic. A missing name or an unknown key is a compile error.
968
- *
969
- * @param arms - One handler per registered command name.
970
- * @returns The result of the arm that ran.
971
- *
972
- * @example
973
- * ```ts
974
- * \@MessageContextMenuRoute('Report Message', 'Pin Message')
975
- * class MessageTools extends MessageContextMenuHandler<'Report Message' | 'Pin Message'> {
976
- * async execute() {
977
- * await this.match({
978
- * 'Report Message': (message) => this.report(message),
979
- * 'Pin Message': (message) => message.pin()
980
- * });
981
- * }
982
- * }
983
- * ```
984
- */
985
- protected match<Ret>(arms: MessageMenuArms<Names, Ret>): Promise<Ret>;
887
+ declare function Nsfw(options?: GateNoticeOptions): Gate<InteractionGateContext<NonModalInteraction>, 'Nsfw'>;
888
+ //#endregion
889
+ //#region src/bot/injectors/EmojiInjector.d.ts
890
+ type SavedEmojiType = GuildEmoji | ApplicationEmoji;
891
+ /** A resolved emoji, plus the discord.js emoji it came from on {@link GatewayEmoji.source}. */
892
+ declare class GatewayEmoji<Source extends SavedEmojiType = SavedEmojiType> extends ResolvedEmoji {
893
+ #private;
894
+ constructor(source: Source);
895
+ /** What discord.js resolved, for `.url` and the management calls. */
896
+ get source(): Source;
986
897
  }
898
+ /**
899
+ * The global {@link Emojis} accessor type. Each key's `source` resolves to its precise class, `GuildEmoji`
900
+ * for a `'guild'` tag and `ApplicationEmoji` for an `'application'` tag, the tags `seedcord codegen`
901
+ * writes into {@link EmojiMap}.
902
+ */
903
+ type InjectedEmojiMap = { [K in keyof EmojiMap]: EmojiMap[K] extends 'guild' ? GatewayEmoji<GuildEmoji> : GatewayEmoji<ApplicationEmoji>; };
904
+ /**
905
+ * The bot's resolved emojis, keyed by {@link EmojiMap}. Filled by {@link EmojiInjector} during startup.
906
+ * Throws if you read a key the injector did not fill.
907
+ */
908
+ declare const Emojis: InjectedEmojiMap;
987
909
  //#endregion
988
- //#region src/handlers/interaction/AutocompleteHandler.d.ts
989
- type FocusedArms<Route extends keyof SlashRegistry, Ret> = { [Name in AutocompletableNames<Route>]: (value: string, respond: (choices: readonly ApplicationCommandOptionChoiceData<ChoiceValueOf<EntryFor<Route, Name>>>[]) => Promise<void>) => Promisable<Ret>; };
910
+ //#region src/bot/utilities/permissions/checkBotPermissions.d.ts
990
911
  /**
991
- * Base class for a Discord autocomplete handler.
992
- *
993
- * Pass the command route(s) from the generated registry as the generic, the same string(s) as
994
- * `@AutocompleteRoute`. Branch on the focused field with `this.match`, read already-entered sibling options
995
- * with `this.options`, and find which command fired with `this.route`.
996
- *
997
- * @typeParam Route - One or more route keys from {@link SlashRegistry}, e.g. `'search'` or `'search' | 'find'`.
998
- * @typeParam Cache - The interaction cache state. The command's contexts set it.
912
+ * Checks the bot's own permissions in a {@link Guild} or {@link TextChannel}. Refuses when a permission in
913
+ * `scope` is missing, or with `inverse` when one is present. When the bot member is uncached, the refusal
914
+ * reports every permission in `scope` as missing.
999
915
  *
1000
916
  * @example
1001
917
  * ```ts
1002
- * \@AutocompleteRoute('search')
1003
- * class SearchAutocomplete extends AutocompleteHandler<'search'> {
1004
- * async execute() {
1005
- * await this.match({
1006
- * query: (value, respond) => respond([{ name: value, value }])
1007
- * });
1008
- * }
1009
- * }
1010
- * ```
1011
- */
918
+ * import { PermissionFlagsBits } from 'discord.js';
919
+ * import { checkBotPermissions } from '@seedcord/gateway';
920
+ *
921
+ * checkBotPermissions(textChannel, [PermissionFlagsBits.SendMessages, PermissionFlagsBits.ViewChannel]);
922
+ * ```
923
+ */
924
+ declare function checkBotPermissions(target: Guild | TextChannel, scope: PermissionScope, inverse?: boolean, errors?: PermissionNoticeOverrides): void;
925
+ //#endregion
926
+ //#region src/bot/utilities/permissions/checkPermissions.d.ts
927
+ /** Options for {@link checkPermissions}. */
928
+ interface CheckPermissionOptions extends PermissionNoticeOverrides {
929
+ /** The role or member to check. */
930
+ for: Role | GuildMember;
931
+ /** A guild reads the base bits from roles, a text channel reads the overwrite-resolved set. */
932
+ in: Guild | TextChannel;
933
+ /** The permission flag bits to check. */
934
+ scope: PermissionScope;
935
+ /** Refuse when the target holds a scope bit. */
936
+ inverse?: boolean;
937
+ }
938
+ /**
939
+ * Checks a {@link Role} or a {@link GuildMember} against the base permission bits in a {@link Guild},
940
+ * before channel overwrites. Refuses when a permission in `scope` is missing, or with `inverse` when one
941
+ * is present.
942
+ *
943
+ * @example
944
+ * ```ts
945
+ * import { PermissionFlagsBits } from 'discord.js';
946
+ * import { checkPermissions } from '@seedcord/gateway';
947
+ *
948
+ * checkPermissions(role, guild, [PermissionFlagsBits.ManageMessages, PermissionFlagsBits.KickMembers]);
949
+ * ```
950
+ *
951
+ * @example
952
+ * ```ts
953
+ * // refuse when the member holds Administrator, and replace the refusal
954
+ * checkPermissions(member, guild, [PermissionFlagsBits.Administrator], true, {
955
+ * dangerousNotice: CustomDangerousNotice
956
+ * });
957
+ * ```
958
+ */
959
+ declare function checkPermissions(target: Role | GuildMember, ctx: Guild, scope: PermissionScope, inverse?: boolean, errors?: PermissionNoticeOverrides): void;
960
+ /**
961
+ * Checks a {@link Role} or a {@link GuildMember} against the overwrite-resolved set in a
962
+ * {@link TextChannel}. Refuses when a permission in `scope` is missing, or with `inverse` when one is
963
+ * present.
964
+ *
965
+ * @example
966
+ * ```ts
967
+ * import { PermissionFlagsBits } from 'discord.js';
968
+ * import { checkPermissions } from '@seedcord/gateway';
969
+ *
970
+ * checkPermissions(role, textChannel, [PermissionFlagsBits.SendMessages, PermissionFlagsBits.ViewChannel]);
971
+ * ```
972
+ */
973
+ declare function checkPermissions(target: Role | GuildMember, ctx: TextChannel, scope: PermissionScope, inverse?: boolean, errors?: PermissionNoticeOverrides): void;
974
+ /**
975
+ * Checks permissions with an options object. Refuses when a permission in
976
+ * {@link CheckPermissionOptions.scope} is missing, or with `options.inverse` when one is present.
977
+ *
978
+ * @example
979
+ * ```ts
980
+ * import { PermissionFlagsBits } from 'discord.js';
981
+ * import { checkPermissions } from '@seedcord/gateway';
982
+ *
983
+ * checkPermissions({
984
+ * for: member,
985
+ * in: textChannel,
986
+ * scope: [PermissionFlagsBits.SendMessages, PermissionFlagsBits.ViewChannel]
987
+ * });
988
+ * ```
989
+ */
990
+ declare function checkPermissions(options: CheckPermissionOptions): void;
991
+ //#endregion
992
+ //#region src/bot/utilities/permissions/hasPermsToAssign.d.ts
993
+ /**
994
+ * Optional custom error constructors for {@link HasPermsToAssignOptions}.
995
+ */
996
+ interface AssignRoleNoticeOverrides {
997
+ higherNotice?: new (message: string, role: Role, botRole: Role) => Notice;
998
+ managedNotice?: new (message: string) => Notice;
999
+ missingNotice?: new (message: string | undefined, subject: string, missingPerms: readonly string[]) => Notice;
1000
+ }
1001
+ /**
1002
+ * Options for {@link hasPermsToAssign}.
1003
+ */
1004
+ interface HasPermsToAssignOptions {
1005
+ targetRole: Role;
1006
+ noticeOverrides?: AssignRoleNoticeOverrides;
1007
+ }
1008
+ /**
1009
+ * 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.
1010
+ *
1011
+ * @param roleOrOptions - Target role or complete options for the check
1012
+ * @throws A `Notice` on refusal, or a `SeedcordError` when the bot has no role in the guild's role cache.
1013
+ *
1014
+ * @example
1015
+ * ```ts
1016
+ * import { hasPermsToAssign } from '@seedcord/gateway';
1017
+ *
1018
+ * // Check if the bot can assign a specific role
1019
+ * hasPermsToAssign(role);
1020
+ *
1021
+ * // Check with custom error handling
1022
+ * hasPermsToAssign({
1023
+ * targetRole: role,
1024
+ * noticeOverrides: {
1025
+ * higherNotice: CustomHigherNotice,
1026
+ * managedNotice: CustomManagedNotice,
1027
+ * missingNotice: CustomMissingPermissionsNotice
1028
+ * }
1029
+ * });
1030
+ * ```
1031
+ */
1032
+ declare function hasPermsToAssign(roleOrOptions: Role | HasPermsToAssignOptions): void;
1033
+ //#endregion
1034
+ //#region src/bot/confirm/getConfirmation.d.ts
1035
+ type Outcome = ReplyResponse | (() => Promisable<ReplyResponse>);
1036
+ /** Options shared by both confirmation forms. */
1037
+ interface ConfirmOptions {
1038
+ /**
1039
+ * Whether the prompt is ephemeral. Only the invoking user can act on it either way, a non-invoker's
1040
+ * click is ignored.
1041
+ *
1042
+ * @defaultValue `true`
1043
+ */
1044
+ ephemeral?: boolean;
1045
+ /**
1046
+ * Milliseconds to wait for a click before the prompt is settled and the result resolves to `false`.
1047
+ *
1048
+ * @defaultValue `30_000`
1049
+ */
1050
+ timeoutMs?: number;
1051
+ /** Shown in place of the prompt when the user confirms. Absent removes the prompt. */
1052
+ onConfirm?: Outcome;
1053
+ /** Shown in place of the prompt when the user cancels. Absent removes the prompt. */
1054
+ onCancel?: Outcome;
1055
+ /** Shown in place of the prompt when the prompt times out. Absent removes the prompt. */
1056
+ onTimeout?: Outcome;
1057
+ }
1058
+ /** Options for the string-prompt form, adding the built-in Confirm/Cancel buttons' labels and style. */
1059
+ interface DefaultConfirmOptions extends ConfirmOptions {
1060
+ /**
1061
+ * Confirm-button label.
1062
+ *
1063
+ * @defaultValue `'Confirm'`
1064
+ */
1065
+ confirmLabel?: string;
1066
+ /**
1067
+ * Cancel-button label.
1068
+ *
1069
+ * @defaultValue `'Cancel'`
1070
+ */
1071
+ cancelLabel?: string;
1072
+ /**
1073
+ * Confirm-button style.
1074
+ *
1075
+ * @defaultValue {@link ButtonStyle.Danger}
1076
+ */
1077
+ confirmStyle?: ButtonStyle;
1078
+ }
1079
+ /**
1080
+ * Shows a confirm/cancel prompt with built-in Confirm and Cancel buttons and resolves to `true` only if the
1081
+ * invoking user clicks confirm. A cancel click or a timeout resolves to `false`. Gate the action behind it
1082
+ * with an early return. A failed prompt send throws into the fault boundary. A {@link ModalHandler} is
1083
+ * excluded at compile time.
1084
+ *
1085
+ * @param handler - The handler to prompt from, `this` inside a non-modal handler.
1086
+ * @param prompt - The message shown above the Confirm and Cancel buttons.
1087
+ * @param options - Ephemeral flag, timeout, the built-in buttons' labels and style, and the optional
1088
+ * `onConfirm`/`onCancel`/`onTimeout` outcome replies that edit the prompt in place.
1089
+ * @returns `true` if confirmed, `false` on cancel or timeout.
1090
+ *
1091
+ * @example
1092
+ * ```ts
1093
+ * import { getConfirmation } from '@seedcord/gateway';
1094
+ *
1095
+ * \@SlashRoute('ban')
1096
+ * class BanHandler extends SlashHandler<'ban'> {
1097
+ * async execute() {
1098
+ * const target = this.options.getUser('target');
1099
+ * if (!(await getConfirmation(this, `Ban ${target.tag}? This cannot be undone.`))) return;
1100
+ * await this.event.guild?.members.ban(target.id);
1101
+ * }
1102
+ * }
1103
+ * ```
1104
+ */
1105
+ declare function getConfirmation(handler: RepliableHandler<NonModalInteraction>, prompt: string, options?: DefaultConfirmOptions): Promise<boolean>;
1106
+ /**
1107
+ * Shows a confirm/cancel prompt you build yourself and resolves to `true` only if the invoking user clicks
1108
+ * confirm. The factory receives the two minted button ids. Set them on your own buttons. A cancel click or a
1109
+ * timeout resolves to `false`. A failed prompt send throws into the fault boundary. A {@link ModalHandler} is
1110
+ * excluded at compile time.
1111
+ *
1112
+ * @param handler - The handler to prompt from, `this` inside a non-modal handler.
1113
+ * @param prompt - A factory given the minted `{ confirm, cancel }` ids that returns the reply to show. Build
1114
+ * it with the `BuilderComponent` and `RowComponent` wrappers.
1115
+ * @param options - Ephemeral flag, timeout, and the optional `onConfirm`/`onCancel`/`onTimeout` outcome
1116
+ * replies that edit the prompt in place.
1117
+ * @returns `true` if confirmed, `false` on cancel or timeout.
1118
+ *
1119
+ * @remarks
1120
+ * The prompt is collected in-process, so it does not survive a bot restart. Without an outcome hook the
1121
+ * prompt is gone once the user confirms, so deliver the result with a fresh `this.followUp(...)`.
1122
+ *
1123
+ * @example
1124
+ * ```ts
1125
+ * import { ButtonBuilder, TextDisplayBuilder } from '@discordjs/builders';
1126
+ * import { ButtonStyle } from 'discord.js';
1127
+ * import { BuilderComponent, RowComponent, getConfirmation } from '@seedcord/gateway';
1128
+ *
1129
+ * class BanPrompt extends BuilderComponent<'container'> {
1130
+ * constructor(text: string) {
1131
+ * super('container');
1132
+ * this.instance.addTextDisplayComponents(new TextDisplayBuilder().setContent(text));
1133
+ * }
1134
+ * }
1135
+ *
1136
+ * class ConfirmRow extends RowComponent<'button'> {
1137
+ * constructor(ids: { confirm: string; cancel: string }) {
1138
+ * super('button');
1139
+ * const buttons = [
1140
+ * [ids.confirm, 'Ban', ButtonStyle.Danger],
1141
+ * [ids.cancel, 'Cancel', ButtonStyle.Secondary]
1142
+ * ] as const;
1143
+ * this.instance.addComponents(
1144
+ * ...buttons.map(([id, label, style]) => new ButtonBuilder().setCustomId(id).setLabel(label).setStyle(style))
1145
+ * );
1146
+ * }
1147
+ * }
1148
+ *
1149
+ * const target = this.options.getUser('target');
1150
+ * const confirmed = await getConfirmation(
1151
+ * this,
1152
+ * (ids) => ({ components: [new BanPrompt(`Ban ${target.tag}?`).component, new ConfirmRow(ids).component] }),
1153
+ * { onConfirm: { components: [new BanPrompt(`Banned ${target.tag}.`).component] } }
1154
+ * );
1155
+ * if (!confirmed) return;
1156
+ * ```
1157
+ */
1158
+ declare function getConfirmation(handler: RepliableHandler<NonModalInteraction>, prompt: (ids: {
1159
+ confirm: string;
1160
+ cancel: string;
1161
+ }) => Promisable<ReplyResponse>, options?: ConfirmOptions): Promise<boolean>;
1162
+ //#endregion
1163
+ //#region src/handlers/interaction/middlewareKinds.d.ts
1164
+ interface Payloads {
1165
+ [InteractionKind.Slash]: ChatInputCommandInteraction;
1166
+ [InteractionKind.Button]: ButtonInteraction;
1167
+ [InteractionKind.Modal]: ModalSubmitInteraction;
1168
+ [InteractionKind.StringMenu]: StringSelectMenuInteraction;
1169
+ [InteractionKind.UserMenu]: UserSelectMenuInteraction;
1170
+ [InteractionKind.RoleMenu]: RoleSelectMenuInteraction;
1171
+ [InteractionKind.ChannelMenu]: ChannelSelectMenuInteraction;
1172
+ [InteractionKind.MentionableMenu]: MentionableSelectMenuInteraction;
1173
+ [InteractionKind.MessageContextMenu]: MessageContextMenuCommandInteraction;
1174
+ [InteractionKind.UserContextMenu]: UserContextMenuCommandInteraction;
1175
+ }
1176
+ type InteractionForKind = { [Kind in MiddlewareKind]: Payloads[Kind]; };
1177
+ /** @internal */
1178
+ type InteractionOf<Kind extends MiddlewareKind> = InteractionForKind[Kind];
1179
+ //#endregion
1180
+ //#region src/handlers/interaction/InteractionMiddleware.d.ts
1181
+ /**
1182
+ * Base class for interaction middleware.
1183
+ *
1184
+ * Middleware runs after the dispatcher builds the handler and before the gates, over the handler's own
1185
+ * reply surface. Name the kinds in the generic and list the same ones in `{ kinds }`. Several kinds give
1186
+ * `this.event` a union. The discord.js guards narrow it. A catchall reads every repliable kind.
1187
+ *
1188
+ * @typeParam Kind - One or more {@link InteractionKind} members. Defaults to every repliable kind.
1189
+ *
1190
+ * @example
1191
+ * ```ts
1192
+ * \@RegisterInteractionMiddleware({ kinds: [InteractionKind.Button] })
1193
+ * class Audit extends InteractionMiddleware<InteractionKind.Button> {
1194
+ * async execute() {
1195
+ * this.logger.info(this.event.customId);
1196
+ * }
1197
+ * }
1198
+ * ```
1199
+ */
1200
+ declare abstract class InteractionMiddleware<in out Kind extends MiddlewareKind = MiddlewareKind> extends RepliableHandler<InteractionOf<Kind>> {
1201
+ /** @internal */
1202
+ readonly [MiddlewareKindsBrand]?: Kind;
1203
+ constructor(event: InteractionOf<Kind>, core: Core, dispatch: DispatchContext, sender: ReplySender);
1204
+ /**
1205
+ * Runs once the handler settles, newest middleware first. A refusal or a throw still reaches it,
1206
+ * since seedcord calls it on every middleware whose `execute()` started. Implement it to release
1207
+ * something this middleware took in `execute()`, such as a lock or an open span. A throw in here
1208
+ * is logged and goes no further.
1209
+ *
1210
+ * @param result - How the dispatch ended. Either failure state carries the thrown value on `caught`.
1211
+ *
1212
+ * @example
1213
+ * ```ts
1214
+ * // the base declares it. an implementation carries `override`.
1215
+ * override async after(result: DispatchResult) {
1216
+ * this.lock.release();
1217
+ * }
1218
+ * ```
1219
+ */
1220
+ after?(result: DispatchResult): Promise<void>;
1221
+ }
1222
+ //#endregion
1223
+ //#region src/inputs/SlashOptions.d.ts
1224
+ interface GatewayLens<Cache extends CacheType> {
1225
+ user: NonNullable<CommandInteractionOption<Cache>['user']>;
1226
+ member: NonNullable<CommandInteractionOption<Cache>['member']>;
1227
+ channel: NonNullable<CommandInteractionOption<Cache>['channel']>;
1228
+ role: NonNullable<CommandInteractionOption<Cache>['role']>;
1229
+ mentionable: NonNullable<CommandInteractionOption<Cache>['member' | 'role' | 'user']>;
1230
+ attachment: NonNullable<CommandInteractionOption<Cache>['attachment']>;
1231
+ }
1232
+ /**
1233
+ * The typed view over a chat-input command's options for one route. Each getter mirrors the discord.js
1234
+ * resolver method but restricts its `name` to that route's options of the matching kind, drops the null
1235
+ * on required options, narrows `choices` to their literal union, and only appears when the route has an
1236
+ * option of that kind. The rich kinds return exactly what the djs resolver returns.
1237
+ *
1238
+ * A channel option declared with `addChannelTypes` narrows `getChannel` to the matching channel subtype.
1239
+ * Pass the raw `this.event.options` resolver for anything this view does not cover.
1240
+ *
1241
+ * @typeParam Route - A route key from the generated {@link SlashRegistry}.
1242
+ * @typeParam Cache - The interaction cache state. The command's contexts set it.
1243
+ */
1244
+ type SlashOptions<Route extends keyof SlashRegistry, Cache extends CacheType = CacheFor<Route>> = SlashOptions$1<Route, GatewayLens<Cache>>;
1245
+ //#endregion
1246
+ //#region src/handlers/interaction/SlashHandler.d.ts
1247
+ type SlashMatchArms<Route extends keyof SlashRegistry, Cache extends CacheType, Ret> = { [Key in Route]: (options: SlashOptions<Key, Cache>) => Promisable<Ret>; };
1248
+ /**
1249
+ * Base class for a chat-input (slash) command handler.
1250
+ *
1251
+ * Pass the route from the generated registry as the generic, the same string as `@SlashRoute`, then read
1252
+ * `this.options` for a single command or `this.match` for several. Command authoring stays plain
1253
+ * discord.js, `seedcord codegen` reads its `toJSON()` to type these options.
1254
+ *
1255
+ * @typeParam Route - One or more route keys from {@link SlashRegistry}, e.g. `'ban'` or `'ban' | 'kick'`.
1256
+ * @typeParam Cache - The interaction cache state. The command's contexts set it.
1257
+ *
1258
+ * @example
1259
+ * ```ts
1260
+ * \@SlashRoute('ban')
1261
+ * class BanHandler extends SlashHandler<'ban'> {
1262
+ * async execute() {
1263
+ * const target = this.options.getUser('target'); // User
1264
+ * const reason = this.options.getString('reason'); // string | null
1265
+ * }
1266
+ * }
1267
+ * ```
1268
+ */
1269
+ declare abstract class SlashHandler<Route extends keyof SlashRegistry, Cache extends CacheType = CacheFor<Route>> extends InteractionHandler<ChatInputCommandInteraction<Cache>> {
1270
+ /** @internal */
1271
+ readonly [SlashRouteBrand]?: Route;
1272
+ /**
1273
+ * The typed options for this command's route. Only the getters for kinds this command declares appear.
1274
+ * Each getter's name argument is restricted to that command's options of its kind.
1275
+ *
1276
+ * - A required option drops the null.
1277
+ * - Choices narrow to their literal union.
1278
+ * - A channel option declared with `addChannelTypes` narrows to those types.
1279
+ *
1280
+ * Use `this.event.options` directly for anything outside this view, such as `getSubcommand()`.
1281
+ * However, for the common case, the methods in this would suffice.
1282
+ */
1283
+ protected get options(): SlashOptions<Route, Cache>;
1284
+ /**
1285
+ * Run the arm for whichever command fired. Use this only when the handler is registered for several
1286
+ * routes.
1287
+ *
1288
+ * Provide one arm per registered route, keyed by its route string, and each arm receives that route's
1289
+ * own narrowed options. The arms must cover every route in the generic, a missing route or an unknown
1290
+ * key is a compile error.
1291
+ *
1292
+ * @param arms - One handler per registered route, keyed by route string.
1293
+ * @returns The result of the arm that ran.
1294
+ *
1295
+ * @example
1296
+ * ```ts
1297
+ * \@SlashRoute('ban', 'kick')
1298
+ * class ModHandler extends SlashHandler<'ban' | 'kick'> {
1299
+ * async execute() {
1300
+ * await this.match({
1301
+ * ban: (options) => this.event.guild?.members.ban(options.getUser('target').id),
1302
+ * kick: (options) => this.event.guild?.members.kick(options.getUser('member').id)
1303
+ * });
1304
+ * }
1305
+ * }
1306
+ * ```
1307
+ */
1308
+ protected match<Ret>(arms: SlashMatchArms<Route, Cache, Ret>): Promise<Ret>;
1309
+ }
1310
+ //#endregion
1311
+ //#region src/handlers/interaction/ContextMenuHandler.d.ts
1312
+ type UserMenuArms<Names extends NamesFor<ApplicationCommandType.User>, Ret> = { [Name in Names]: (target: User, member: CacheTypeReducer<MenuCacheFor<ApplicationCommandType.User, Name>, GuildMember, APIInteractionGuildMember> | null) => Promisable<Ret>; };
1313
+ type MessageMenuArms<Names extends NamesFor<ApplicationCommandType.Message>, Ret> = { [Name in Names]: (target: Message<BooleanCache<MenuCacheFor<ApplicationCommandType.Message, Name>>>) => Promisable<Ret>; };
1314
+ /**
1315
+ * Base class for a user context-menu command handler (right-click a user).
1316
+ *
1317
+ * Pass the command name(s) as the generic, the same as `@UserContextMenuRoute`. Read the right-clicked
1318
+ * user from `this.target` and that same user's guild member from `this.targetMember`.
1319
+ *
1320
+ * @typeParam Names - One or more command names from the generated user registry.
1321
+ * @typeParam Cache - The interaction cache state. The command's contexts set it.
1322
+ *
1323
+ * @example
1324
+ * ```ts
1325
+ * \@UserContextMenuRoute('View Profile')
1326
+ * class ViewProfile extends UserContextMenuHandler<'View Profile'> {
1327
+ * async execute() {
1328
+ * const user = this.target;
1329
+ * }
1330
+ * }
1331
+ * ```
1332
+ */
1333
+ declare abstract class UserContextMenuHandler<Names extends NamesFor<ApplicationCommandType.User>, Cache extends CacheType = MenuCacheFor<ApplicationCommandType.User, Names>> extends InteractionHandler<UserContextMenuCommandInteraction<Cache>> {
1334
+ /** @internal */
1335
+ readonly [ContextMenuKindBrand]?: ApplicationCommandType.User;
1336
+ /** @internal */
1337
+ readonly [ContextMenuNamesBrand]?: Names;
1338
+ protected get target(): User;
1339
+ /** The right-clicked user's guild member, null outside a guild. */
1340
+ protected get targetMember(): CacheTypeReducer<Cache, GuildMember, APIInteractionGuildMember> | null;
1341
+ /** Which of the registered commands fired. */
1342
+ protected get commandName(): Names;
1343
+ /**
1344
+ * Run the arm for whichever command fired. Use this only when the handler is registered for several
1345
+ * names.
1346
+ *
1347
+ * Provide one arm per registered name, keyed by the name Discord shows in the right-click menu. Each
1348
+ * arm receives the clicked user and that user's guild member, both narrowed to the cache state of that
1349
+ * one command. The arms must cover every name in the generic. A missing name or an unknown key is a
1350
+ * compile error.
1351
+ *
1352
+ * @param arms - One handler per registered command name.
1353
+ * @returns The result of the arm that ran.
1354
+ *
1355
+ * @example
1356
+ * ```ts
1357
+ * \@UserContextMenuRoute('View Profile', 'Warn')
1358
+ * class Moderation extends UserContextMenuHandler<'View Profile' | 'Warn'> {
1359
+ * async execute() {
1360
+ * await this.match({
1361
+ * 'View Profile': (user) => this.reply(`Profile for ${user.tag}.`),
1362
+ * Warn: (user, member) => this.reply(`Warned ${member?.displayName ?? user.tag}.`)
1363
+ * });
1364
+ * }
1365
+ * }
1366
+ * ```
1367
+ */
1368
+ protected match<Ret>(arms: UserMenuArms<Names, Ret>): Promise<Ret>;
1369
+ }
1370
+ /**
1371
+ * Base class for a message context-menu command handler (right-click a message).
1372
+ *
1373
+ * Pass the command name(s) as the generic, the same as `@MessageContextMenuRoute`. Read the right-clicked
1374
+ * message from `this.target`.
1375
+ *
1376
+ * @typeParam Names - One or more command names from the generated message registry.
1377
+ * @typeParam Cache - The interaction cache state. The command's contexts set it.
1378
+ *
1379
+ * @example
1380
+ * ```ts
1381
+ * \@MessageContextMenuRoute('Report Message')
1382
+ * class ReportMessage extends MessageContextMenuHandler<'Report Message'> {
1383
+ * async execute() {
1384
+ * const message = this.target;
1385
+ * }
1386
+ * }
1387
+ * ```
1388
+ */
1389
+ declare abstract class MessageContextMenuHandler<Names extends NamesFor<ApplicationCommandType.Message>, Cache extends CacheType = MenuCacheFor<ApplicationCommandType.Message, Names>> extends InteractionHandler<MessageContextMenuCommandInteraction<Cache>> {
1390
+ /** @internal */
1391
+ readonly [ContextMenuKindBrand]?: ApplicationCommandType.Message;
1392
+ /** @internal */
1393
+ readonly [ContextMenuNamesBrand]?: Names;
1394
+ protected get target(): Message<BooleanCache<Cache>>;
1395
+ /** Which of the registered commands fired. */
1396
+ protected get commandName(): Names;
1397
+ /**
1398
+ * Run the arm for whichever command fired. Use this only when the handler is registered for several
1399
+ * names.
1400
+ *
1401
+ * Provide one arm per registered name, keyed by the name Discord shows in the right-click menu. Each
1402
+ * arm receives the clicked message, narrowed to the cache state of that one command. The arms must
1403
+ * cover every name in the generic. A missing name or an unknown key is a compile error.
1404
+ *
1405
+ * @param arms - One handler per registered command name.
1406
+ * @returns The result of the arm that ran.
1407
+ *
1408
+ * @example
1409
+ * ```ts
1410
+ * \@MessageContextMenuRoute('Report Message', 'Pin Message')
1411
+ * class MessageTools extends MessageContextMenuHandler<'Report Message' | 'Pin Message'> {
1412
+ * async execute() {
1413
+ * await this.match({
1414
+ * 'Report Message': (message) => this.report(message),
1415
+ * 'Pin Message': (message) => message.pin()
1416
+ * });
1417
+ * }
1418
+ * }
1419
+ * ```
1420
+ */
1421
+ protected match<Ret>(arms: MessageMenuArms<Names, Ret>): Promise<Ret>;
1422
+ }
1423
+ //#endregion
1424
+ //#region src/handlers/interaction/AutocompleteHandler.d.ts
1425
+ type FocusedArms<Route extends keyof SlashRegistry, Ret> = { [Name in AutocompletableNames<Route>]: (value: string, respond: (choices: readonly ApplicationCommandOptionChoiceData<ChoiceValueOf<EntryFor<Route, Name>>>[]) => Promise<void>) => Promisable<Ret>; };
1426
+ /**
1427
+ * Base class for a Discord autocomplete handler.
1428
+ *
1429
+ * Pass the command route(s) from the generated registry as the generic, the same string(s) as
1430
+ * `@AutocompleteRoute`. Branch on the focused field with `this.match`, read already-entered sibling options
1431
+ * with `this.options`, and find which command fired with `this.route`.
1432
+ *
1433
+ * @typeParam Route - One or more route keys from {@link SlashRegistry}, e.g. `'search'` or `'search' | 'find'`.
1434
+ * @typeParam Cache - The interaction cache state. The command's contexts set it.
1435
+ *
1436
+ * @example
1437
+ * ```ts
1438
+ * \@AutocompleteRoute('search')
1439
+ * class SearchAutocomplete extends AutocompleteHandler<'search'> {
1440
+ * async execute() {
1441
+ * await this.match({
1442
+ * query: (value, respond) => respond([{ name: value, value }])
1443
+ * });
1444
+ * }
1445
+ * }
1446
+ * ```
1447
+ */
1012
1448
  declare abstract class AutocompleteHandler<Route extends keyof SlashRegistry, Cache extends CacheType = CacheFor<Route>> extends BaseHandler<AutocompleteInteraction<Cache>> {
1013
1449
  /** @internal */
1014
1450
  readonly [AutocompleteRouteBrand]?: Route;
1015
- constructor(event: AutocompleteInteraction<Cache>, core: Core, dispatch?: DispatchContext);
1451
+ constructor(event: AutocompleteInteraction<Cache>, core: Core, dispatch: DispatchContext);
1016
1452
  private decodedFocused?;
1017
1453
  /**
1018
1454
  * The focused option for this interaction. `value` is always the raw partial string the user is typing,
@@ -1199,493 +1635,133 @@ declare abstract class SelectMenuHandler<Event extends AnySelectMenuInteraction,
1199
1635
  *
1200
1636
  * Register the customId definitions with `@StringMenuRoute`, list the same ones in the generic, then read
1201
1637
  * the chosen option values from `this.values`. Read `this.params` for a single route or `this.match` for
1202
- * several. Reply through the handler members, or rewrite the source message with `this.update`.
1203
- *
1204
- * @typeParam Defs - The customId definitions this handler decodes, e.g. `[typeof TopicsId]`.
1205
- * @typeParam Cache - The interaction cache state, `'cached'` by default.
1206
- *
1207
- * @example
1208
- * ```ts
1209
- * \@StringMenuRoute(TopicsId)
1210
- * class Topics extends StringMenuHandler<[typeof TopicsId]> {
1211
- * async execute() {
1212
- * const { userId } = this.params;
1213
- * await this.update(`<@${userId}> now follows ${this.values.join(', ')}`);
1214
- * }
1215
- * }
1216
- * ```
1217
- */
1218
- declare abstract class StringMenuHandler<Defs extends readonly AnyCustomId[], Cache extends CacheType = 'cached'> extends SelectMenuHandler<StringSelectMenuInteraction<Cache>, Defs> {
1219
- /** @internal */
1220
- readonly [ComponentKindBrand]?: InteractionKind.StringMenu;
1221
- }
1222
- /**
1223
- * Base class for a user select menu handler.
1224
- *
1225
- * Beside `this.values` this handler declares `this.users` and `this.members`. Register the customId
1226
- * definitions with `@UserMenuRoute` and list the same ones in the generic.
1227
- *
1228
- * @typeParam Defs - The customId definitions this handler decodes, e.g. `[typeof AssignId]`.
1229
- * @typeParam Cache - The interaction cache state, `'cached'` by default.
1230
- *
1231
- * @example
1232
- * ```ts
1233
- * \@UserMenuRoute(AssignId)
1234
- * class Assign extends UserMenuHandler<[typeof AssignId]> {
1235
- * async execute() {
1236
- * const { roleId } = this.params;
1237
- * await this.reply(`assigning ${this.users.size} member(s) to <@&${roleId}>`);
1238
- * }
1239
- * }
1240
- * ```
1241
- */
1242
- declare abstract class UserMenuHandler<Defs extends readonly AnyCustomId[], Cache extends CacheType = 'cached'> extends SelectMenuHandler<UserSelectMenuInteraction<Cache>, Defs> {
1243
- /** @internal */
1244
- readonly [ComponentKindBrand]?: InteractionKind.UserMenu;
1245
- /** The users this select picked. */
1246
- protected get users(): UserSelectMenuInteraction<Cache>['users'];
1247
- /** The guild members behind the picked users. Discord resolves these only inside a guild. */
1248
- protected get members(): UserSelectMenuInteraction<Cache>['members'];
1249
- }
1250
- /**
1251
- * Base class for a role select menu handler.
1252
- *
1253
- * Beside `this.values` this handler declares `this.roles`. Register the customId definitions with
1254
- * `@RoleMenuRoute` and list the same ones in the generic.
1255
- *
1256
- * @typeParam Defs - The customId definitions this handler decodes, e.g. `[typeof GrantId]`.
1257
- * @typeParam Cache - The interaction cache state, `'cached'` by default.
1258
- *
1259
- * @example
1260
- * ```ts
1261
- * \@RoleMenuRoute(GrantId)
1262
- * class Grant extends RoleMenuHandler<[typeof GrantId]> {
1263
- * async execute() {
1264
- * await this.update(`granting ${this.roles.size} role(s)`);
1265
- * }
1266
- * }
1267
- * ```
1268
- */
1269
- declare abstract class RoleMenuHandler<Defs extends readonly AnyCustomId[], Cache extends CacheType = 'cached'> extends SelectMenuHandler<RoleSelectMenuInteraction<Cache>, Defs> {
1270
- /** @internal */
1271
- readonly [ComponentKindBrand]?: InteractionKind.RoleMenu;
1272
- /** The roles this select picked. */
1273
- protected get roles(): RoleSelectMenuInteraction<Cache>['roles'];
1274
- }
1275
- /**
1276
- * Base class for a channel select menu handler.
1277
- *
1278
- * Beside `this.values` this handler declares `this.channels`. Register the customId definitions with
1279
- * `@ChannelMenuRoute` and list the same ones in the generic.
1280
- *
1281
- * @typeParam Defs - The customId definitions this handler decodes, e.g. `[typeof LogTargetId]`.
1282
- * @typeParam Cache - The interaction cache state, `'cached'` by default.
1283
- *
1284
- * @example
1285
- * ```ts
1286
- * \@ChannelMenuRoute(LogTargetId)
1287
- * class LogTarget extends ChannelMenuHandler<[typeof LogTargetId]> {
1288
- * async execute() {
1289
- * await this.update(`logging to ${this.channels.size} channel(s)`);
1290
- * }
1291
- * }
1292
- * ```
1293
- */
1294
- declare abstract class ChannelMenuHandler<Defs extends readonly AnyCustomId[], Cache extends CacheType = 'cached'> extends SelectMenuHandler<ChannelSelectMenuInteraction<Cache>, Defs> {
1295
- /** @internal */
1296
- readonly [ComponentKindBrand]?: InteractionKind.ChannelMenu;
1297
- /** The channels this select picked. */
1298
- protected get channels(): ChannelSelectMenuInteraction<Cache>['channels'];
1299
- }
1300
- /**
1301
- * Base class for a mentionable select menu handler.
1302
- *
1303
- * A mentionable menu accepts users and roles together. `this.users`, `this.members`, and `this.roles`
1304
- * can each come back empty.
1305
- *
1306
- * @typeParam Defs - The customId definitions this handler decodes, e.g. `[typeof InviteId]`.
1307
- * @typeParam Cache - The interaction cache state, `'cached'` by default.
1308
- *
1309
- * @example
1310
- * ```ts
1311
- * \@MentionableMenuRoute(InviteId)
1312
- * class Invite extends MentionableMenuHandler<[typeof InviteId]> {
1313
- * async execute() {
1314
- * await this.update(`inviting ${this.users.size} user(s) and ${this.roles.size} role(s)`);
1315
- * }
1316
- * }
1317
- * ```
1318
- */
1319
- declare abstract class MentionableMenuHandler<Defs extends readonly AnyCustomId[], Cache extends CacheType = 'cached'> extends SelectMenuHandler<MentionableSelectMenuInteraction<Cache>, Defs> {
1320
- /** @internal */
1321
- readonly [ComponentKindBrand]?: InteractionKind.MentionableMenu;
1322
- /** The users this select picked. */
1323
- protected get users(): MentionableSelectMenuInteraction<Cache>['users'];
1324
- /** The guild members behind the picked users. Discord resolves these only inside a guild. */
1325
- protected get members(): MentionableSelectMenuInteraction<Cache>['members'];
1326
- /** The roles this select picked. */
1327
- protected get roles(): MentionableSelectMenuInteraction<Cache>['roles'];
1328
- }
1329
- //#endregion
1330
- //#region src/bot/decorators/Middlewares.d.ts
1331
- /**
1332
- * Middleware types supported by Seedcord
1333
- */
1334
- declare enum MiddlewareType {
1335
- Interaction = "interaction",
1336
- Event = "event"
1337
- }
1338
- /**
1339
- * Additional middleware registration options
1340
- *
1341
- * @typeParam MType - The type of middleware being registered
1342
- */
1343
- interface MiddlewareOptions<MType extends MiddlewareType, Events extends readonly ValidNonInteractionKeys[] = readonly ValidNonInteractionKeys[]> {
1344
- /**
1345
- * Restrict event middleware execution to specific Discord client events. The middleware's `EventMiddleware`
1346
- * generic must list the same events, or applying the decorator is a compile error.
1347
- */
1348
- readonly events?: MType extends MiddlewareType.Event ? Events : never;
1349
- }
1350
- /**
1351
- * Decorator used to register middleware with priority ordering. The lower the priority number, the earlier it runs.
1352
- *
1353
- * Interaction middleware cannot specify event filters.
1354
- *
1355
- * @param type - Middleware kind from {@link MiddlewareType}
1356
- * @param priority - Ordering value where lower runs earlier. {@default `0` }
1357
- * @param options - Additional registration options
1358
- *
1359
- * @decorator
1360
- *
1361
- * @example
1362
- * ```ts
1363
- * \@Middleware(MiddlewareType.Event, 10, { events: [Events.MessageCreate, Events.MessageUpdate] })
1364
- * class MyEventMiddleware extends EventMiddleware {}
1365
- * ```
1366
- * @throws A **SeedcordTypeError** If priority is not a finite number
1367
- * @throws A **SeedcordError** If interaction middleware specifies event filters
1368
- */
1369
- declare function Middleware<MType extends MiddlewareType, const Events extends readonly ValidNonInteractionKeys[] = []>(type: MType, priority?: number, options?: MiddlewareOptions<MType, Events>): (ctor: MType extends MiddlewareType.Interaction ? Constructor<InteractionMiddleware<Repliables>> : Events extends readonly [] ? Constructor<EventMiddleware<ValidNonInteractionKeys>> : Constructor<EventMiddleware<Events[number]>>) => void;
1370
- //#endregion
1371
- //#region src/bot/gates/catalog/IgnoreBots.d.ts
1372
- /**
1373
- * Drops a client event whose actor is a bot, with a {@link Silence} so nothing is replied. Event-only, because a
1374
- * Silence on an interaction would leave Discord's failed-interaction state. It rejects an interaction handler at
1375
- * the decorator line. Takes no options, so attach it directly without calling it.
1376
- *
1377
- * @see {@link Gated}
1378
- *
1379
- * @example
1380
- * ```ts
1381
- * import { Events } from 'discord.js';
1382
- *
1383
- * \@Gated(IgnoreBots)
1384
- * class OnMessage extends EventHandler<Events.MessageCreate> {
1385
- * async execute() {
1386
- * // ...
1387
- * }
1388
- * }
1389
- * ```
1390
- */
1391
- declare const IgnoreBots: import("@seedcord/core").Gate<EventGateContext<ValidNonInteractionKeys>, "IgnoreBots">;
1392
- //#endregion
1393
- //#region src/bot/gates/catalog/Nsfw.d.ts
1394
- /**
1395
- * Requires an age-restricted channel, else refuses. Pass {@link GateNoticeOptions} to reword or replace the refusal.
1396
- *
1397
- * A thread inherits its parent channel's nsfw flag. ModalSubmit has no reliable channel, so it is excluded.
1398
- *
1399
- * @param options - Reword the default refusal with `message`, or replace it with `notice`.
1400
- *
1401
- * @see {@link Gated}
1402
- *
1403
- * @example
1404
- * ```ts
1405
- * \@Gated(Nsfw())
1406
- * \@SlashRoute('nsfwcmd')
1407
- * class NsfwHandler extends SlashHandler<'nsfwcmd'> {
1408
- * async execute() {
1409
- * // ...
1410
- * }
1411
- * }
1412
- * ```
1413
- */
1414
- declare function Nsfw(options?: GateNoticeOptions): Gate<InteractionGateContext<NonModalInteraction>, 'Nsfw'>;
1415
- //#endregion
1416
- //#region src/bot/injectors/EmojiInjector.d.ts
1417
- type SavedEmojiType = GuildEmoji | ApplicationEmoji;
1418
- /** A resolved emoji, plus the discord.js emoji it came from on {@link GatewayEmoji.source}. */
1419
- declare class GatewayEmoji<Source extends SavedEmojiType = SavedEmojiType> extends ResolvedEmoji {
1420
- #private;
1421
- constructor(source: Source);
1422
- /** What discord.js resolved, for `.url` and the management calls. */
1423
- get source(): Source;
1424
- }
1425
- /**
1426
- * The global {@link Emojis} accessor type. Each key's `source` resolves to its precise class, `GuildEmoji`
1427
- * for a `'guild'` tag and `ApplicationEmoji` for an `'application'` tag, the tags `seedcord codegen`
1428
- * writes into {@link EmojiMap}.
1429
- */
1430
- type InjectedEmojiMap = { [K in keyof EmojiMap]: EmojiMap[K] extends 'guild' ? GatewayEmoji<GuildEmoji> : GatewayEmoji<ApplicationEmoji>; };
1431
- /**
1432
- * The bot's resolved emojis, keyed by {@link EmojiMap}. Filled by {@link EmojiInjector} during startup.
1433
- * Throws if you read a key the injector did not fill.
1434
- */
1435
- declare const Emojis: InjectedEmojiMap;
1436
- //#endregion
1437
- //#region src/bot/utilities/permissions/checkBotPermissions.d.ts
1438
- /**
1439
- * Checks the bot's own permissions in a {@link Guild} or {@link TextChannel}. Refuses when a permission in
1440
- * `scope` is missing, or with `inverse` when one is present. When the bot member is uncached, the refusal
1441
- * reports every permission in `scope` as missing.
1442
- *
1443
- * @example
1444
- * ```ts
1445
- * import { PermissionFlagsBits } from 'discord.js';
1446
- * import { checkBotPermissions } from '@seedcord/gateway';
1447
- *
1448
- * checkBotPermissions(textChannel, [PermissionFlagsBits.SendMessages, PermissionFlagsBits.ViewChannel]);
1449
- * ```
1450
- */
1451
- declare function checkBotPermissions(target: Guild | TextChannel, scope: PermissionScope, inverse?: boolean, errors?: PermissionNoticeOverrides): void;
1452
- //#endregion
1453
- //#region src/bot/utilities/permissions/checkPermissions.d.ts
1454
- /** Options for {@link checkPermissions}. */
1455
- interface CheckPermissionOptions extends PermissionNoticeOverrides {
1456
- /** The role or member to check. */
1457
- for: Role | GuildMember;
1458
- /** A guild reads the base bits from roles, a text channel reads the overwrite-resolved set. */
1459
- in: Guild | TextChannel;
1460
- /** The permission flag bits to check. */
1461
- scope: PermissionScope;
1462
- /** Refuse when the target holds a scope bit. */
1463
- inverse?: boolean;
1464
- }
1465
- /**
1466
- * Checks a {@link Role} or a {@link GuildMember} against the base permission bits in a {@link Guild},
1467
- * before channel overwrites. Refuses when a permission in `scope` is missing, or with `inverse` when one
1468
- * is present.
1469
- *
1470
- * @example
1471
- * ```ts
1472
- * import { PermissionFlagsBits } from 'discord.js';
1473
- * import { checkPermissions } from '@seedcord/gateway';
1638
+ * several. Reply through the handler members, or rewrite the source message with `this.update`.
1474
1639
  *
1475
- * checkPermissions(role, guild, [PermissionFlagsBits.ManageMessages, PermissionFlagsBits.KickMembers]);
1476
- * ```
1640
+ * @typeParam Defs - The customId definitions this handler decodes, e.g. `[typeof TopicsId]`.
1641
+ * @typeParam Cache - The interaction cache state, `'cached'` by default.
1477
1642
  *
1478
1643
  * @example
1479
1644
  * ```ts
1480
- * // refuse when the member holds Administrator, and replace the refusal
1481
- * checkPermissions(member, guild, [PermissionFlagsBits.Administrator], true, {
1482
- * dangerousNotice: CustomDangerousNotice
1483
- * });
1645
+ * \@StringMenuRoute(TopicsId)
1646
+ * class Topics extends StringMenuHandler<[typeof TopicsId]> {
1647
+ * async execute() {
1648
+ * const { userId } = this.params;
1649
+ * await this.update(`<@${userId}> now follows ${this.values.join(', ')}`);
1650
+ * }
1651
+ * }
1484
1652
  * ```
1485
1653
  */
1486
- declare function checkPermissions(target: Role | GuildMember, ctx: Guild, scope: PermissionScope, inverse?: boolean, errors?: PermissionNoticeOverrides): void;
1654
+ declare abstract class StringMenuHandler<Defs extends readonly AnyCustomId[], Cache extends CacheType = 'cached'> extends SelectMenuHandler<StringSelectMenuInteraction<Cache>, Defs> {
1655
+ /** @internal */
1656
+ readonly [ComponentKindBrand]?: InteractionKind.StringMenu;
1657
+ }
1487
1658
  /**
1488
- * Checks a {@link Role} or a {@link GuildMember} against the overwrite-resolved set in a
1489
- * {@link TextChannel}. Refuses when a permission in `scope` is missing, or with `inverse` when one is
1490
- * present.
1659
+ * Base class for a user select menu handler.
1491
1660
  *
1492
- * @example
1493
- * ```ts
1494
- * import { PermissionFlagsBits } from 'discord.js';
1495
- * import { checkPermissions } from '@seedcord/gateway';
1661
+ * Beside `this.values` this handler declares `this.users` and `this.members`. Register the customId
1662
+ * definitions with `@UserMenuRoute` and list the same ones in the generic.
1496
1663
  *
1497
- * checkPermissions(role, textChannel, [PermissionFlagsBits.SendMessages, PermissionFlagsBits.ViewChannel]);
1498
- * ```
1499
- */
1500
- declare function checkPermissions(target: Role | GuildMember, ctx: TextChannel, scope: PermissionScope, inverse?: boolean, errors?: PermissionNoticeOverrides): void;
1501
- /**
1502
- * Checks permissions with an options object. Refuses when a permission in
1503
- * {@link CheckPermissionOptions.scope} is missing, or with `options.inverse` when one is present.
1664
+ * @typeParam Defs - The customId definitions this handler decodes, e.g. `[typeof AssignId]`.
1665
+ * @typeParam Cache - The interaction cache state, `'cached'` by default.
1504
1666
  *
1505
1667
  * @example
1506
1668
  * ```ts
1507
- * import { PermissionFlagsBits } from 'discord.js';
1508
- * import { checkPermissions } from '@seedcord/gateway';
1509
- *
1510
- * checkPermissions({
1511
- * for: member,
1512
- * in: textChannel,
1513
- * scope: [PermissionFlagsBits.SendMessages, PermissionFlagsBits.ViewChannel]
1514
- * });
1669
+ * \@UserMenuRoute(AssignId)
1670
+ * class Assign extends UserMenuHandler<[typeof AssignId]> {
1671
+ * async execute() {
1672
+ * const { roleId } = this.params;
1673
+ * await this.reply(`assigning ${this.users.size} member(s) to <@&${roleId}>`);
1674
+ * }
1675
+ * }
1515
1676
  * ```
1516
1677
  */
1517
- declare function checkPermissions(options: CheckPermissionOptions): void;
1518
- //#endregion
1519
- //#region src/bot/utilities/permissions/hasPermsToAssign.d.ts
1520
- /**
1521
- * Optional custom error constructors for {@link HasPermsToAssignOptions}.
1522
- */
1523
- interface AssignRoleNoticeOverrides {
1524
- higherNotice?: new (message: string, role: Role, botRole: Role) => Notice;
1525
- managedNotice?: new (message: string) => Notice;
1526
- missingNotice?: new (message: string | undefined, subject: string, missingPerms: readonly string[]) => Notice;
1527
- }
1528
- /**
1529
- * Options for {@link hasPermsToAssign}.
1530
- */
1531
- interface HasPermsToAssignOptions {
1532
- targetRole: Role;
1533
- noticeOverrides?: AssignRoleNoticeOverrides;
1678
+ declare abstract class UserMenuHandler<Defs extends readonly AnyCustomId[], Cache extends CacheType = 'cached'> extends SelectMenuHandler<UserSelectMenuInteraction<Cache>, Defs> {
1679
+ /** @internal */
1680
+ readonly [ComponentKindBrand]?: InteractionKind.UserMenu;
1681
+ /** The users this select picked. */
1682
+ protected get users(): UserSelectMenuInteraction<Cache>['users'];
1683
+ /** The guild members behind the picked users. Discord resolves these only inside a guild. */
1684
+ protected get members(): UserSelectMenuInteraction<Cache>['members'];
1534
1685
  }
1535
1686
  /**
1536
- * 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.
1687
+ * Base class for a role select menu handler.
1537
1688
  *
1538
- * @param roleOrOptions - Target role or complete options for the check
1539
- * @throws A `Notice` on refusal, or a `SeedcordError` when the bot has no role in the guild's role cache.
1689
+ * Beside `this.values` this handler declares `this.roles`. Register the customId definitions with
1690
+ * `@RoleMenuRoute` and list the same ones in the generic.
1691
+ *
1692
+ * @typeParam Defs - The customId definitions this handler decodes, e.g. `[typeof GrantId]`.
1693
+ * @typeParam Cache - The interaction cache state, `'cached'` by default.
1540
1694
  *
1541
1695
  * @example
1542
1696
  * ```ts
1543
- * import { hasPermsToAssign } from '@seedcord/gateway';
1544
- *
1545
- * // Check if the bot can assign a specific role
1546
- * hasPermsToAssign(role);
1547
- *
1548
- * // Check with custom error handling
1549
- * hasPermsToAssign({
1550
- * targetRole: role,
1551
- * noticeOverrides: {
1552
- * higherNotice: CustomHigherNotice,
1553
- * managedNotice: CustomManagedNotice,
1554
- * missingNotice: CustomMissingPermissionsNotice
1555
- * }
1556
- * });
1697
+ * \@RoleMenuRoute(GrantId)
1698
+ * class Grant extends RoleMenuHandler<[typeof GrantId]> {
1699
+ * async execute() {
1700
+ * await this.update(`granting ${this.roles.size} role(s)`);
1701
+ * }
1702
+ * }
1557
1703
  * ```
1558
1704
  */
1559
- declare function hasPermsToAssign(roleOrOptions: Role | HasPermsToAssignOptions): void;
1560
- //#endregion
1561
- //#region src/bot/confirm/getConfirmation.d.ts
1562
- type Outcome = ReplyResponse | (() => Promisable<ReplyResponse>);
1563
- /** Options shared by both confirmation forms. */
1564
- interface ConfirmOptions {
1565
- /**
1566
- * Whether the prompt is ephemeral. Only the invoking user can act on it either way, a non-invoker's
1567
- * click is ignored.
1568
- *
1569
- * @defaultValue `true`
1570
- */
1571
- ephemeral?: boolean;
1572
- /**
1573
- * Milliseconds to wait for a click before the prompt is settled and the result resolves to `false`.
1574
- *
1575
- * @defaultValue `30_000`
1576
- */
1577
- timeoutMs?: number;
1578
- /** Shown in place of the prompt when the user confirms. Absent removes the prompt. */
1579
- onConfirm?: Outcome;
1580
- /** Shown in place of the prompt when the user cancels. Absent removes the prompt. */
1581
- onCancel?: Outcome;
1582
- /** Shown in place of the prompt when the prompt times out. Absent removes the prompt. */
1583
- onTimeout?: Outcome;
1584
- }
1585
- /** Options for the string-prompt form, adding the built-in Confirm/Cancel buttons' labels and style. */
1586
- interface DefaultConfirmOptions extends ConfirmOptions {
1587
- /**
1588
- * Confirm-button label.
1589
- *
1590
- * @defaultValue `'Confirm'`
1591
- */
1592
- confirmLabel?: string;
1593
- /**
1594
- * Cancel-button label.
1595
- *
1596
- * @defaultValue `'Cancel'`
1597
- */
1598
- cancelLabel?: string;
1599
- /**
1600
- * Confirm-button style.
1601
- *
1602
- * @defaultValue {@link ButtonStyle.Danger}
1603
- */
1604
- confirmStyle?: ButtonStyle;
1705
+ declare abstract class RoleMenuHandler<Defs extends readonly AnyCustomId[], Cache extends CacheType = 'cached'> extends SelectMenuHandler<RoleSelectMenuInteraction<Cache>, Defs> {
1706
+ /** @internal */
1707
+ readonly [ComponentKindBrand]?: InteractionKind.RoleMenu;
1708
+ /** The roles this select picked. */
1709
+ protected get roles(): RoleSelectMenuInteraction<Cache>['roles'];
1605
1710
  }
1606
1711
  /**
1607
- * Shows a confirm/cancel prompt with built-in Confirm and Cancel buttons and resolves to `true` only if the
1608
- * invoking user clicks confirm. A cancel click or a timeout resolves to `false`. Gate the action behind it
1609
- * with an early return. A failed prompt send throws into the fault boundary. A {@link ModalHandler} is
1610
- * excluded at compile time.
1712
+ * Base class for a channel select menu handler.
1611
1713
  *
1612
- * @param handler - The handler to prompt from, `this` inside a non-modal handler.
1613
- * @param prompt - The message shown above the Confirm and Cancel buttons.
1614
- * @param options - Ephemeral flag, timeout, the built-in buttons' labels and style, and the optional
1615
- * `onConfirm`/`onCancel`/`onTimeout` outcome replies that edit the prompt in place.
1616
- * @returns `true` if confirmed, `false` on cancel or timeout.
1714
+ * Beside `this.values` this handler declares `this.channels`. Register the customId definitions with
1715
+ * `@ChannelMenuRoute` and list the same ones in the generic.
1716
+ *
1717
+ * @typeParam Defs - The customId definitions this handler decodes, e.g. `[typeof LogTargetId]`.
1718
+ * @typeParam Cache - The interaction cache state, `'cached'` by default.
1617
1719
  *
1618
1720
  * @example
1619
1721
  * ```ts
1620
- * import { getConfirmation } from '@seedcord/gateway';
1621
- *
1622
- * \@SlashRoute('ban')
1623
- * class BanHandler extends SlashHandler<'ban'> {
1722
+ * \@ChannelMenuRoute(LogTargetId)
1723
+ * class LogTarget extends ChannelMenuHandler<[typeof LogTargetId]> {
1624
1724
  * async execute() {
1625
- * const target = this.options.getUser('target');
1626
- * if (!(await getConfirmation(this, `Ban ${target.tag}? This cannot be undone.`))) return;
1627
- * await this.event.guild?.members.ban(target.id);
1725
+ * await this.update(`logging to ${this.channels.size} channel(s)`);
1628
1726
  * }
1629
1727
  * }
1630
1728
  * ```
1631
1729
  */
1632
- declare function getConfirmation(handler: RepliableHandler<NonModalInteraction>, prompt: string, options?: DefaultConfirmOptions): Promise<boolean>;
1730
+ declare abstract class ChannelMenuHandler<Defs extends readonly AnyCustomId[], Cache extends CacheType = 'cached'> extends SelectMenuHandler<ChannelSelectMenuInteraction<Cache>, Defs> {
1731
+ /** @internal */
1732
+ readonly [ComponentKindBrand]?: InteractionKind.ChannelMenu;
1733
+ /** The channels this select picked. */
1734
+ protected get channels(): ChannelSelectMenuInteraction<Cache>['channels'];
1735
+ }
1633
1736
  /**
1634
- * Shows a confirm/cancel prompt you build yourself and resolves to `true` only if the invoking user clicks
1635
- * confirm. The factory receives the two minted button ids. Set them on your own buttons. A cancel click or a
1636
- * timeout resolves to `false`. A failed prompt send throws into the fault boundary. A {@link ModalHandler} is
1637
- * excluded at compile time.
1737
+ * Base class for a mentionable select menu handler.
1638
1738
  *
1639
- * @param handler - The handler to prompt from, `this` inside a non-modal handler.
1640
- * @param prompt - A factory given the minted `{ confirm, cancel }` ids that returns the reply to show. Build
1641
- * it with the `BuilderComponent` and `RowComponent` wrappers.
1642
- * @param options - Ephemeral flag, timeout, and the optional `onConfirm`/`onCancel`/`onTimeout` outcome
1643
- * replies that edit the prompt in place.
1644
- * @returns `true` if confirmed, `false` on cancel or timeout.
1739
+ * A mentionable menu accepts users and roles together. `this.users`, `this.members`, and `this.roles`
1740
+ * can each come back empty.
1645
1741
  *
1646
- * @remarks
1647
- * The prompt is collected in-process, so it does not survive a bot restart. Without an outcome hook the
1648
- * prompt is gone once the user confirms, so deliver the result with a fresh `this.followUp(...)`.
1742
+ * @typeParam Defs - The customId definitions this handler decodes, e.g. `[typeof InviteId]`.
1743
+ * @typeParam Cache - The interaction cache state, `'cached'` by default.
1649
1744
  *
1650
1745
  * @example
1651
1746
  * ```ts
1652
- * import { ButtonBuilder, TextDisplayBuilder } from '@discordjs/builders';
1653
- * import { ButtonStyle } from 'discord.js';
1654
- * import { BuilderComponent, RowComponent, getConfirmation } from '@seedcord/gateway';
1655
- *
1656
- * class BanPrompt extends BuilderComponent<'container'> {
1657
- * constructor(text: string) {
1658
- * super('container');
1659
- * this.instance.addTextDisplayComponents(new TextDisplayBuilder().setContent(text));
1660
- * }
1661
- * }
1662
- *
1663
- * class ConfirmRow extends RowComponent<'button'> {
1664
- * constructor(ids: { confirm: string; cancel: string }) {
1665
- * super('button');
1666
- * const buttons = [
1667
- * [ids.confirm, 'Ban', ButtonStyle.Danger],
1668
- * [ids.cancel, 'Cancel', ButtonStyle.Secondary]
1669
- * ] as const;
1670
- * this.instance.addComponents(
1671
- * ...buttons.map(([id, label, style]) => new ButtonBuilder().setCustomId(id).setLabel(label).setStyle(style))
1672
- * );
1747
+ * \@MentionableMenuRoute(InviteId)
1748
+ * class Invite extends MentionableMenuHandler<[typeof InviteId]> {
1749
+ * async execute() {
1750
+ * await this.update(`inviting ${this.users.size} user(s) and ${this.roles.size} role(s)`);
1673
1751
  * }
1674
1752
  * }
1675
- *
1676
- * const target = this.options.getUser('target');
1677
- * const confirmed = await getConfirmation(
1678
- * this,
1679
- * (ids) => ({ components: [new BanPrompt(`Ban ${target.tag}?`).component, new ConfirmRow(ids).component] }),
1680
- * { onConfirm: { components: [new BanPrompt(`Banned ${target.tag}.`).component] } }
1681
- * );
1682
- * if (!confirmed) return;
1683
1753
  * ```
1684
1754
  */
1685
- declare function getConfirmation(handler: RepliableHandler<NonModalInteraction>, prompt: (ids: {
1686
- confirm: string;
1687
- cancel: string;
1688
- }) => Promisable<ReplyResponse>, options?: ConfirmOptions): Promise<boolean>;
1755
+ declare abstract class MentionableMenuHandler<Defs extends readonly AnyCustomId[], Cache extends CacheType = 'cached'> extends SelectMenuHandler<MentionableSelectMenuInteraction<Cache>, Defs> {
1756
+ /** @internal */
1757
+ readonly [ComponentKindBrand]?: InteractionKind.MentionableMenu;
1758
+ /** The users this select picked. */
1759
+ protected get users(): MentionableSelectMenuInteraction<Cache>['users'];
1760
+ /** The guild members behind the picked users. Discord resolves these only inside a guild. */
1761
+ protected get members(): MentionableSelectMenuInteraction<Cache>['members'];
1762
+ /** The roles this select picked. */
1763
+ protected get roles(): MentionableSelectMenuInteraction<Cache>['roles'];
1764
+ }
1689
1765
  //#endregion
1690
1766
  //#region src/pagination/PageContext.d.ts
1691
1767
  /**
@@ -1703,7 +1779,7 @@ interface PageContext {
1703
1779
  }
1704
1780
  //#endregion
1705
1781
  //#region src/pagination/Paginator.d.ts
1706
- type PaginatorHandlerCtor<Prefix extends string> = new (event: ButtonInteraction<CacheType>, core: Core) => ButtonHandler<[PageCursor<Prefix>], CacheType> & {
1782
+ type PaginatorHandlerCtor<Prefix extends string> = new (...args: ConstructorParameters<typeof ButtonHandler>) => ButtonHandler<[PageCursor<Prefix>], CacheType> & {
1707
1783
  execute(): Promise<void>;
1708
1784
  };
1709
1785
  /**
@@ -1856,5 +1932,5 @@ declare class Seedcord extends Pluggable<'gateway', 'server'> implements Core, S
1856
1932
  /** Package version */
1857
1933
  declare const version: string;
1858
1934
  //#endregion
1859
- export { ArraySource, AssignRoleNoticeOverrides, AutocompleteHandler, BaseHandler, ButtonHandler, ChannelMenuHandler, type CheckPermissionOptions, ComponentHandler, type ConfirmOptions, type Core, CursorSource, type DefaultConfirmOptions, Emojis, type EventGateContext, EventHandler, EventMiddleware, type EventSpec, type EventsConfig, type GateContext, Gated, type GatewayBotConfig, type GatewayConfig, GatewayEmoji, type GatewayFile, type GatewayPluginOptions, type GatewayReplyResponse, HasPermsToAssignOptions, IgnoreBots, type InjectedEmojiMap, type InteractionGateContext, InteractionHandler, InteractionMiddleware, MentionableMenuHandler, MessageContextMenuHandler, Middleware, type MiddlewareOptions, MiddlewareType, ModalHandler, Nsfw, type PageContext, type PageSource, Paginator, Plugin, type PluginLifecycleSpec, type PluginOptions, RegisterEvent, type RegisterEventOptions, RepliableHandler, type Repliables, ReplySender, RoleMenuHandler, Seedcord, SelectMenuHandler, type SentMessage, SlashHandler, type SlashOptions, StringMenuHandler, Subscriber, UserContextMenuHandler, UserMenuHandler, type ValidEventTypes, type ValidInteractionTypes, type ValidNonInteractionKeys, WebhookLog, WinstonConsoleSink, WinstonFileSink, checkBotPermissions, checkPermissions, getConfirmation, hasPermsToAssign, version };
1935
+ export { ArraySource, AssignRoleNoticeOverrides, AutocompleteHandler, BaseHandler, ButtonHandler, ChannelMenuHandler, type CheckPermissionOptions, ComponentHandler, type ConfirmOptions, type Core, CursorSource, type DefaultConfirmOptions, Emojis, type EventGateContext, EventHandler, EventMiddleware, type EventMiddlewareOptions, type EventSpec, type EventsConfig, type GateContext, Gated, type GatewayBotConfig, type GatewayConfig, GatewayEmoji, type GatewayFile, type GatewayPluginOptions, type GatewayReplyResponse, HasPermsToAssignOptions, IgnoreBots, type InjectedEmojiMap, type InteractionGateContext, InteractionHandler, InteractionMiddleware, MentionableMenuHandler, MessageContextMenuHandler, ModalHandler, Nsfw, type PageContext, type PageSource, Paginator, Plugin, type PluginLifecycleSpec, type PluginOptions, RegisterEvent, RegisterEventMiddleware, type RegisterEventOptions, RepliableHandler, type Repliables, ReplySender, RoleMenuHandler, Seedcord, SelectMenuHandler, type SentMessage, SlashHandler, type SlashOptions, StringMenuHandler, Subscriber, UserContextMenuHandler, UserMenuHandler, type ValidEventTypes, type ValidInteractionTypes, type ValidNonInteractionKeys, WebhookLog, WinstonConsoleSink, WinstonFileSink, checkBotPermissions, checkPermissions, getConfirmation, hasPermsToAssign, version };
1860
1936
  //# sourceMappingURL=index.d.mts.map