@seedcord/gateway 0.1.4 → 0.2.0-next.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/README.md CHANGED
@@ -1,16 +1,45 @@
1
- <p align="center">
1
+ <div align="center">
2
2
  <picture>
3
3
  <source media="(prefers-color-scheme: dark)" srcset="https://cdn.seedcord.org/assets/wordmark-dark.webp" />
4
4
  <img src="https://cdn.seedcord.org/assets/wordmark-light.webp" alt="seedcord" width="440" />
5
5
  </picture>
6
- </p>
6
+ </div>
7
7
 
8
- ---
8
+ <div align="center">
9
+ <h3>The whole Discord bot, wired and typed</h3>
10
+ <a href="https://seedcord.org">Website</a> ·
11
+ <a href="https://guide.seedcord.org">Guide</a> ·
12
+ <a href="https://docs.seedcord.org">Reference</a> ·
13
+ <a href="https://discord.gg/DzFxY58WXf">Discord</a>
14
+ </div>
9
15
 
10
- _This repository is a work in progress._
16
+ <br />
11
17
 
12
- - There are no stable releases yet but changes are being made actively.
13
- - Until a major v1.0.0 release for seedcord, expect breaking changes in minor versions.
14
- - Documentation will come soon as well!
18
+ <div align="center">
15
19
 
16
- If you'd like to try it out, you can check out the code in `mock`
20
+ [![npm](https://img.shields.io/npm/v/@seedcord/gateway?style=flat-square&logo=npm&logoColor=c8341f&label=&labelColor=1f1f1f&color=c8341f)](https://www.npmjs.com/package/@seedcord/gateway) [![node](https://img.shields.io/node/v/@seedcord/gateway?style=flat-square&label=node&labelColor=1f1f1f&color=4d7d33)](https://nodejs.org) [![license](https://img.shields.io/npm/l/@seedcord/gateway?style=flat-square&label=license&labelColor=1f1f1f&color=f8f6e8)](LICENSE)
21
+
22
+ </div>
23
+
24
+ ## About
25
+
26
+ `@seedcord/gateway` runs a seedcord bot over a websocket connection, built on discord.js. A gateway connection carries message, member, voice, and reaction events. Discord never posts those to an interactions endpoint. Pick this transport when the bot reacts to any of them.
27
+
28
+ Until v1.0.0, minor versions can break.
29
+
30
+ ## Installation
31
+
32
+ ```sh
33
+ pnpm add @seedcord/gateway discord.js
34
+ ```
35
+
36
+ `discord.js`, `envapt`, and `typescript` are peer dependencies. `pnpm create seedcord` sets all of it up.
37
+
38
+ ## What it adds over the core
39
+
40
+ Everything in `@seedcord/core`, plus the parts that use a live connection:
41
+
42
+ - `Seedcord`, the class that connects and holds the discord.js client
43
+ - event subscribers, through `Subscriber` and `WebhookLog`
44
+ - `getConfirmation`, which waits on a button press and returns the answer
45
+ - pagination sources bound to the gateway
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import "reflect-metadata";
2
- import { AnyCustomId, AutocompletableNames, BaseReplySender, ChoiceValueOf, EntryFor, FocusedField, GateFitsWith, HasComponentDefs, MatchArms, ModalLike, PageCursor, SingleParams, SlashOptions as SlashOptions$1 } 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, SelectMenuKind, SlashOptionRegistry, Subscriber as Subscriber$1, SubscriptionKey, WebhookLog as WebhookLog$1 } from "@seedcord/core";
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";
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";
@@ -80,10 +80,12 @@ interface GatewayConfig extends Config {
80
80
  /**
81
81
  * The Discord client and its controllers. Access it through `core.bot`.
82
82
  */
83
+ declare const loggerSlot: unique symbol;
83
84
  declare class Bot implements Initializeable, HmrAware {
84
85
  readonly botToken: string;
86
+ private readonly logger;
85
87
  /** @internal */
86
- readonly logger: Logger;
88
+ readonly [loggerSlot]: Logger;
87
89
  private isInitialized;
88
90
  private readonly _client;
89
91
  private readonly interactions?;
@@ -479,6 +481,66 @@ type EventSpec<EventKey extends ValidNonInteractionKeys> = readonly [event: Even
479
481
  */
480
482
  declare function RegisterEvent<const Defs extends readonly EventSpec<ValidNonInteractionKeys>[]>(...defs: Defs): <HandlerCtor extends Constructor<EventHandler<Defs[number][0]>>>(constructor: HandlerCtor) => void;
481
483
  //#endregion
484
+ //#region src/bot/ReplySender.d.ts
485
+ type SentMessage = Message;
486
+ /** Writes interaction responses through discord.js. The repliable handler bases and the dispatcher construct it. */
487
+ declare class ReplySender extends BaseReplySender<SentMessage, GatewayFile> {
488
+ private readonly interaction;
489
+ constructor(interaction: Repliables, routeId: string, bus: Bus);
490
+ protected writeReply(response: GatewayReplyResponse | string, opts?: SendOpts): Promise<SentMessage | undefined>;
491
+ protected writeDefer(opts?: DeferOpts): Promise<void>;
492
+ protected writeDeferUpdate(): Promise<void>;
493
+ protected writeUpdate(response: GatewayReplyResponse | string): Promise<SentMessage | undefined>;
494
+ protected writeFollowUp(response: GatewayReplyResponse | string, opts?: SendOpts): Promise<SentMessage>;
495
+ protected writeEditOriginal(response: GatewayReplyResponse | string): Promise<SentMessage>;
496
+ protected writeEditTarget(targetId: string, response: GatewayReplyResponse | string): Promise<SentMessage>;
497
+ protected writeDeleteOriginal(): Promise<void>;
498
+ protected writeDeleteTarget(targetId: string): Promise<void>;
499
+ protected writeModal(data: APIModalInteractionResponseCallbackData): Promise<void>;
500
+ protected guardModalSource(): void;
501
+ private rejectModalSubmit;
502
+ private sourceInteraction;
503
+ private replyOptions;
504
+ private editOptions;
505
+ private updateOptions;
506
+ private createdMessage;
507
+ }
508
+ //#endregion
509
+ //#region src/handlers/RepliableHandler.d.ts
510
+ /**
511
+ * Shared base the repliable interaction handlers extend.
512
+ *
513
+ * Not a public entry point. Extend {@link SlashHandler}, {@link ButtonHandler}, {@link ModalHandler},
514
+ * {@link SelectMenuHandler}, or {@link ContextMenuHandler} instead. This class binds the core reply base
515
+ * to the gateway sender.
516
+ *
517
+ * @typeParam Event - The repliable interaction type this handler processes
518
+ */
519
+ declare abstract class RepliableHandler<Event extends Repliables> extends RepliableHandler$1<Event, Core, SentMessage, GatewayFile, ReplySender> {
520
+ /** @internal */
521
+ getEvent(): Event;
522
+ protected buildSender(event: Event, core: Core, routeId: string): ReplySender;
523
+ }
524
+ //#endregion
525
+ //#region src/handlers/interaction/InteractionHandler.d.ts
526
+ /**
527
+ * Shared base the typed interaction handlers extend.
528
+ *
529
+ * Not a public entry point. Extend {@link SlashHandler}, {@link ButtonHandler}, {@link ModalHandler},
530
+ * or {@link SelectMenuHandler} instead. This class adds `showModal` on top of the reply members those bases
531
+ * share.
532
+ *
533
+ * @typeParam Repliable - The interaction type this handler processes
534
+ */
535
+ declare abstract class InteractionHandler<Repliable extends Repliables> extends RepliableHandler<Repliable> {
536
+ constructor(event: Repliable, core: Core, dispatch?: DispatchContext);
537
+ /**
538
+ * Open a modal. Must be the initial response to this interaction. The modal kind rejects this call at
539
+ * compile time (Discord forbids a modal in response to a modal).
540
+ */
541
+ protected showModal(this: InteractionHandler<NonModalInteraction>, modal: ModalLike): Promise<void>;
542
+ }
543
+ //#endregion
482
544
  //#region src/bot/gates/Gate.d.ts
483
545
  /**
484
546
  * The interaction arm. It extends {@link GuildPermissionsContext} with the live interaction and
@@ -566,66 +628,6 @@ interface EventGateContext<Names extends ValidNonInteractionKeys = ValidNonInter
566
628
  */
567
629
  type GateContext = InteractionGateContext | EventGateContext;
568
630
  //#endregion
569
- //#region src/bot/ReplySender.d.ts
570
- type SentMessage = Message;
571
- /** Writes interaction responses through discord.js. The repliable handler bases and the dispatcher construct it. */
572
- declare class ReplySender extends BaseReplySender<SentMessage, GatewayFile> {
573
- private readonly interaction;
574
- constructor(interaction: Repliables, routeId: string, bus: Bus);
575
- protected writeReply(response: GatewayReplyResponse | string, opts?: SendOpts): Promise<SentMessage | undefined>;
576
- protected writeDefer(opts?: DeferOpts): Promise<void>;
577
- protected writeDeferUpdate(): Promise<void>;
578
- protected writeUpdate(response: GatewayReplyResponse | string): Promise<SentMessage | undefined>;
579
- protected writeFollowUp(response: GatewayReplyResponse | string, opts?: SendOpts): Promise<SentMessage>;
580
- protected writeEditOriginal(response: GatewayReplyResponse | string): Promise<SentMessage>;
581
- protected writeEditTarget(targetId: string, response: GatewayReplyResponse | string): Promise<SentMessage>;
582
- protected writeDeleteOriginal(): Promise<void>;
583
- protected writeDeleteTarget(targetId: string): Promise<void>;
584
- protected writeModal(data: APIModalInteractionResponseCallbackData): Promise<void>;
585
- protected guardModalSource(): void;
586
- private rejectModalSubmit;
587
- private sourceInteraction;
588
- private replyOptions;
589
- private editOptions;
590
- private updateOptions;
591
- private createdMessage;
592
- }
593
- //#endregion
594
- //#region src/handlers/RepliableHandler.d.ts
595
- /**
596
- * Shared base the repliable interaction handlers extend.
597
- *
598
- * Not a public entry point. Extend {@link SlashHandler}, {@link ButtonHandler}, {@link ModalHandler},
599
- * {@link SelectMenuHandler}, or {@link ContextMenuHandler} instead. This class binds the core reply base
600
- * to the gateway sender.
601
- *
602
- * @typeParam Event - The repliable interaction type this handler processes
603
- */
604
- declare abstract class RepliableHandler<Event extends Repliables> extends RepliableHandler$1<Event, Core, SentMessage, GatewayFile, ReplySender> {
605
- /** @internal */
606
- getEvent(): Event;
607
- protected buildSender(event: Event, core: Core, routeId: string): ReplySender;
608
- }
609
- //#endregion
610
- //#region src/handlers/interaction/InteractionHandler.d.ts
611
- /**
612
- * Shared base the typed interaction handlers extend.
613
- *
614
- * Not a public entry point. Extend {@link SlashHandler}, {@link ButtonHandler}, {@link ModalHandler},
615
- * or {@link SelectMenuHandler} instead. This class adds `showModal` on top of the reply members those bases
616
- * share.
617
- *
618
- * @typeParam Repliable - The interaction type this handler processes
619
- */
620
- declare abstract class InteractionHandler<Repliable extends Repliables> extends RepliableHandler<Repliable> {
621
- constructor(event: Repliable, core: Core, dispatch?: DispatchContext);
622
- /**
623
- * Open a modal. Must be the initial response to this interaction. The modal kind rejects this call at
624
- * compile time (Discord forbids a modal in response to a modal).
625
- */
626
- protected showModal(this: InteractionHandler<NonModalInteraction>, modal: ModalLike): Promise<void>;
627
- }
628
- //#endregion
629
631
  //#region src/bot/gates/matching.d.ts
630
632
  type AnyHandlerCtor = abstract new (...args: never[]) => {
631
633
  execute(): Promise<void>;
@@ -734,7 +736,7 @@ type SlashMatchArms<Route extends keyof SlashOptionRegistry, Cache extends Cache
734
736
  */
735
737
  declare abstract class SlashHandler<Route extends keyof SlashOptionRegistry, Cache extends CacheType = 'cached'> extends InteractionHandler<ChatInputCommandInteraction<Cache>> {
736
738
  /** @internal */
737
- readonly __slashRoute?: Route;
739
+ readonly [SlashRouteBrand]?: Route;
738
740
  /**
739
741
  * The typed options for this command's route. Required options drop the null, choices narrow to their
740
742
  * literal union, and only the getters for kinds this command actually uses appear. Use `this.event.options`
@@ -795,7 +797,7 @@ type TargetMemberFor<Kind extends ContextMenuKind, Cache extends CacheType> = Ki
795
797
  */
796
798
  declare abstract class ContextMenuHandler<Kind extends ContextMenuKind, Cache extends CacheType = 'cached'> extends InteractionHandler<InteractionFor<Kind, Cache>> {
797
799
  /** @internal */
798
- readonly __ctxKind?: Kind;
800
+ readonly [ContextMenuKindBrand]?: Kind;
799
801
  protected get target(): TargetFor<Kind, Cache>;
800
802
  /**
801
803
  * The invoking guild member, resolved only on user menus. Reading it on a message menu is a compile
@@ -830,7 +832,7 @@ type FocusedArms<Route extends keyof SlashOptionRegistry, Ret> = { [Name in Auto
830
832
  */
831
833
  declare abstract class AutocompleteHandler<Route extends keyof SlashOptionRegistry, Cache extends CacheType = 'cached'> extends BaseHandler<AutocompleteInteraction<Cache>> {
832
834
  /** @internal */
833
- readonly __autocompleteRoute?: Route;
835
+ readonly [AutocompleteRouteBrand]?: Route;
834
836
  constructor(event: AutocompleteInteraction<Cache>, core: Core, dispatch?: DispatchContext);
835
837
  private decodedFocused?;
836
838
  /**
@@ -889,7 +891,7 @@ type ComponentInteraction = ButtonInteraction | ModalSubmitInteraction | AnySele
889
891
  */
890
892
  declare abstract class ComponentHandler<Event extends ComponentInteraction, Defs extends readonly AnyCustomId[]> extends InteractionHandler<Event> implements HasComponentDefs<Defs> {
891
893
  /** @internal */
892
- readonly __componentDefs?: Defs;
894
+ readonly [ComponentDefsBrand]?: Defs;
893
895
  /** Rewrite the source message this component interaction came from. */
894
896
  protected update(response: GatewayReplyResponse | string): Promise<SentMessage>;
895
897
  /** Acknowledge the component without changing the source message. */
@@ -959,7 +961,7 @@ declare abstract class ComponentHandler<Event extends ComponentInteraction, Defs
959
961
  */
960
962
  declare abstract class ButtonHandler<Defs extends readonly AnyCustomId[], Cache extends CacheType = 'cached'> extends ComponentHandler<ButtonInteraction<Cache>, Defs> {
961
963
  /** @internal */
962
- readonly __component?: 'button';
964
+ readonly [ComponentKindBrand]?: 'button';
963
965
  }
964
966
  //#endregion
965
967
  //#region src/handlers/interaction/components/ModalHandler.d.ts
@@ -988,7 +990,7 @@ declare abstract class ButtonHandler<Defs extends readonly AnyCustomId[], Cache
988
990
  */
989
991
  declare abstract class ModalHandler<Defs extends readonly AnyCustomId[], Cache extends CacheType = 'cached'> extends ComponentHandler<ModalSubmitInteraction<Cache>, Defs> {
990
992
  /** @internal */
991
- readonly __component?: 'modal';
993
+ readonly [ComponentKindBrand]?: 'modal';
992
994
  /** Rewrite the message this modal was opened from. Throws when a command opened the modal. */
993
995
  protected update(response: GatewayReplyResponse | string): Promise<SentMessage>;
994
996
  /** Acknowledge the submit without changing the source message. Throws when a command opened the modal. */
@@ -1023,7 +1025,7 @@ type SelectMenuInteractionFor<SelectMenu extends SelectMenuKind, Cache extends C
1023
1025
  */
1024
1026
  declare abstract class SelectMenuHandler<Kind extends SelectMenuKind, Defs extends readonly AnyCustomId[], Cache extends CacheType = 'cached'> extends ComponentHandler<SelectMenuInteractionFor<Kind, Cache>, Defs> {
1025
1027
  /** @internal */
1026
- readonly __component?: Kind;
1028
+ readonly [ComponentKindBrand]?: Kind;
1027
1029
  }
1028
1030
  //#endregion
1029
1031
  //#region src/bot/decorators/Middlewares.d.ts
@@ -1113,11 +1115,20 @@ declare const IgnoreBots: import("@seedcord/core").Gate<EventGateContext<ValidNo
1113
1115
  declare function Nsfw(options?: GateNoticeOptions): Gate<InteractionGateContext<NonModalInteraction>, 'Nsfw'>;
1114
1116
  //#endregion
1115
1117
  //#region src/bot/injectors/EmojiInjector.d.ts
1118
+ type SavedEmojiType = GuildEmoji | ApplicationEmoji;
1119
+ /** A resolved emoji, plus the discord.js emoji it came from on {@link GatewayEmoji.source}. */
1120
+ declare class GatewayEmoji<Source extends SavedEmojiType = SavedEmojiType> extends ResolvedEmoji {
1121
+ #private;
1122
+ constructor(source: Source);
1123
+ /** What discord.js resolved, for `.url` and the management calls. */
1124
+ get source(): Source;
1125
+ }
1116
1126
  /**
1117
- * The global {@link Emojis} accessor type. Each key resolves to its precise class, `GuildEmoji` for a `'guild'`
1118
- * tag and `ApplicationEmoji` for an `'application'` tag, the tags `seedcord codegen` writes into {@link EmojiMap}.
1127
+ * The global {@link Emojis} accessor type. Each key's `source` resolves to its precise class, `GuildEmoji`
1128
+ * for a `'guild'` tag and `ApplicationEmoji` for an `'application'` tag, the tags `seedcord codegen`
1129
+ * writes into {@link EmojiMap}.
1119
1130
  */
1120
- type InjectedEmojiMap = { [K in keyof EmojiMap]: EmojiMap[K] extends 'guild' ? GuildEmoji : ApplicationEmoji; };
1131
+ type InjectedEmojiMap = { [K in keyof EmojiMap]: EmojiMap[K] extends 'guild' ? GatewayEmoji<GuildEmoji> : GatewayEmoji<ApplicationEmoji>; };
1121
1132
  /**
1122
1133
  * The bot's resolved emojis, keyed by {@link EmojiMap}. Filled by {@link EmojiInjector} during startup.
1123
1134
  * A read before that throws.
@@ -1523,5 +1534,5 @@ declare class Seedcord extends Pluggable<'gateway', 'server'> implements Core, S
1523
1534
  /** Package version */
1524
1535
  declare const version: string;
1525
1536
  //#endregion
1526
- 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, 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 };
1537
+ 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 };
1527
1538
  //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs CHANGED
@@ -1,10 +1,10 @@
1
1
  import "reflect-metadata";
2
2
  import { installNodeDefaults } from "@seedcord/logger/node";
3
- import { BaseReplySender, CommandInjector, EventMetadataKey, FaultThrottle, GateNotice, GatedMetadataKey, HmrManager, InteractionMetadataKey, InteractionRouteKeys, InteractionRoutes, MiddlewareMetadataKey, MissingPermissions, NoticeCard, PermissionNames, PublishDefault, accessorStore, areRoutes, asError, clearStore, decodeComponentRoute, deferFlags, guardedAccessor, isEmojiTuple, outcomeFor, pickNotice, prefixOf, queuedMsFor, reportDispatch, reportedWrite, routeIdOf, runHandlerGates, sendFlags, setBotColor, slowGateMonitor } from "@seedcord/core/internal";
4
- import { SeedcordErrorCode } from "@seedcord/errors";
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";
4
+ import { SeedcordErrorCode, paint } from "@seedcord/errors";
5
5
  import { SeedcordError, SeedcordTypeError, validateDiscordToken } from "@seedcord/errors/internal";
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, RowComponent, Silence, Subscriber as Subscriber$1, WebhookLog as WebhookLog$1, assertPermissions, defineGate } from "@seedcord/core";
7
- import { Logger, LoggerChannelRegistry, paint } from "@seedcord/logger";
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
+ import { Logger, LoggerChannelRegistry } from "@seedcord/logger";
8
8
  import { ButtonStyle, Client, ComponentType, DiscordAPIError, Events, Guild, GuildMember, 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";
@@ -13,7 +13,6 @@ import { CommandRegistry, CoordinatedShutdown, CoordinatedStartup, HealthCheck,
13
13
  import { MemoryRateLimiter } from "@seedcord/rate-limiter";
14
14
  import { SeedcordBrand } from "@seedcord/types/internal";
15
15
  import { Envapter } from "envapt";
16
- import chalk from "chalk";
17
16
  import { Envapt } from "envapt/legacy";
18
17
  import { HmrModuleHandler } from "@seedcord/core/hmr";
19
18
  import { formatFilePath, hasKeys } from "@seedcord/utils";
@@ -260,6 +259,18 @@ function Nsfw(options) {
260
259
 
261
260
  //#endregion
262
261
  //#region src/bot/injectors/EmojiInjector.ts
262
+ /** A resolved emoji, plus the discord.js emoji it came from on {@link GatewayEmoji.source}. */
263
+ var GatewayEmoji = class extends ResolvedEmoji {
264
+ #source;
265
+ constructor(source) {
266
+ super(source.name, source.id, source.animated);
267
+ this.#source = source;
268
+ }
269
+ /** What discord.js resolved, for `.url` and the management calls. */
270
+ get source() {
271
+ return this.#source;
272
+ }
273
+ };
263
274
  const emojiStorage = accessorStore();
264
275
  /**
265
276
  * The bot's resolved emojis, keyed by {@link EmojiMap}. Filled by {@link EmojiInjector} during startup.
@@ -304,7 +315,7 @@ var EmojiInjector = class {
304
315
  failures.push(` - "${emojiName}" for "${key}" was not found in guild ${guildId}`);
305
316
  return;
306
317
  }
307
- emojiStorage[key] = guildEmoji;
318
+ emojiStorage[key] = new GatewayEmoji(guildEmoji);
308
319
  }
309
320
  resolveString(key, emojiName, failures) {
310
321
  const appEmoji = this.core.bot.client.application?.emojis.cache.find((e) => e.name === emojiName);
@@ -312,7 +323,7 @@ var EmojiInjector = class {
312
323
  failures.push(` - "${emojiName}" for "${key}" was not found among the application emojis`);
313
324
  return;
314
325
  }
315
- emojiStorage[key] = appEmoji;
326
+ emojiStorage[key] = new GatewayEmoji(appEmoji);
316
327
  }
317
328
  clearEmojis() {
318
329
  clearStore(emojiStorage);
@@ -1350,35 +1361,26 @@ function extractErrorResponse(error, core, origin) {
1350
1361
  response: override ? new override(uuid).render(ctx) : new Fault().render(ctx)
1351
1362
  };
1352
1363
  }
1353
- function withThrottle(core, origin, error, publish) {
1354
- const throttle = FaultThrottle.for(core);
1355
- const key = faultKey(origin, error);
1356
- if (!throttle.shouldReport(key)) {
1357
- logger$2.debug(`throttled duplicate fault ${key}`);
1358
- return;
1359
- }
1360
- publish();
1361
- throttle.markReported(key);
1362
- }
1363
1364
  function reportFault(denial, core, origin, uuid) {
1364
1365
  logger$2.error(`${denial.name}: ${uuid}`, denial);
1365
- withThrottle(core, origin, denial, () => {
1366
- if (origin.interaction) core.bus[PublishDefault]("handledException", {
1367
- denial,
1368
- uuid,
1369
- source: buildInteractionSource(origin.interaction)
1370
- });
1371
- else if (origin.event) core.bus[PublishDefault]("handledException", {
1372
- denial,
1373
- uuid,
1374
- source: buildEventSource(origin.event, origin)
1375
- });
1376
- else core.bus[PublishDefault]("unknownException", {
1377
- uuid,
1378
- error: denial,
1379
- ...scalarActors(origin),
1380
- metadata: metadataFor(origin)
1381
- });
1366
+ if (origin.interaction) core.bus[PublishDefault]("handledException", {
1367
+ denial,
1368
+ uuid,
1369
+ routeId: origin.routeId,
1370
+ source: buildInteractionSource(origin.interaction)
1371
+ });
1372
+ else if (origin.event) core.bus[PublishDefault]("handledException", {
1373
+ denial,
1374
+ uuid,
1375
+ routeId: origin.routeId,
1376
+ source: buildEventSource(origin.event, origin)
1377
+ });
1378
+ else core.bus[PublishDefault]("unknownException", {
1379
+ uuid,
1380
+ error: denial,
1381
+ routeId: origin.routeId,
1382
+ ...scalarActors(origin),
1383
+ metadata: metadataFor(origin)
1382
1384
  });
1383
1385
  }
1384
1386
  function causeLine(error) {
@@ -1390,13 +1392,12 @@ function causeLine(error) {
1390
1392
  function reportRawFault(error, core, origin, uuid) {
1391
1393
  if (core.config.errors?.errorStack ?? false) logger$2.error(uuid, error);
1392
1394
  else logger$2.error(`${uuid} | ${error.message}${causeLine(error)}`);
1393
- withThrottle(core, origin, error, () => {
1394
- core.bus[PublishDefault]("unknownException", {
1395
- uuid,
1396
- error,
1397
- ...scalarActors(origin),
1398
- metadata: metadataFor(origin)
1399
- });
1395
+ core.bus[PublishDefault]("unknownException", {
1396
+ uuid,
1397
+ error,
1398
+ routeId: origin.routeId,
1399
+ ...scalarActors(origin),
1400
+ metadata: metadataFor(origin)
1400
1401
  });
1401
1402
  }
1402
1403
  function scalarActors(origin) {
@@ -1430,19 +1431,6 @@ function buildEventSource(event, origin) {
1430
1431
  raw: event.args
1431
1432
  };
1432
1433
  }
1433
- function faultKey(origin, error) {
1434
- const name = error instanceof Notice ? error.name : error instanceof DiscordAPIError ? String(error.code) : error.constructor.name;
1435
- if (origin.event) return `${origin.event.name}:${origin.event.handler}:${name}`;
1436
- if (origin.interaction) return `${interactionRoute(origin.interaction)}:${name}`;
1437
- if (origin.route) return `autocomplete:${origin.route}:${name}`;
1438
- return `autocomplete:${name}`;
1439
- }
1440
- function interactionRoute(interaction) {
1441
- if (interaction.isChatInputCommand()) return slashRouteOf(interaction);
1442
- if (interaction.isContextMenuCommand()) return interaction.commandName;
1443
- if (interaction.isButton() || interaction.isAnySelectMenu() || interaction.isModalSubmit()) return prefixOf(interaction.customId) || interaction.customId;
1444
- return "interaction";
1445
- }
1446
1434
  function buildInteractionSource(interaction) {
1447
1435
  const command = interaction.isChatInputCommand() ? slashRouteOf(interaction) : interaction.isContextMenuCommand() ? interaction.commandName : null;
1448
1436
  const customId = interaction.isButton() || interaction.isAnySelectMenu() || interaction.isModalSubmit() ? interaction.customId : null;
@@ -1489,6 +1477,7 @@ function handleEventFault(caught, eventName, handlerName, args, core) {
1489
1477
  args,
1490
1478
  channelId: actor.channelId
1491
1479
  },
1480
+ routeId: `event:${eventName}:${handlerName}`,
1492
1481
  guild: actor.guild,
1493
1482
  user: actor.user
1494
1483
  });
@@ -1672,7 +1661,7 @@ var EventDispatcher = class {
1672
1661
  if (this.attachedEvents.has(eventName)) return;
1673
1662
  this.attachedEvents.add(eventName);
1674
1663
  const handlerEntries = this.eventMap.get(eventName);
1675
- this.logger.debug(`Attaching ${paint.mint.bold(eventName)} to the client with ${paint.mute(handlerEntries?.length ?? 0)} handler(s)`);
1664
+ this.logger.debug(`Attaching ${paint.sky.bold(eventName)} to the client with ${paint.mute(handlerEntries?.length ?? 0)} handler(s)`);
1676
1665
  this.core.bot.client.on(eventName, (...args) => {
1677
1666
  if (this.draining) return;
1678
1667
  this.core.bus[PublishDefault]("anyEvent", {
@@ -1710,7 +1699,7 @@ var EventDispatcher = class {
1710
1699
  }
1711
1700
  async processHandler(eventName, ctor, args) {
1712
1701
  try {
1713
- this.logger.debug(`Processing ${paint.mint.bold(eventName)} with ${paint.mute(ctor.name)}`);
1702
+ this.logger.debug(`Processing ${paint.sky.bold(eventName)} with ${paint.mute(ctor.name)}`);
1714
1703
  const handler = new ctor(args, this.core, eventName);
1715
1704
  const eventCtx = eventGateContext(eventName, args, this.core);
1716
1705
  await runHandlerGates(ctor, eventCtx);
@@ -1767,16 +1756,17 @@ async function handleInteractionFault(caught, interaction, core, routeId, sender
1767
1756
  }
1768
1757
  if (interaction.isAutocomplete()) {
1769
1758
  extractErrorResponse(error, core, {
1759
+ routeId,
1770
1760
  guild: interaction.guild,
1771
1761
  user: interaction.user,
1772
- metadata: interaction,
1773
- route: slashRouteOf(interaction)
1762
+ metadata: interaction
1774
1763
  });
1775
1764
  await sendEmptyChoices(interaction, core, routeId);
1776
1765
  return;
1777
1766
  }
1778
1767
  const { response } = extractErrorResponse(error, core, {
1779
1768
  interaction,
1769
+ routeId,
1780
1770
  guild: interaction.guild,
1781
1771
  user: interaction.user,
1782
1772
  metadata: interaction
@@ -1867,11 +1857,11 @@ var InteractionDispatcher = class {
1867
1857
  });
1868
1858
  }
1869
1859
  warnUnhandledRoutes(commandLeaves) {
1870
- for (const route of commandLeaves) if (!this.slashMap.has(route)) this.logger.warn(`Slash route ${paint.sky.bold(route)} has no registered ${chalk.bold("@SlashRoute")} handler.`);
1860
+ for (const route of commandLeaves) if (!this.slashMap.has(route)) this.logger.warn(`Slash route ${paint.sky.bold(route)} has no registered ${paint.bold("@SlashRoute")} handler.`);
1871
1861
  }
1872
1862
  warnUnhandledContextMenuRoutes(leaves) {
1873
- for (const name of leaves.user) if (!this.userContextMenuMap.has(name)) this.logger.warn(`User context menu ${paint.sky.bold(name)} has no registered ${chalk.bold("@ContextMenuRoute")} handler.`);
1874
- for (const name of leaves.message) if (!this.messageContextMenuMap.has(name)) this.logger.warn(`Message context menu ${paint.sky.bold(name)} has no registered ${chalk.bold("@ContextMenuRoute")} handler.`);
1863
+ for (const name of leaves.user) if (!this.userContextMenuMap.has(name)) this.logger.warn(`User context menu ${paint.sky.bold(name)} has no registered ${paint.bold("@ContextMenuRoute")} handler.`);
1864
+ for (const name of leaves.message) if (!this.messageContextMenuMap.has(name)) this.logger.warn(`Message context menu ${paint.sky.bold(name)} has no registered ${paint.bold("@ContextMenuRoute")} handler.`);
1875
1865
  }
1876
1866
  getArtifacts(handlerClass) {
1877
1867
  const artifacts = [];
@@ -2083,7 +2073,7 @@ var InteractionDispatcher = class {
2083
2073
  }
2084
2074
  buildHandler(HandlerCtor, interaction, dispatch, key, isFallback) {
2085
2075
  if (isFallback) this.logger.warn(`No handler found for key ${paint.sky.bold(key)}. Falling back to ${HandlerCtor.name}.`);
2086
- this.logger.debug(`Processing ${paint.mint.bold(key)} with ${paint.mute(HandlerCtor.name)}`);
2076
+ this.logger.debug(`Processing ${paint.sky.bold(key)} with ${paint.mute(HandlerCtor.name)}`);
2087
2077
  return new HandlerCtor(interaction, this.core, dispatch);
2088
2078
  }
2089
2079
  async runMiddlewares(interaction) {
@@ -2193,9 +2183,11 @@ const LOGOUT_TIMEOUT_MS = 2e3;
2193
2183
  /**
2194
2184
  * The Discord client and its controllers. Access it through `core.bot`.
2195
2185
  */
2186
+ const loggerSlot = Symbol("seedcord:bot:logger");
2196
2187
  var Bot = class {
2197
- /** @internal */
2198
2188
  logger = new Logger("Bot", { channel: "bot" });
2189
+ /** @internal */
2190
+ [loggerSlot] = this.logger;
2199
2191
  isInitialized = false;
2200
2192
  _client;
2201
2193
  interactions;
@@ -2275,23 +2267,27 @@ var Bot = class {
2275
2267
  this._client.once(Events.ClientReady, () => ready.resolve());
2276
2268
  this._client.login(token);
2277
2269
  await ready.promise;
2278
- this.logger.info(`Logged in as ${chalk.bold.magenta(this._client.user?.username)}!`);
2270
+ this.logger.info(`Logged in as ${paint.sky.bold(this._client.user?.username)}!`);
2279
2271
  return this;
2280
2272
  }
2281
2273
  async logout() {
2282
2274
  await this._client.destroy();
2283
- this.logger.info(chalk.bold.red("Logged out of Discord!"));
2275
+ this.logger.info(paint.coral.bold("Logged out of Discord!"));
2284
2276
  }
2285
2277
  get client() {
2286
2278
  return this._client;
2287
2279
  }
2288
2280
  };
2289
2281
  __decorate([Envapt("DISCORD_BOT_TOKEN", { converter: (raw) => validateDiscordToken(raw) })], Bot.prototype, "botToken", void 0);
2282
+ /** @internal */
2283
+ function botLoggerOf(bot) {
2284
+ return bot[loggerSlot];
2285
+ }
2290
2286
 
2291
2287
  //#endregion
2292
2288
  //#region src/version.ts
2293
2289
  /** Package version */
2294
- const version = "0.1.4";
2290
+ const version = "0.2.0-next.0";
2295
2291
 
2296
2292
  //#endregion
2297
2293
  //#region src/Seedcord.ts
@@ -2348,14 +2344,14 @@ var Seedcord = class Seedcord extends Pluggable {
2348
2344
  registerStartupTasks() {
2349
2345
  if (Envapter.isDevelopment || Envapter.isTest) this.registerHmrAwareModules();
2350
2346
  this.startup.addTask(StartupPhase.Configuration, "bus-initialization", async () => {
2351
- this.bus.logger.utils.initialization("Subscribers", "start");
2347
+ busLoggerOf(this.bus).utils.initialization("Subscribers", "start");
2352
2348
  await this.subscribers.init();
2353
- this.bus.logger.utils.initialization("Subscribers", "end");
2349
+ busLoggerOf(this.bus).utils.initialization("Subscribers", "end");
2354
2350
  });
2355
2351
  this.startup.addTask(StartupPhase.Login, "bot-initialization", async () => {
2356
- this.bot.logger.utils.initialization("Bot", "start");
2352
+ botLoggerOf(this.bot).utils.initialization("Bot", "start");
2357
2353
  await this.bot.init();
2358
- this.bot.logger.utils.initialization("Bot", "end");
2354
+ botLoggerOf(this.bot).utils.initialization("Bot", "end");
2359
2355
  });
2360
2356
  const { healthCheck } = this;
2361
2357
  if (healthCheck) this.startup.addTask(StartupPhase.Ready, "health-check", async () => {
@@ -2390,5 +2386,5 @@ var Seedcord = class Seedcord extends Pluggable {
2390
2386
  };
2391
2387
 
2392
2388
  //#endregion
2393
- export { ArraySource, AutocompleteHandler, BaseHandler, ButtonHandler, ComponentHandler, ContextMenuHandler, CursorSource, Emojis, EventHandler, EventMiddleware, Gated, IgnoreBots, InteractionHandler, InteractionMiddleware, Middleware, MiddlewareType, ModalHandler, Nsfw, Paginator, Plugin, RegisterEvent, RepliableHandler, Seedcord, SelectMenuHandler, SlashHandler, Subscriber, WebhookLog, checkBotPermissions, checkPermissions, getConfirmation, hasPermsToAssign, version };
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 };
2394
2390
  //# sourceMappingURL=index.mjs.map