@seedcord/gateway 0.2.1 → 0.3.1

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,6 +1,6 @@
1
1
  import "reflect-metadata";
2
- import { AnyCustomId, AutocompletableNames, AutocompleteRouteBrand, BaseReplySender, ChoiceValueOf, ComponentDefsBrand, ComponentKindBrand, ContextMenuKindBrand, EntryFor, FocusedField, GateFitsWith, HasComponentDefs, MatchArms, ModalLike, PageCursor, SingleParams, SlashOptions as SlashOptions$1, SlashRouteBrand } from "@seedcord/core/internal";
3
- import { ArraySource as ArraySource$1, AutocompleteOptions, BaseHandler as BaseHandler$1, Bus, ContextMenuKind, CoreBase, CursorSource as CursorSource$1, DispatchContext, Gate, GateContextBase, GateNoticeOptions, GuildPermissionsContext, Initializeable, Notice, PaginatorBase, PaginatorConfig, PermissionNoticeOverrides, PermissionScope, RepliableHandler as RepliableHandler$1, RequiredOf, ResolvedEmoji, SelectMenuKind, SlashOptionRegistry, Subscriber as Subscriber$1, SubscriptionKey, WebhookLog as WebhookLog$1 } from "@seedcord/core";
2
+ import { AnyCustomId, AutocompletableNames, AutocompleteRouteBrand, BaseReplySender, CacheFor, ChoiceValueOf, ComponentDefsBrand, ComponentKindBrand, ContextMenuKindBrand, ContextMenuNamesBrand, EntryFor, FocusedField, GateFitsWith, HasComponentDefs, Initializeable, MatchArms, MenuCacheFor, ModalLike, NamesFor, PageCursor, SingleParams, SlashOptions as SlashOptions$1, SlashRouteBrand } from "@seedcord/core/internal";
3
+ import { ArraySource as ArraySource$1, AutocompleteOptions, BaseHandler as BaseHandler$1, Bus, CoreBase, CursorSource as CursorSource$1, DispatchContext, Gate, GateContextBase, GateNoticeOptions, GuildPermissionsContext, Notice, PaginatorBase, PaginatorConfig, PermissionNoticeOverrides, PermissionScope, RepliableHandler as RepliableHandler$1, RequiredOf, ResolvedEmoji, SelectMenuKind, SlashRegistry, Subscriber as Subscriber$1, SubscriptionKey, WebhookLog as WebhookLog$1 } from "@seedcord/core";
4
4
  import { Logger } from "@seedcord/logger";
5
5
  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, ModalSubmitInteraction, Role, RoleSelectMenuInteraction, StringSelectMenuInteraction, TextChannel, User, UserContextMenuCommandInteraction, UserSelectMenuInteraction } from "discord.js";
6
6
  import { Plugin as Plugin$1, PluginLifecycleSpec, PluginOptions, PluginOptions as PluginOptions$1 } from "@seedcord/core/plugin";
@@ -77,16 +77,17 @@ interface GatewayConfig extends Config {
77
77
  }
78
78
  //#endregion
79
79
  //#region src/bot/Bot.d.ts
80
+ declare const loggerSlot: unique symbol;
80
81
  /**
81
82
  * The Discord client and its controllers. Access it through `core.bot`.
82
83
  */
83
- declare const loggerSlot: unique symbol;
84
84
  declare class Bot implements Initializeable, HmrAware {
85
85
  readonly botToken: string;
86
86
  private readonly logger;
87
87
  /** @internal */
88
88
  readonly [loggerSlot]: Logger;
89
89
  private isInitialized;
90
+ private readonly intents;
90
91
  private readonly _client;
91
92
  private readonly interactions?;
92
93
  private readonly events?;
@@ -96,7 +97,8 @@ declare class Bot implements Initializeable, HmrAware {
96
97
  onHmr(event: HmrUpdateEvent): Promise<void>;
97
98
  /** @internal */
98
99
  constructor(core: Core);
99
- private applicationId;
100
+ /** @internal */
101
+ get applicationId(): string;
100
102
  private registerShutdownTasks;
101
103
  private stopAccepting;
102
104
  private drain;
@@ -706,13 +708,13 @@ interface GatewayLens<Cache extends CacheType> {
706
708
  * A channel option declared with `addChannelTypes` narrows `getChannel` to the matching channel subtype.
707
709
  * Pass the raw `this.event.options` resolver for anything this view does not cover.
708
710
  *
709
- * @typeParam Route - A route key from the generated {@link SlashOptionRegistry}.
710
- * @typeParam Cache - The interaction cache state, `'cached'` by default.
711
+ * @typeParam Route - A route key from the generated {@link SlashRegistry}.
712
+ * @typeParam Cache - The interaction cache state. The command's contexts set it.
711
713
  */
712
- type SlashOptions<Route extends keyof SlashOptionRegistry, Cache extends CacheType = 'cached'> = SlashOptions$1<Route, GatewayLens<Cache>>;
714
+ type SlashOptions<Route extends keyof SlashRegistry, Cache extends CacheType = CacheFor<Route>> = SlashOptions$1<Route, GatewayLens<Cache>>;
713
715
  //#endregion
714
716
  //#region src/handlers/interaction/SlashHandler.d.ts
715
- type SlashMatchArms<Route extends keyof SlashOptionRegistry, Cache extends CacheType, Ret> = { [Key in Route]: (options: SlashOptions<Key, Cache>) => Promisable<Ret>; };
717
+ type SlashMatchArms<Route extends keyof SlashRegistry, Cache extends CacheType, Ret> = { [Key in Route]: (options: SlashOptions<Key, Cache>) => Promisable<Ret>; };
716
718
  /**
717
719
  * Base class for a chat-input (slash) command handler.
718
720
  *
@@ -720,8 +722,8 @@ type SlashMatchArms<Route extends keyof SlashOptionRegistry, Cache extends Cache
720
722
  * `this.options` for a single command or `this.match` for several. Command authoring stays plain
721
723
  * discord.js, `seedcord codegen` reads its `toJSON()` to type these options.
722
724
  *
723
- * @typeParam Route - One or more route keys from {@link SlashOptionRegistry}, e.g. `'ban'` or `'ban' | 'kick'`.
724
- * @typeParam Cache - The interaction cache state, `'cached'` by default.
725
+ * @typeParam Route - One or more route keys from {@link SlashRegistry}, e.g. `'ban'` or `'ban' | 'kick'`.
726
+ * @typeParam Cache - The interaction cache state. The command's contexts set it.
725
727
  *
726
728
  * @example
727
729
  * ```ts
@@ -734,7 +736,7 @@ type SlashMatchArms<Route extends keyof SlashOptionRegistry, Cache extends Cache
734
736
  * }
735
737
  * ```
736
738
  */
737
- declare abstract class SlashHandler<Route extends keyof SlashOptionRegistry, Cache extends CacheType = 'cached'> extends InteractionHandler<ChatInputCommandInteraction<Cache>> {
739
+ declare abstract class SlashHandler<Route extends keyof SlashRegistry, Cache extends CacheType = CacheFor<Route>> extends InteractionHandler<ChatInputCommandInteraction<Cache>> {
738
740
  /** @internal */
739
741
  readonly [SlashRouteBrand]?: Route;
740
742
  /**
@@ -771,43 +773,63 @@ declare abstract class SlashHandler<Route extends keyof SlashOptionRegistry, Cac
771
773
  }
772
774
  //#endregion
773
775
  //#region src/handlers/interaction/ContextMenuHandler.d.ts
774
- type InteractionFor<Kind extends ContextMenuKind, Cache extends CacheType> = Kind extends ApplicationCommandType.User ? UserContextMenuCommandInteraction<Cache> : MessageContextMenuCommandInteraction<Cache>;
775
- type TargetFor<Kind extends ContextMenuKind, Cache extends CacheType> = Kind extends ApplicationCommandType.User ? User : Message<BooleanCache<Cache>>;
776
- type TargetMemberFor<Kind extends ContextMenuKind, Cache extends CacheType> = Kind extends ApplicationCommandType.User ? CacheTypeReducer<Cache, GuildMember, APIInteractionGuildMember> | null : never;
777
776
  /**
778
- * Base class for a context-menu command handler (right-click a user or a message).
777
+ * Base class for a user context-menu command handler (right-click a user).
779
778
  *
780
- * Pass the kind from `discord.js`'s {@link ApplicationCommandType} as the generic, the same value as
781
- * `@ContextMenuRoute`. Read the right-clicked entity from `this.target`, a `User` for a user menu and a
782
- * `Message` for a message menu. Context menus carry no options, so a handler registered for several names
783
- * reads `this.target` uniformly with no per-name branch.
779
+ * Pass the command name(s) as the generic, the same as `@UserContextMenuRoute`. Read the right-clicked
780
+ * user from `this.target` and that same user's guild member from `this.targetMember`.
784
781
  *
785
- * @typeParam Kind - `ApplicationCommandType.User` or `ApplicationCommandType.Message`.
786
- * @typeParam Cache - The interaction cache state, `'cached'` by default.
782
+ * @typeParam Names - One or more command names from the generated user registry.
783
+ * @typeParam Cache - The interaction cache state. The command's contexts set it.
784
+ *
785
+ * @example
786
+ * ```ts
787
+ * \@UserContextMenuRoute('View Profile')
788
+ * class ViewProfile extends UserContextMenuHandler<'View Profile'> {
789
+ * async execute() {
790
+ * const user = this.target;
791
+ * }
792
+ * }
793
+ * ```
794
+ */
795
+ declare abstract class UserContextMenuHandler<Names extends NamesFor<ApplicationCommandType.User>, Cache extends CacheType = MenuCacheFor<ApplicationCommandType.User, Names>> extends InteractionHandler<UserContextMenuCommandInteraction<Cache>> {
796
+ /** @internal */
797
+ readonly [ContextMenuKindBrand]?: ApplicationCommandType.User;
798
+ /** @internal */
799
+ readonly [ContextMenuNamesBrand]?: Names;
800
+ protected get target(): User;
801
+ /** The right-clicked user's guild member, null outside a guild. */
802
+ protected get targetMember(): CacheTypeReducer<Cache, GuildMember, APIInteractionGuildMember> | null;
803
+ }
804
+ /**
805
+ * Base class for a message context-menu command handler (right-click a message).
806
+ *
807
+ * Pass the command name(s) as the generic, the same as `@MessageContextMenuRoute`. Read the right-clicked
808
+ * message from `this.target`.
809
+ *
810
+ * @typeParam Names - One or more command names from the generated message registry.
811
+ * @typeParam Cache - The interaction cache state. The command's contexts set it.
787
812
  *
788
813
  * @example
789
814
  * ```ts
790
- * \@ContextMenuRoute(ApplicationCommandType.Message, 'Report Message')
791
- * class ReportMessage extends ContextMenuHandler<ApplicationCommandType.Message> {
815
+ * \@MessageContextMenuRoute('Report Message')
816
+ * class ReportMessage extends MessageContextMenuHandler<'Report Message'> {
792
817
  * async execute() {
793
818
  * const message = this.target;
794
819
  * }
795
820
  * }
796
821
  * ```
797
822
  */
798
- declare abstract class ContextMenuHandler<Kind extends ContextMenuKind, Cache extends CacheType = 'cached'> extends InteractionHandler<InteractionFor<Kind, Cache>> {
823
+ declare abstract class MessageContextMenuHandler<Names extends NamesFor<ApplicationCommandType.Message>, Cache extends CacheType = MenuCacheFor<ApplicationCommandType.Message, Names>> extends InteractionHandler<MessageContextMenuCommandInteraction<Cache>> {
799
824
  /** @internal */
800
- readonly [ContextMenuKindBrand]?: Kind;
801
- protected get target(): TargetFor<Kind, Cache>;
802
- /**
803
- * The invoking guild member, resolved only on user menus. Reading it on a message menu is a compile
804
- * error since the type is `never` there.
805
- */
806
- protected get targetMember(): TargetMemberFor<Kind, Cache>;
825
+ readonly [ContextMenuKindBrand]?: ApplicationCommandType.Message;
826
+ /** @internal */
827
+ readonly [ContextMenuNamesBrand]?: Names;
828
+ protected get target(): Message<BooleanCache<Cache>>;
807
829
  }
808
830
  //#endregion
809
831
  //#region src/handlers/interaction/AutocompleteHandler.d.ts
810
- type FocusedArms<Route extends keyof SlashOptionRegistry, Ret> = { [Name in AutocompletableNames<Route>]: (value: string, respond: (choices: readonly ApplicationCommandOptionChoiceData<ChoiceValueOf<EntryFor<Route, Name>>>[]) => Promise<void>) => Promisable<Ret>; };
832
+ 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>; };
811
833
  /**
812
834
  * Base class for a Discord autocomplete handler.
813
835
  *
@@ -815,8 +837,8 @@ type FocusedArms<Route extends keyof SlashOptionRegistry, Ret> = { [Name in Auto
815
837
  * `@AutocompleteRoute`. Branch on the focused field with `this.match`, read already-entered sibling options
816
838
  * with `this.options`, and find which command fired with `this.route`.
817
839
  *
818
- * @typeParam Route - One or more route keys from {@link SlashOptionRegistry}, e.g. `'search'` or `'search' | 'find'`.
819
- * @typeParam Cache - The interaction cache state, `'cached'` by default.
840
+ * @typeParam Route - One or more route keys from {@link SlashRegistry}, e.g. `'search'` or `'search' | 'find'`.
841
+ * @typeParam Cache - The interaction cache state. The command's contexts set it.
820
842
  *
821
843
  * @example
822
844
  * ```ts
@@ -830,7 +852,7 @@ type FocusedArms<Route extends keyof SlashOptionRegistry, Ret> = { [Name in Auto
830
852
  * }
831
853
  * ```
832
854
  */
833
- declare abstract class AutocompleteHandler<Route extends keyof SlashOptionRegistry, Cache extends CacheType = 'cached'> extends BaseHandler<AutocompleteInteraction<Cache>> {
855
+ declare abstract class AutocompleteHandler<Route extends keyof SlashRegistry, Cache extends CacheType = CacheFor<Route>> extends BaseHandler<AutocompleteInteraction<Cache>> {
834
856
  /** @internal */
835
857
  readonly [AutocompleteRouteBrand]?: Route;
836
858
  constructor(event: AutocompleteInteraction<Cache>, core: Core, dispatch?: DispatchContext);
@@ -1131,7 +1153,7 @@ declare class GatewayEmoji<Source extends SavedEmojiType = SavedEmojiType> exten
1131
1153
  type InjectedEmojiMap = { [K in keyof EmojiMap]: EmojiMap[K] extends 'guild' ? GatewayEmoji<GuildEmoji> : GatewayEmoji<ApplicationEmoji>; };
1132
1154
  /**
1133
1155
  * The bot's resolved emojis, keyed by {@link EmojiMap}. Filled by {@link EmojiInjector} during startup.
1134
- * A read before that throws.
1156
+ * Throws if you read a key the injector did not fill.
1135
1157
  */
1136
1158
  declare const Emojis: InjectedEmojiMap;
1137
1159
  //#endregion
@@ -1404,7 +1426,7 @@ interface PageContext {
1404
1426
  }
1405
1427
  //#endregion
1406
1428
  //#region src/pagination/Paginator.d.ts
1407
- type PaginatorHandlerCtor<Prefix extends string> = new (event: ButtonInteraction<'cached'>, core: Core) => ButtonHandler<[PageCursor<Prefix>]> & {
1429
+ type PaginatorHandlerCtor<Prefix extends string> = new (event: ButtonInteraction<CacheType>, core: Core) => ButtonHandler<[PageCursor<Prefix>], CacheType> & {
1408
1430
  execute(): Promise<void>;
1409
1431
  };
1410
1432
  /**
@@ -1418,7 +1440,7 @@ type PaginatorHandlerCtor<Prefix extends string> = new (event: ButtonInteraction
1418
1440
  * ```ts
1419
1441
  * export const Bans = new Paginator({
1420
1442
  * prefix: 'bans',
1421
- * source: new ArraySource((ctx) => ctx.guild.bans.fetch().then((b) => [...b.values()]), { perPage: 10 }),
1443
+ * source: new ArraySource((ctx) => ctx.guild?.bans.fetch().then((b) => [...b.values()]) ?? [], { perPage: 10 }),
1422
1444
  * renderItem: (ban) => ban.user.tag
1423
1445
  * });
1424
1446
  *
@@ -1517,6 +1539,8 @@ declare class Seedcord extends Pluggable<'gateway', 'server'> implements Core, S
1517
1539
  readonly bot: Bot;
1518
1540
  /** The discord.js client's REST client. Its token is set during the Login phase. */
1519
1541
  readonly rest: REST;
1542
+ /** The bot's Discord application id. Throws if you read it before login. */
1543
+ get applicationId(): string;
1520
1544
  /** @see {@link IRateLimiter} */
1521
1545
  readonly rateLimiter: IRateLimiter;
1522
1546
  /**
@@ -1541,5 +1565,5 @@ declare class Seedcord extends Pluggable<'gateway', 'server'> implements Core, S
1541
1565
  /** Package version */
1542
1566
  declare const version: string;
1543
1567
  //#endregion
1544
- export { ArraySource, AssignRoleNoticeOverrides, AutocompleteHandler, BaseHandler, ButtonHandler, type CheckPermissionOptions, ComponentHandler, type ConfirmOptions, ContextMenuHandler, 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, Middleware, type MiddlewareOptions, MiddlewareType, ModalHandler, Nsfw, type PageContext, Paginator, Plugin, type PluginLifecycleSpec, type PluginOptions, RegisterEvent, type RegisterEventOptions, RepliableHandler, type Repliables, Seedcord, SelectMenuHandler, SlashHandler, type SlashOptions, Subscriber, type ValidEventTypes, type ValidInteractionTypes, type ValidNonInteractionKeys, WebhookLog, checkBotPermissions, checkPermissions, getConfirmation, hasPermsToAssign, version };
1568
+ export { ArraySource, AssignRoleNoticeOverrides, AutocompleteHandler, BaseHandler, ButtonHandler, 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, MessageContextMenuHandler, Middleware, type MiddlewareOptions, MiddlewareType, ModalHandler, Nsfw, type PageContext, Paginator, Plugin, type PluginLifecycleSpec, type PluginOptions, RegisterEvent, type RegisterEventOptions, RepliableHandler, type Repliables, Seedcord, SelectMenuHandler, SlashHandler, type SlashOptions, Subscriber, UserContextMenuHandler, type ValidEventTypes, type ValidInteractionTypes, type ValidNonInteractionKeys, WebhookLog, checkBotPermissions, checkPermissions, getConfirmation, hasPermsToAssign, version };
1545
1569
  //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs CHANGED
@@ -1,11 +1,11 @@
1
1
  import "reflect-metadata";
2
2
  import { installNodeDefaults } from "@seedcord/logger/node";
3
- import { BaseReplySender, CommandInjector, EventMetadataKey, GateNotice, GatedMetadataKey, HmrManager, InteractionMetadataKey, InteractionRouteKeys, InteractionRoutes, MiddlewareMetadataKey, MissingPermissions, NoticeCard, PermissionNames, PublishDefault, accessorStore, areRoutes, asError, busLoggerOf, clearStore, decodeComponentRoute, deferFlags, guardedAccessor, isEmojiTuple, outcomeFor, pickNotice, prefixOf, queuedMsFor, reportDispatch, reportedWrite, routeIdOf, runHandlerGates, sendFlags, setBotColor, slowGateMonitor } from "@seedcord/core/internal";
3
+ import { BaseReplySender, EventMetadataKey, GateNotice, GatedMetadataKey, HmrManager, InteractionMetadataKey, InteractionRouteKeys, InteractionRoutes, MiddlewareMetadataKey, MissingPermissions, NoticeCard, PermissionNames, PublishDefault, accessorStore, areRoutes, asError, busLoggerOf, clearStore, decodeComponentRoute, deferFlags, guardedAccessor, isEmojiTuple, outcomeFor, pickNotice, prefixOf, queuedMsFor, reportDispatch, reportedWrite, routeIdOf, runHandlerGates, sendFlags, setBotColor, slowGateMonitor } from "@seedcord/core/internal";
4
4
  import { SeedcordErrorCode, paint } from "@seedcord/errors";
5
5
  import { SeedcordError, SeedcordTypeError, validateDiscordToken } from "@seedcord/errors/internal";
6
6
  import { ArraySource as ArraySource$1, BaseHandler as BaseHandler$1, BuilderComponent, Bus, CursorSource as CursorSource$1, CustomId, DispatchContext, Fault, Notice, PaginatorBase, RepliableHandler as RepliableHandler$1, ResolvedEmoji, RowComponent, Silence, Subscriber as Subscriber$1, WebhookLog as WebhookLog$1, assertPermissions, defineGate } from "@seedcord/core";
7
7
  import { Logger, LoggerChannelRegistry } from "@seedcord/logger";
8
- import { ButtonStyle, Client, ComponentType, DiscordAPIError, Events, Guild, GuildMember, Message, MessageFlags, PermissionFlagsBits, RESTJSONErrorCodes, Role, User } from "discord.js";
8
+ import { ButtonStyle, Client, ComponentType, DiscordAPIError, Events, GatewayIntentBits, Guild, GuildMember, IntentsBitField, InteractionContextType, Message, MessageFlags, PermissionFlagsBits, RESTJSONErrorCodes, Role, User } from "discord.js";
9
9
  import { ButtonBuilder, TextDisplayBuilder } from "@discordjs/builders";
10
10
  import { buildSlashRoute } from "@seedcord/utils/internal";
11
11
  import { Plugin as Plugin$1 } from "@seedcord/core/plugin";
@@ -274,7 +274,7 @@ var GatewayEmoji = class extends ResolvedEmoji {
274
274
  const emojiStorage = accessorStore();
275
275
  /**
276
276
  * The bot's resolved emojis, keyed by {@link EmojiMap}. Filled by {@link EmojiInjector} during startup.
277
- * A read before that throws.
277
+ * Throws if you read a key the injector did not fill.
278
278
  */
279
279
  const Emojis = guardedAccessor("Emojis", emojiStorage);
280
280
  var EmojiInjector = class {
@@ -708,8 +708,8 @@ function slashRouteOf(interaction) {
708
708
  * `this.options` for a single command or `this.match` for several. Command authoring stays plain
709
709
  * discord.js, `seedcord codegen` reads its `toJSON()` to type these options.
710
710
  *
711
- * @typeParam Route - One or more route keys from {@link SlashOptionRegistry}, e.g. `'ban'` or `'ban' | 'kick'`.
712
- * @typeParam Cache - The interaction cache state, `'cached'` by default.
711
+ * @typeParam Route - One or more route keys from {@link SlashRegistry}, e.g. `'ban'` or `'ban' | 'kick'`.
712
+ * @typeParam Cache - The interaction cache state. The command's contexts set it.
713
713
  *
714
714
  * @example
715
715
  * ```ts
@@ -766,39 +766,57 @@ var SlashHandler = class extends InteractionHandler {
766
766
  //#endregion
767
767
  //#region src/handlers/interaction/ContextMenuHandler.ts
768
768
  /**
769
- * Base class for a context-menu command handler (right-click a user or a message).
769
+ * Base class for a user context-menu command handler (right-click a user).
770
770
  *
771
- * Pass the kind from `discord.js`'s {@link ApplicationCommandType} as the generic, the same value as
772
- * `@ContextMenuRoute`. Read the right-clicked entity from `this.target`, a `User` for a user menu and a
773
- * `Message` for a message menu. Context menus carry no options, so a handler registered for several names
774
- * reads `this.target` uniformly with no per-name branch.
771
+ * Pass the command name(s) as the generic, the same as `@UserContextMenuRoute`. Read the right-clicked
772
+ * user from `this.target` and that same user's guild member from `this.targetMember`.
775
773
  *
776
- * @typeParam Kind - `ApplicationCommandType.User` or `ApplicationCommandType.Message`.
777
- * @typeParam Cache - The interaction cache state, `'cached'` by default.
774
+ * @typeParam Names - One or more command names from the generated user registry.
775
+ * @typeParam Cache - The interaction cache state. The command's contexts set it.
778
776
  *
779
777
  * @example
780
778
  * ```ts
781
- * \@ContextMenuRoute(ApplicationCommandType.Message, 'Report Message')
782
- * class ReportMessage extends ContextMenuHandler<ApplicationCommandType.Message> {
779
+ * \@UserContextMenuRoute('View Profile')
780
+ * class ViewProfile extends UserContextMenuHandler<'View Profile'> {
783
781
  * async execute() {
784
- * const message = this.target;
782
+ * const user = this.target;
785
783
  * }
786
784
  * }
787
785
  * ```
788
786
  */
789
- var ContextMenuHandler = class extends InteractionHandler {
787
+ var UserContextMenuHandler = class extends InteractionHandler {
790
788
  get target() {
791
- const event = this.event;
792
- return "targetUser" in event ? event.targetUser : event.targetMessage;
789
+ return this.event.targetUser;
793
790
  }
794
- /**
795
- * The invoking guild member, resolved only on user menus. Reading it on a message menu is a compile
796
- * error since the type is `never` there.
797
- */
791
+ /** The right-clicked user's guild member, null outside a guild. */
798
792
  get targetMember() {
799
793
  return this.event.targetMember;
800
794
  }
801
795
  };
796
+ /**
797
+ * Base class for a message context-menu command handler (right-click a message).
798
+ *
799
+ * Pass the command name(s) as the generic, the same as `@MessageContextMenuRoute`. Read the right-clicked
800
+ * message from `this.target`.
801
+ *
802
+ * @typeParam Names - One or more command names from the generated message registry.
803
+ * @typeParam Cache - The interaction cache state. The command's contexts set it.
804
+ *
805
+ * @example
806
+ * ```ts
807
+ * \@MessageContextMenuRoute('Report Message')
808
+ * class ReportMessage extends MessageContextMenuHandler<'Report Message'> {
809
+ * async execute() {
810
+ * const message = this.target;
811
+ * }
812
+ * }
813
+ * ```
814
+ */
815
+ var MessageContextMenuHandler = class extends InteractionHandler {
816
+ get target() {
817
+ return this.event.targetMessage;
818
+ }
819
+ };
802
820
 
803
821
  //#endregion
804
822
  //#region src/handlers/interaction/AutocompleteHandler.ts
@@ -809,8 +827,8 @@ var ContextMenuHandler = class extends InteractionHandler {
809
827
  * `@AutocompleteRoute`. Branch on the focused field with `this.match`, read already-entered sibling options
810
828
  * with `this.options`, and find which command fired with `this.route`.
811
829
  *
812
- * @typeParam Route - One or more route keys from {@link SlashOptionRegistry}, e.g. `'search'` or `'search' | 'find'`.
813
- * @typeParam Cache - The interaction cache state, `'cached'` by default.
830
+ * @typeParam Route - One or more route keys from {@link SlashRegistry}, e.g. `'search'` or `'search' | 'find'`.
831
+ * @typeParam Cache - The interaction cache state. The command's contexts set it.
814
832
  *
815
833
  * @example
816
834
  * ```ts
@@ -1177,7 +1195,7 @@ function contextOf(interaction, core) {
1177
1195
  * ```ts
1178
1196
  * export const Bans = new Paginator({
1179
1197
  * prefix: 'bans',
1180
- * source: new ArraySource((ctx) => ctx.guild.bans.fetch().then((b) => [...b.values()]), { perPage: 10 }),
1198
+ * source: new ArraySource((ctx) => ctx.guild?.bans.fetch().then((b) => [...b.values()]) ?? [], { perPage: 10 }),
1181
1199
  * renderItem: (ban) => ban.user.tag
1182
1200
  * });
1183
1201
  *
@@ -2165,6 +2183,20 @@ var InteractionDispatcher = class {
2165
2183
  }
2166
2184
  };
2167
2185
 
2186
+ //#endregion
2187
+ //#region src/miscellaneous/assertGuildsIntent.ts
2188
+ function reachesGuilds(command) {
2189
+ const contexts = command.toJSON().contexts;
2190
+ if (!contexts || contexts.length === 0) return true;
2191
+ return contexts.includes(InteractionContextType.Guild);
2192
+ }
2193
+ function assertGuildsIntent(intents, commands) {
2194
+ if ((IntentsBitField.resolve(intents) & GatewayIntentBits.Guilds) !== 0) return;
2195
+ const needing = commands.filter((command) => reachesGuilds(command)).map((command) => command.name);
2196
+ if (needing.length === 0) return;
2197
+ throw new SeedcordError(SeedcordErrorCode.MissingGuildsIntent, [needing.join(", ")]);
2198
+ }
2199
+
2168
2200
  //#endregion
2169
2201
  //#region \0@oxc-project+runtime@0.142.0/helpers/esm/decorate.js
2170
2202
  function __decorate(decorators, target, key, desc) {
@@ -2180,15 +2212,16 @@ const DISPATCH_DRAIN_TIMEOUT_MS = 5e3;
2180
2212
  const DRAIN_TASK_TIMEOUT_MS = 6e3;
2181
2213
  const UNBIND_TIMEOUT_MS = 2e3;
2182
2214
  const LOGOUT_TIMEOUT_MS = 2e3;
2215
+ const loggerSlot = Symbol("seedcord:bot:logger");
2183
2216
  /**
2184
2217
  * The Discord client and its controllers. Access it through `core.bot`.
2185
2218
  */
2186
- const loggerSlot = Symbol("seedcord:bot:logger");
2187
2219
  var Bot = class {
2188
2220
  logger = new Logger("Bot", { channel: "bot" });
2189
2221
  /** @internal */
2190
2222
  [loggerSlot] = this.logger;
2191
2223
  isInitialized = false;
2224
+ intents;
2192
2225
  _client;
2193
2226
  interactions;
2194
2227
  events;
@@ -2202,26 +2235,16 @@ var Bot = class {
2202
2235
  }
2203
2236
  /** @internal */
2204
2237
  constructor(core) {
2238
+ this.intents = core.config.bot.clientOptions.intents;
2205
2239
  this._client = new Client(core.config.bot.clientOptions);
2206
2240
  if (core.config.bot.interactions.path) this.interactions = new InteractionDispatcher(core);
2207
2241
  if (core.config.bot.events.path) this.events = new EventDispatcher(core);
2208
- const commandsDir = core.config.bot.commands.path;
2209
- if (commandsDir) {
2210
- const injector = new CommandInjector();
2211
- const registry = new CommandRegistry({
2212
- dir: commandsDir,
2213
- rest: this._client.rest,
2214
- applicationId: () => this.applicationId(),
2215
- onDeployed: (result) => {
2216
- injector.inject(result, registry.allCommands());
2217
- }
2218
- });
2219
- this.commandRegistry = registry;
2220
- }
2242
+ if (core.config.bot.commands.path) this.commandRegistry = new CommandRegistry(core);
2221
2243
  this.emojiInjector = new EmojiInjector(core);
2222
2244
  this.registerShutdownTasks(core);
2223
2245
  }
2224
- applicationId() {
2246
+ /** @internal */
2247
+ get applicationId() {
2225
2248
  const { application } = this._client;
2226
2249
  if (!application) throw new SeedcordError(SeedcordErrorCode.CoreApplicationUnavailable);
2227
2250
  return application.id;
@@ -2252,6 +2275,7 @@ var Bot = class {
2252
2275
  await this.emojiInjector.init();
2253
2276
  if (this.commandRegistry) {
2254
2277
  await this.commandRegistry.init();
2278
+ assertGuildsIntent(this.intents, this.commandRegistry.allCommands());
2255
2279
  await this.commandRegistry.setCommands();
2256
2280
  this.interactions?.warnUnhandledRoutes(this.commandRegistry.routeLeaves());
2257
2281
  this.interactions?.warnUnhandledContextMenuRoutes(this.commandRegistry.contextMenuLeaves());
@@ -2287,7 +2311,7 @@ function botLoggerOf(bot) {
2287
2311
  //#endregion
2288
2312
  //#region src/version.ts
2289
2313
  /** Package version */
2290
- const version = "0.2.1";
2314
+ const version = "0.3.1";
2291
2315
 
2292
2316
  //#endregion
2293
2317
  //#region src/Seedcord.ts
@@ -2312,6 +2336,10 @@ var Seedcord = class Seedcord extends Pluggable {
2312
2336
  bot;
2313
2337
  /** The discord.js client's REST client. Its token is set during the Login phase. */
2314
2338
  rest;
2339
+ /** The bot's Discord application id. Throws if you read it before login. */
2340
+ get applicationId() {
2341
+ return this.bot.applicationId;
2342
+ }
2315
2343
  /** @see {@link IRateLimiter} */
2316
2344
  rateLimiter;
2317
2345
  /**
@@ -2386,5 +2414,5 @@ var Seedcord = class Seedcord extends Pluggable {
2386
2414
  };
2387
2415
 
2388
2416
  //#endregion
2389
- export { ArraySource, AutocompleteHandler, BaseHandler, ButtonHandler, ComponentHandler, ContextMenuHandler, CursorSource, Emojis, EventHandler, EventMiddleware, Gated, GatewayEmoji, IgnoreBots, InteractionHandler, InteractionMiddleware, Middleware, MiddlewareType, ModalHandler, Nsfw, Paginator, Plugin, RegisterEvent, RepliableHandler, Seedcord, SelectMenuHandler, SlashHandler, Subscriber, WebhookLog, checkBotPermissions, checkPermissions, getConfirmation, hasPermsToAssign, version };
2417
+ export { ArraySource, AutocompleteHandler, BaseHandler, ButtonHandler, ComponentHandler, CursorSource, Emojis, EventHandler, EventMiddleware, Gated, GatewayEmoji, IgnoreBots, InteractionHandler, InteractionMiddleware, MessageContextMenuHandler, Middleware, MiddlewareType, ModalHandler, Nsfw, Paginator, Plugin, RegisterEvent, RepliableHandler, Seedcord, SelectMenuHandler, SlashHandler, Subscriber, UserContextMenuHandler, WebhookLog, checkBotPermissions, checkPermissions, getConfirmation, hasPermsToAssign, version };
2390
2418
  //# sourceMappingURL=index.mjs.map