spearkit 0.1.0 → 0.2.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.ts CHANGED
@@ -1,7 +1,219 @@
1
1
  import * as discord_js from 'discord.js';
2
- import { RepliableInteraction, InteractionReplyOptions, InteractionResponse, Message, AutocompleteInteraction, ChatInputCommandInteraction, JSONEncodable, APIModalInteractionResponseCallbackData, ModalComponentData, ModalBuilder, APIApplicationCommandChannelOption, CommandInteractionOption, Attachment, ApplicationCommandOptionType, LocalizationMap, Awaitable, APIApplicationCommandBasicOption, PermissionResolvable, RESTPostAPIChatInputApplicationCommandsJSONBody, RESTPostAPIApplicationCommandsJSONBody, REST, RESTPutAPIApplicationCommandsResult, RESTPutAPIApplicationGuildCommandsResult, ClientEvents, Client, ButtonInteraction, StringSelectMenuInteraction, UserSelectMenuInteraction, RoleSelectMenuInteraction, ChannelSelectMenuInteraction, MentionableSelectMenuInteraction, InteractionUpdateOptions, ModalSubmitInteraction, Interaction, ButtonBuilder, ButtonStyle, ComponentEmojiResolvable, ChannelSelectMenuBuilder, MentionableSelectMenuBuilder, TextInputStyle, RoleSelectMenuBuilder, StringSelectMenuBuilder, SelectMenuComponentOptionData, UserSelectMenuBuilder, ChannelType, MessageActionRowComponentBuilder, ActionRowBuilder, GatewayIntentBits, ClientOptions } from 'discord.js';
2
+ import { RepliableInteraction, InteractionReplyOptions, InteractionResponse, Message, APIApplicationCommandChannelOption, CommandInteractionOption, Attachment, ApplicationCommandOptionType, LocalizationMap, Awaitable, ChatInputCommandInteraction, APIApplicationCommandBasicOption, AutocompleteInteraction, JSONEncodable, APIModalInteractionResponseCallbackData, ModalComponentData, ModalBuilder, PermissionResolvable, RESTPostAPIChatInputApplicationCommandsJSONBody, RESTPostAPIApplicationCommandsJSONBody, REST, RESTPutAPIApplicationCommandsResult, RESTPutAPIApplicationGuildCommandsResult, ClientEvents, Client, ButtonInteraction, ChannelSelectMenuInteraction, StringSelectMenuInteraction, UserSelectMenuInteraction, RoleSelectMenuInteraction, MentionableSelectMenuInteraction, ModalSubmitInteraction, Interaction, MessagePayload, MessageReplyOptions, MessageCreateOptions, GatewayIntentBits, ClientOptions, InteractionUpdateOptions, ButtonBuilder, ButtonStyle, ComponentEmojiResolvable, ChannelSelectMenuBuilder, MentionableSelectMenuBuilder, TextInputStyle, RoleSelectMenuBuilder, StringSelectMenuBuilder, SelectMenuComponentOptionData, UserSelectMenuBuilder, ChannelType, MessageActionRowComponentBuilder, ActionRowBuilder } from 'discord.js';
3
3
  export * from 'discord.js';
4
4
 
5
+ /**
6
+ * A small, dependency-free structured logger used across spearkit so every
7
+ * problem (command/component/event failures, gateway errors, your own code)
8
+ * lands in one consistent, debuggable place.
9
+ *
10
+ * It is intentionally tiny: levels, scopes, structured data and a pluggable
11
+ * sink. No `any`/`unknown` leaks into your code — log metadata is constrained
12
+ * to primitive {@link LogValue}s and an optional {@link Error}.
13
+ */
14
+ /** Severity of a log entry, lowest to highest. */
15
+ type LogLevel = "debug" | "info" | "warn" | "error";
16
+ /** A minimum severity to emit, or `"silent"` to suppress everything. */
17
+ type LogThreshold = LogLevel | "silent";
18
+ /** A primitive metadata value attached to a log entry. */
19
+ type LogValue = string | number | boolean | bigint | null | undefined;
20
+ /** Extra context passed alongside a log message. */
21
+ interface LogOptions {
22
+ /** An error to attach; the default sink renders its stack. */
23
+ error?: Error;
24
+ /** Structured key/value metadata. */
25
+ data?: Record<string, LogValue>;
26
+ }
27
+ /** A fully-resolved record handed to a {@link LogSink}. */
28
+ interface LogEntry {
29
+ readonly level: LogLevel;
30
+ readonly message: string;
31
+ readonly scope?: string;
32
+ readonly timestamp: Date;
33
+ readonly error?: Error;
34
+ readonly data?: Readonly<Record<string, LogValue>>;
35
+ }
36
+ /** Receives every entry at or above the configured threshold. */
37
+ type LogSink = (entry: LogEntry) => void;
38
+ /** Construction options for a {@link Logger}. */
39
+ interface LoggerOptions {
40
+ /** Minimum level to emit. Default `"info"`. */
41
+ level?: LogThreshold;
42
+ /** Where entries go. Default {@link consoleSink}. */
43
+ sink?: LogSink;
44
+ /** A scope prefix for every entry (e.g. `"commands"`). */
45
+ scope?: string;
46
+ }
47
+ /** Default sink: human-readable lines to the console (stderr for warn/error). */
48
+ declare function consoleSink(entry: LogEntry): void;
49
+ /**
50
+ * A leveled, scoped logger. Create one directly or read `client.logger`.
51
+ * {@link child} loggers share the parent's threshold and sink, so calling
52
+ * {@link setLevel} on any of them affects the whole tree.
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * const log = new Logger({ level: "debug" });
57
+ * log.info("ready", { data: { shard: 0 } });
58
+ * log.child("commands").error("handler failed", { error });
59
+ * ```
60
+ */
61
+ declare class Logger {
62
+ private state;
63
+ /** The scope prefix applied to every entry, if any. */
64
+ readonly scope?: string;
65
+ constructor(options?: LoggerOptions);
66
+ /** The current minimum threshold. */
67
+ get level(): LogThreshold;
68
+ /** Change the threshold for this logger and every child sharing its state. */
69
+ setLevel(level: LogThreshold): this;
70
+ /** Whether an entry of `level` would currently be emitted. */
71
+ enabled(level: LogLevel): boolean;
72
+ /** A child logger with an extra scope segment, sharing this logger's state. */
73
+ child(scope: string): Logger;
74
+ /** Emit an entry at an explicit level. */
75
+ log(level: LogLevel, message: string, options?: LogOptions): void;
76
+ /** Verbose diagnostics, off by default. */
77
+ debug(message: string, options?: LogOptions): void;
78
+ /** Normal operational messages. */
79
+ info(message: string, options?: LogOptions): void;
80
+ /** Recoverable problems worth attention. */
81
+ warn(message: string, options?: LogOptions): void;
82
+ /** Failures. Attach the cause via `{ error }`. */
83
+ error(message: string, options?: LogOptions): void;
84
+ }
85
+ /** Coerce an unknown thrown value into an {@link Error}. */
86
+ declare function toError(value: unknown): Error;
87
+
88
+ /** The flat key/value map parsed from a `.env` file. */
89
+ type ParsedEnv = Record<string, string>;
90
+ /** Options for {@link loadEnv}. */
91
+ interface LoadEnvOptions {
92
+ /** File to read. Default `.env` in the current working directory. */
93
+ path?: string;
94
+ /** Overwrite variables already present in `process.env`. Default `false`. */
95
+ override?: boolean;
96
+ }
97
+ /** Parse `.env`-formatted text into a flat object. Does not touch `process.env`. */
98
+ declare function parseEnv(content: string): ParsedEnv;
99
+ /**
100
+ * Read a `.env` file and merge it into `process.env`. Existing variables win
101
+ * unless `override` is set. Missing files are ignored (returns `{}`), so it is
102
+ * safe to call unconditionally.
103
+ *
104
+ * @returns the parsed key/value pairs from the file.
105
+ */
106
+ declare function loadEnv(options?: LoadEnvOptions): ParsedEnv;
107
+ /** Typed, ergonomic reader over `process.env`. */
108
+ interface EnvReader {
109
+ /** A string value (empty strings count as missing), or `undefined`/`fallback`. */
110
+ string(key: string): string | undefined;
111
+ string(key: string, fallback: string): string;
112
+ /** A numeric value, or `undefined`/`fallback` when missing or non-numeric. */
113
+ number(key: string): number | undefined;
114
+ number(key: string, fallback: number): number;
115
+ /** A boolean (`true/1/yes/on` vs `false/0/no/off`), or `undefined`/`fallback`. */
116
+ boolean(key: string): boolean | undefined;
117
+ boolean(key: string, fallback: boolean): boolean;
118
+ /** A string value, throwing if the variable is missing or empty. */
119
+ require(key: string): string;
120
+ }
121
+ /**
122
+ * Typed accessor over `process.env`.
123
+ *
124
+ * @example
125
+ * ```ts
126
+ * loadEnv();
127
+ * const token = env.require("DISCORD_TOKEN");
128
+ * const port = env.number("PORT", 3000);
129
+ * const debug = env.boolean("DEBUG", false);
130
+ * ```
131
+ */
132
+ declare const env: EnvReader;
133
+
134
+ /**
135
+ * Rate-limit commands per user, per role, per guild, per channel or globally.
136
+ *
137
+ * A cooldown is described by a {@link CooldownConfig}: a base `duration`, the
138
+ * `scope` it is keyed on, an `exempt` set (users/roles that never wait) and
139
+ * per-user / per-role `overrides` (different durations for specific ids). Set a
140
+ * default on the client (applies to every command) and/or per command.
141
+ */
142
+ /** What a cooldown is bucketed against. Default `"user"`. */
143
+ type CooldownScope = "user" | "guild" | "channel" | "global";
144
+ /** Users and roles that bypass a cooldown entirely. */
145
+ interface CooldownExemptions {
146
+ /** User ids that never wait. */
147
+ users?: readonly string[];
148
+ /** Role ids whose members never wait. */
149
+ roles?: readonly string[];
150
+ }
151
+ /** Per-user and per-role duration overrides (milliseconds; `0` disables). */
152
+ interface CooldownOverrides {
153
+ /** `userId -> duration ms`. */
154
+ users?: Readonly<Record<string, number>>;
155
+ /** `roleId -> duration ms`. The most lenient matching role wins. */
156
+ roles?: Readonly<Record<string, number>>;
157
+ }
158
+ /** Full cooldown description. */
159
+ interface CooldownConfig {
160
+ /** Base cooldown in milliseconds. */
161
+ duration: number;
162
+ /** What the cooldown is keyed on. Default `"user"`. */
163
+ scope?: CooldownScope;
164
+ /** Users/roles that bypass the cooldown. */
165
+ exempt?: CooldownExemptions;
166
+ /** Per-user / per-role duration overrides. */
167
+ overrides?: CooldownOverrides;
168
+ /** Message shown when blocked. A function receives the remaining ms. */
169
+ message?: string | ((remainingMs: number) => string);
170
+ }
171
+ /** A `CooldownConfig`, or a bare duration in milliseconds. */
172
+ type CooldownInput = number | CooldownConfig;
173
+ /** Normalise a {@link CooldownInput} to a full {@link CooldownConfig}. */
174
+ declare function normalizeCooldown(input: CooldownInput): CooldownConfig;
175
+ /** The actor a cooldown is evaluated for. */
176
+ interface CooldownActor {
177
+ userId: string;
178
+ roleIds: readonly string[];
179
+ guildId: string | null;
180
+ channelId: string | null;
181
+ }
182
+ /** Whether an action is allowed now, and if not, how long remains. */
183
+ type CooldownResult = {
184
+ allowed: true;
185
+ } | {
186
+ allowed: false;
187
+ remaining: number;
188
+ };
189
+ /**
190
+ * Resolve the cooldown an actor should serve. `null` means exempt (no
191
+ * cooldown). Otherwise a duration in milliseconds (which may be `0`).
192
+ */
193
+ declare function effectiveDuration(config: CooldownConfig, actor: CooldownActor): number | null;
194
+ /**
195
+ * Tracks last-use timestamps and decides whether an action is allowed.
196
+ * Stateful but dependency-free; one instance is shared on `client.cooldowns`.
197
+ */
198
+ declare class CooldownManager {
199
+ private readonly hits;
200
+ /** Number of tracked buckets. */
201
+ get size(): number;
202
+ /**
203
+ * Check whether `actor` may use `bucket`, recording the use when allowed.
204
+ * Exempt actors and non-positive durations are always allowed (no record).
205
+ */
206
+ consume(bucket: string, input: CooldownInput, actor: CooldownActor, now?: number): CooldownResult;
207
+ /** Like {@link consume} but never records — a read-only check. */
208
+ peek(bucket: string, input: CooldownInput, actor: CooldownActor, now?: number): CooldownResult;
209
+ /** Clear a single actor's cooldown for a bucket. Returns whether one existed. */
210
+ reset(bucket: string, actor: CooldownActor, scope?: CooldownScope): boolean;
211
+ /** Drop every tracked cooldown. */
212
+ clear(): void;
213
+ }
214
+ /** Build the user-facing message for a blocked action. */
215
+ declare function formatCooldownMessage(config: CooldownConfig, remainingMs: number): string;
216
+
5
217
  /** Reply options with an ergonomic `ephemeral` shortcut (mapped to flags). */
6
218
  type ReplyData = InteractionReplyOptions & {
7
219
  ephemeral?: boolean;
@@ -54,42 +266,6 @@ declare abstract class BaseContext<I extends RepliableInteraction = RepliableInt
54
266
  error(message: string): Promise<void>;
55
267
  }
56
268
 
57
- /**
58
- * The handler argument for a slash command. Wraps the discord.js interaction
59
- * and exposes the resolved, fully-typed {@link options}.
60
- */
61
- declare class CommandContext<O extends OptionMap = OptionMap> extends BaseContext<ChatInputCommandInteraction> {
62
- /** Resolved option values, typed from the command's `options` map. */
63
- readonly options: ResolvedOptions<O>;
64
- constructor(interaction: ChatInputCommandInteraction,
65
- /** Resolved option values, typed from the command's `options` map. */
66
- options: ResolvedOptions<O>);
67
- get commandName(): string;
68
- /** The invoked subcommand name, if any. */
69
- get subcommand(): string | null;
70
- /** Present a modal to the user in response to this command. */
71
- showModal(modal: JSONEncodable<APIModalInteractionResponseCallbackData> | ModalComponentData | ModalBuilder): Promise<void>;
72
- }
73
- /**
74
- * The handler argument for autocomplete requests. Provides the focused value
75
- * and a typed {@link respond} helper.
76
- */
77
- declare class AutocompleteContext {
78
- readonly interaction: AutocompleteInteraction;
79
- constructor(interaction: AutocompleteInteraction);
80
- get client(): discord_js.Client<true>;
81
- get user(): discord_js.User;
82
- get guild(): discord_js.Guild | null;
83
- get guildId(): string | null;
84
- get commandName(): string;
85
- /** Name of the option currently being completed. */
86
- get focusedName(): string;
87
- /** Current partial value typed by the user. */
88
- get value(): string;
89
- /** Send autocomplete suggestions (capped at the discord limit of 25). */
90
- respond(choices: OptionChoice<string | number>[]): Promise<void>;
91
- }
92
-
93
269
  /**
94
270
  * Resolved runtime value types, derived directly from discord.js' option
95
271
  * resolver so spearkit stays exactly in lockstep with the underlying getters.
@@ -202,6 +378,42 @@ declare function readOption(resolver: OptionReader, name: string, def: AnyOption
202
378
  /** True if any option in the map declares an autocomplete handler. */
203
379
  declare function optionsHaveAutocomplete(options: OptionMap): boolean;
204
380
 
381
+ /**
382
+ * The handler argument for a slash command. Wraps the discord.js interaction
383
+ * and exposes the resolved, fully-typed {@link options}.
384
+ */
385
+ declare class CommandContext<O extends OptionMap = OptionMap> extends BaseContext<ChatInputCommandInteraction> {
386
+ /** Resolved option values, typed from the command's `options` map. */
387
+ readonly options: ResolvedOptions<O>;
388
+ constructor(interaction: ChatInputCommandInteraction,
389
+ /** Resolved option values, typed from the command's `options` map. */
390
+ options: ResolvedOptions<O>);
391
+ get commandName(): string;
392
+ /** The invoked subcommand name, if any. */
393
+ get subcommand(): string | null;
394
+ /** Present a modal to the user in response to this command. */
395
+ showModal(modal: JSONEncodable<APIModalInteractionResponseCallbackData> | ModalComponentData | ModalBuilder): Promise<void>;
396
+ }
397
+ /**
398
+ * The handler argument for autocomplete requests. Provides the focused value
399
+ * and a typed {@link respond} helper.
400
+ */
401
+ declare class AutocompleteContext {
402
+ readonly interaction: AutocompleteInteraction;
403
+ constructor(interaction: AutocompleteInteraction);
404
+ get client(): discord_js.Client<true>;
405
+ get user(): discord_js.User;
406
+ get guild(): discord_js.Guild | null;
407
+ get guildId(): string | null;
408
+ get commandName(): string;
409
+ /** Name of the option currently being completed. */
410
+ get focusedName(): string;
411
+ /** Current partial value typed by the user. */
412
+ get value(): string;
413
+ /** Send autocomplete suggestions (capped at the discord limit of 25). */
414
+ respond(choices: OptionChoice<string | number>[]): Promise<void>;
415
+ }
416
+
205
417
  /** Metadata shared by every kind of command. */
206
418
  interface CommonMeta {
207
419
  /** Permissions a member must have by default to see/use the command. */
@@ -212,6 +424,8 @@ interface CommonMeta {
212
424
  guildOnly?: boolean;
213
425
  nameLocalizations?: LocalizationMap;
214
426
  descriptionLocalizations?: LocalizationMap;
427
+ /** Rate-limit this command. A number is a duration in ms; see {@link CooldownConfig}. */
428
+ cooldown?: CooldownInput;
215
429
  }
216
430
  /** Configuration for a leaf (non-subcommand) slash command. */
217
431
  interface CommandConfig<O extends OptionMap, R> extends CommonMeta {
@@ -264,6 +478,7 @@ interface SlashCommandSpec {
264
478
  hasAutocomplete: boolean;
265
479
  executor: (interaction: ChatInputCommandInteraction) => Promise<void>;
266
480
  autocompleter: (interaction: AutocompleteInteraction) => Promise<void>;
481
+ cooldown?: CooldownConfig;
267
482
  }
268
483
  /**
269
484
  * A registered slash command. Serialises itself for the discord REST API and
@@ -278,6 +493,8 @@ declare class SlashCommand {
278
493
  private readonly json;
279
494
  private readonly executor;
280
495
  private readonly autocompleter;
496
+ /** Resolved cooldown configuration for this command, if any. */
497
+ readonly cooldown?: CooldownConfig;
281
498
  /** @internal */
282
499
  constructor(spec: SlashCommandSpec);
283
500
  /** Serialise to the discord REST chat-input command payload. */
@@ -308,6 +525,88 @@ declare function subcommandGroup(config: SubcommandGroupConfig): SubcommandGroup
308
525
  /** Define a command that routes to subcommands and/or subcommand groups. */
309
526
  declare function commandGroup(config: CommandGroupConfig): SlashCommand;
310
527
 
528
+ /** What kind of interaction was used. */
529
+ type UsageType = "command" | "prefix" | "component" | "event";
530
+ /** A single recorded use. */
531
+ interface UsageEvent {
532
+ readonly type: UsageType;
533
+ /** Command/component name (or event name). */
534
+ readonly name: string;
535
+ readonly userId?: string;
536
+ readonly userTag?: string;
537
+ readonly guildId?: string | null;
538
+ readonly channelId?: string | null;
539
+ /** Free-form extra detail. */
540
+ readonly detail?: string;
541
+ readonly timestamp: Date;
542
+ }
543
+ /** A pluggable persistence backend for {@link UsageEvent}s. */
544
+ interface UsageStore {
545
+ /** Persist one event. */
546
+ record(event: UsageEvent): Awaitable<void>;
547
+ /** Read every persisted event. */
548
+ all(): Awaitable<readonly UsageEvent[]>;
549
+ }
550
+ /** In-memory store; great for tests and dashboards. Optionally capped. */
551
+ declare class MemoryUsageStore implements UsageStore {
552
+ private readonly limit;
553
+ private readonly events;
554
+ constructor(limit?: number);
555
+ record(event: UsageEvent): void;
556
+ all(): readonly UsageEvent[];
557
+ /** Total recorded events. */
558
+ get size(): number;
559
+ /** Events recorded for a given user id. */
560
+ byUser(userId: string): UsageEvent[];
561
+ /** Forget everything. */
562
+ clear(): void;
563
+ }
564
+ /**
565
+ * File-backed store using newline-delimited JSON (`.jsonl`). Appends one line
566
+ * per event — durable, human-inspectable, and dependency-free.
567
+ */
568
+ declare class JsonFileUsageStore implements UsageStore {
569
+ private readonly path;
570
+ constructor(path: string);
571
+ record(event: UsageEvent): Promise<void>;
572
+ all(): Promise<readonly UsageEvent[]>;
573
+ }
574
+ /** Default one-line rendering of a usage event for a Discord channel. */
575
+ declare function formatUsage(event: UsageEvent): string;
576
+ /** Client-level usage configuration (the `usage` option). */
577
+ interface UsageOptions {
578
+ /** Persist events to this store (a database). */
579
+ store?: UsageStore;
580
+ /** Mirror events into this Discord channel id. */
581
+ channel?: string;
582
+ /** Custom channel-line formatter. */
583
+ format?: (event: UsageEvent) => string;
584
+ }
585
+ /**
586
+ * Routes each {@link UsageEvent} to a store and/or a Discord channel. The
587
+ * client owns one as `client.usage`. Tracking is fire-and-forget: a slow store
588
+ * or channel never blocks command handling, and failures are logged.
589
+ */
590
+ declare class UsageTracker {
591
+ /** The configured store, if any. Directly queryable. */
592
+ store?: UsageStore;
593
+ private reporter?;
594
+ private client?;
595
+ private logger?;
596
+ /** Whether anything will happen on {@link track}. */
597
+ get enabled(): boolean;
598
+ /** @internal Used by the client to resolve report channels. */
599
+ setClient(client: SpearClient): this;
600
+ setLogger(logger: Logger): this;
601
+ /** Persist events to a store (a database). */
602
+ setStore(store: UsageStore): this;
603
+ /** Mirror events into a Discord channel. */
604
+ reportTo(channelId: string, format?: (event: UsageEvent) => string): this;
605
+ /** Record a use. Returns immediately; storing/reporting happen in the background. */
606
+ track(event: UsageEvent): void;
607
+ private run;
608
+ }
609
+
311
610
  /** Error hook invoked when a command handler throws. */
312
611
  type CommandErrorHandler = (error: Error, interaction: ChatInputCommandInteraction) => Awaitable<void>;
313
612
  /** Options for pushing commands to discord. */
@@ -327,6 +626,10 @@ type DeployResult = RESTPutAPIApplicationCommandsResult | RESTPutAPIApplicationG
327
626
  declare class CommandRegistry {
328
627
  private readonly commands;
329
628
  private errorHandler?;
629
+ private logger?;
630
+ private cooldowns?;
631
+ private defaultCooldown?;
632
+ private onUsage?;
330
633
  /** Register one or more commands. Later registrations override by name. */
331
634
  add(...commands: SlashCommand[]): this;
332
635
  /** Remove a command by name. */
@@ -341,6 +644,12 @@ declare class CommandRegistry {
341
644
  get size(): number;
342
645
  /** Set the handler used when a command throws. */
343
646
  onError(handler: CommandErrorHandler): this;
647
+ /** Attach a logger used for dispatch tracing (debug level). */
648
+ setLogger(logger: Logger): this;
649
+ /** Wire a shared cooldown manager and an optional default cooldown for every command. */
650
+ setCooldowns(manager: CooldownManager, defaultCooldown?: CooldownConfig): this;
651
+ /** Attach a hook called after each successful command execution. */
652
+ setUsageHook(hook: (event: UsageEvent) => void): this;
344
653
  /** Serialise every command to discord REST payloads. */
345
654
  toJSON(): RESTPostAPIApplicationCommandsJSONBody[];
346
655
  /** Dispatch an incoming chat-input interaction to its command. */
@@ -355,6 +664,7 @@ declare class CommandRegistry {
355
664
  */
356
665
  deploy(options: DeployOptions): Promise<DeployResult>;
357
666
  private defaultErrorReply;
667
+ private replyCooldown;
358
668
  }
359
669
 
360
670
  /** A typed handler for a discord.js client event. */
@@ -407,6 +717,411 @@ declare class EventRegistry {
407
717
  detachAll(client: Client): void;
408
718
  }
409
719
 
720
+ /** Shared shape of every routed component. */
721
+ interface RouteBase {
722
+ readonly namespace: string;
723
+ readonly paramNames: readonly string[];
724
+ }
725
+ /** Routing entry for a button. */
726
+ interface ButtonRoute extends RouteBase {
727
+ readonly kind: "button";
728
+ handle(interaction: ButtonInteraction, params: Record<string, string>): Promise<void>;
729
+ }
730
+ /** Routing entry for a string select. */
731
+ interface StringSelectRoute extends RouteBase {
732
+ readonly kind: "stringSelect";
733
+ handle(interaction: StringSelectMenuInteraction, params: Record<string, string>): Promise<void>;
734
+ }
735
+ /** Routing entry for a user select. */
736
+ interface UserSelectRoute extends RouteBase {
737
+ readonly kind: "userSelect";
738
+ handle(interaction: UserSelectMenuInteraction, params: Record<string, string>): Promise<void>;
739
+ }
740
+ /** Routing entry for a role select. */
741
+ interface RoleSelectRoute extends RouteBase {
742
+ readonly kind: "roleSelect";
743
+ handle(interaction: RoleSelectMenuInteraction, params: Record<string, string>): Promise<void>;
744
+ }
745
+ /** Routing entry for a channel select. */
746
+ interface ChannelSelectRoute extends RouteBase {
747
+ readonly kind: "channelSelect";
748
+ handle(interaction: ChannelSelectMenuInteraction, params: Record<string, string>): Promise<void>;
749
+ }
750
+ /** Routing entry for a mentionable select. */
751
+ interface MentionableSelectRoute extends RouteBase {
752
+ readonly kind: "mentionableSelect";
753
+ handle(interaction: MentionableSelectMenuInteraction, params: Record<string, string>): Promise<void>;
754
+ }
755
+ /** Routing entry for a modal submission. */
756
+ interface ModalRoute extends RouteBase {
757
+ readonly kind: "modal";
758
+ handle(interaction: ModalSubmitInteraction, params: Record<string, string>): Promise<void>;
759
+ }
760
+ /** Any registrable component routing entry. */
761
+ type ComponentDef = ButtonRoute | StringSelectRoute | UserSelectRoute | RoleSelectRoute | ChannelSelectRoute | MentionableSelectRoute | ModalRoute;
762
+ /** Error hook invoked when a component handler throws. */
763
+ type ComponentErrorHandler = (error: Error, interaction: RepliableInteraction) => Awaitable<void>;
764
+ /**
765
+ * Routes button, select and modal interactions to the handlers registered for
766
+ * their custom-id namespace. Decodes the custom-id, extracts typed params, and
767
+ * invokes the matching handler.
768
+ */
769
+ declare class ComponentRegistry {
770
+ private readonly buttons;
771
+ private readonly stringSelects;
772
+ private readonly userSelects;
773
+ private readonly roleSelects;
774
+ private readonly channelSelects;
775
+ private readonly mentionableSelects;
776
+ private readonly modals;
777
+ private errorHandler?;
778
+ private logger?;
779
+ private onUsage?;
780
+ /** Register one or more components. Later registrations override by namespace. */
781
+ add(...defs: ComponentDef[]): this;
782
+ /** Set the handler used when a component throws. */
783
+ onError(handler: ComponentErrorHandler): this;
784
+ /** Attach a logger used for dispatch tracing (debug level). */
785
+ setLogger(logger: Logger): this;
786
+ /** Attach a hook called after each successful component handler run. */
787
+ setUsageHook(hook: (event: UsageEvent) => void): this;
788
+ /** Total number of registered components. */
789
+ get size(): number;
790
+ /**
791
+ * Dispatch an interaction to its component handler. Returns `true` if a
792
+ * handler matched and ran, `false` otherwise.
793
+ */
794
+ handle(interaction: Interaction): Promise<boolean>;
795
+ private exec;
796
+ }
797
+
798
+ /**
799
+ * A spearkit plugin: a named, reusable bundle of commands, events and components.
800
+ * Its {@link setup} runs once when added to a client via `client.use(plugin)`.
801
+ */
802
+ interface SpearPlugin {
803
+ readonly name: string;
804
+ setup(client: SpearClient): Awaitable<void>;
805
+ }
806
+ /** Identity helper that gives a plugin object its type and editor hints. */
807
+ declare function definePlugin(plugin: SpearPlugin): SpearPlugin;
808
+
809
+ /** Options for the directory loader. */
810
+ interface LoadOptions {
811
+ /** File extensions to import. Default: `.js`, `.mjs`, `.cjs`. */
812
+ extensions?: readonly string[];
813
+ /** Recurse into subdirectories. Default: `true`. */
814
+ recursive?: boolean;
815
+ }
816
+ /**
817
+ * Recursively import a directory and collect every spearkit-registrable export
818
+ * (commands, events, components) found in default or named exports.
819
+ */
820
+ declare function collectModules(dir: string, options?: LoadOptions): Promise<Registerable[]>;
821
+ /**
822
+ * Load a directory and register everything it exports into the client.
823
+ * Returns the number of items registered.
824
+ */
825
+ declare function loadInto(client: SpearClient, dir: string, options?: LoadOptions): Promise<number>;
826
+
827
+ /** Options controlling how prefix messages are recognised. */
828
+ interface PrefixOptions {
829
+ /** One or more command prefixes (e.g. `"!"` or `["!", "?"]`). */
830
+ prefix?: string | readonly string[];
831
+ /** Also accept a leading bot mention as a prefix. Default `true`. */
832
+ mention?: boolean;
833
+ /** Ignore messages authored by bots. Default `true`. */
834
+ ignoreBots?: boolean;
835
+ /** Match command names case-insensitively. Default `true`. */
836
+ caseInsensitive?: boolean;
837
+ }
838
+ /** Configuration for a prefix command. */
839
+ interface PrefixCommandConfig<R = void> {
840
+ /** Primary command name (the word after the prefix). */
841
+ name: string;
842
+ /** Alternative names that also trigger the command. */
843
+ aliases?: readonly string[];
844
+ /** Human description (for your own help command). */
845
+ description?: string;
846
+ /** Rate-limit this command. A number is a duration in ms. */
847
+ cooldown?: CooldownInput;
848
+ /** Handler invoked with a {@link PrefixContext}. */
849
+ run: (ctx: PrefixContext) => Awaitable<R>;
850
+ }
851
+ /** A registrable prefix command. Build it with {@link prefixCommand}. */
852
+ interface PrefixCommand {
853
+ readonly kind: "prefixCommand";
854
+ readonly name: string;
855
+ readonly aliases: readonly string[];
856
+ readonly description?: string;
857
+ readonly cooldown?: CooldownConfig;
858
+ readonly run: (ctx: PrefixContext) => Promise<void>;
859
+ }
860
+ /** Define a prefix command. */
861
+ declare function prefixCommand<R = void>(config: PrefixCommandConfig<R>): PrefixCommand;
862
+ /** The handler argument for a prefix command: the message plus parsed args. */
863
+ declare class PrefixContext {
864
+ /** The triggering message. */
865
+ readonly message: Message;
866
+ /** The matched command name (as typed). */
867
+ readonly commandName: string;
868
+ /** Whitespace-split arguments after the command name. */
869
+ readonly args: string[];
870
+ /** The raw text after the command name. */
871
+ readonly rest: string;
872
+ constructor(
873
+ /** The triggering message. */
874
+ message: Message,
875
+ /** The matched command name (as typed). */
876
+ commandName: string,
877
+ /** Whitespace-split arguments after the command name. */
878
+ args: string[],
879
+ /** The raw text after the command name. */
880
+ rest: string);
881
+ get client(): Message["client"];
882
+ get author(): discord_js.User;
883
+ get member(): discord_js.GuildMember | null;
884
+ get guild(): discord_js.Guild | null;
885
+ get guildId(): string | null;
886
+ get channel(): discord_js.DMChannel | discord_js.PartialDMChannel | discord_js.PartialGroupDMChannel | discord_js.NewsChannel | discord_js.StageChannel | discord_js.TextChannel | discord_js.PublicThreadChannel<boolean> | discord_js.PrivateThreadChannel | discord_js.VoiceChannel;
887
+ get channelId(): string;
888
+ /** Reply to the triggering message. */
889
+ reply(content: string | MessagePayload | MessageReplyOptions): Promise<Message>;
890
+ /** Send a message to the same channel (no reply reference). */
891
+ send(content: string | MessagePayload | MessageCreateOptions): Promise<Message | undefined>;
892
+ }
893
+ /** Error hook invoked when a prefix command handler throws. */
894
+ type PrefixErrorHandler = (error: Error, message: Message) => Awaitable<void>;
895
+ /** Holds prefix commands and dispatches matching messages to them. */
896
+ declare class PrefixRegistry {
897
+ private readonly commands;
898
+ private readonly lookup;
899
+ private options;
900
+ private logger?;
901
+ private cooldowns?;
902
+ private defaultCooldown?;
903
+ private errorHandler?;
904
+ private onUsage?;
905
+ /** Configure prefixes and matching behaviour. */
906
+ setOptions(input: string | readonly string[] | PrefixOptions): this;
907
+ /** Attach a logger for dispatch tracing and error reporting. */
908
+ setLogger(logger: Logger): this;
909
+ /** Attach a hook called after each successful prefix command run. */
910
+ setUsageHook(hook: (event: UsageEvent) => void): this;
911
+ /** Share a cooldown manager and an optional default cooldown. */
912
+ setCooldowns(manager: CooldownManager, defaultCooldown?: CooldownConfig): this;
913
+ /** Set the handler used when a prefix command throws. */
914
+ onError(handler: PrefixErrorHandler): this;
915
+ /** Register one or more prefix commands (and their aliases). */
916
+ add(...commands: PrefixCommand[]): this;
917
+ private index;
918
+ /** Look up a command by name or alias. */
919
+ get(nameOrAlias: string): PrefixCommand | undefined;
920
+ /** Number of registered commands (excluding aliases). */
921
+ get size(): number;
922
+ /** Every registered command. */
923
+ list(): PrefixCommand[];
924
+ /** Strip a matching prefix (or bot mention) from `content`, or return `null`. */
925
+ private stripPrefix;
926
+ /**
927
+ * Parse and dispatch a message. Returns `true` when a command ran (or was
928
+ * blocked by a cooldown), `false` when the message was not a prefix command.
929
+ */
930
+ handle(message: Message): Promise<boolean>;
931
+ }
932
+
933
+ /** Anything that can be handed to {@link SpearClient.register}. */
934
+ type Registerable = SlashCommand | EventDef | ComponentDef | ScheduledTask | PrefixCommand;
935
+ /**
936
+ * Ready-made intent presets. Pass one to {@link SpearClient} as `intents`.
937
+ * `all` includes privileged intents — enable them in the developer portal.
938
+ */
939
+ declare const Intents: {
940
+ /** No intents. */
941
+ readonly none: GatewayIntentBits[];
942
+ /** Just `Guilds` — enough for slash commands and interactions. */
943
+ readonly default: readonly [GatewayIntentBits.Guilds];
944
+ /** Guild + member gateway data. */
945
+ readonly guilds: readonly [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers];
946
+ /** Read message content (privileged) alongside guild messages. */
947
+ readonly messages: readonly [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent];
948
+ /** Every intent, including privileged ones. */
949
+ readonly all: GatewayIntentBits[];
950
+ };
951
+ /** spearkit-specific client options layered on top of discord.js {@link ClientOptions}. */
952
+ interface SpearOptions {
953
+ /** A {@link Logger} instance, or options to build one. Exposed as `client.logger`. */
954
+ logger?: Logger | LoggerOptions;
955
+ /**
956
+ * Auto-load a `.env` file into `process.env` on {@link SpearClient.start}.
957
+ * `true` (default) loads `.env` from the cwd; pass {@link LoadEnvOptions} for
958
+ * a custom path or override behaviour, or `false` to disable.
959
+ */
960
+ dotenv?: boolean | LoadEnvOptions;
961
+ /** A default cooldown applied to every command. A command's own cooldown overrides it. */
962
+ cooldown?: CooldownInput;
963
+ /** Enable prefix (text) commands. A string/array sets prefixes; an object configures matching. */
964
+ prefix?: string | readonly string[] | PrefixOptions;
965
+ /** Track command/component/prefix usage to a store and/or a Discord channel. */
966
+ usage?: UsageOptions;
967
+ }
968
+ /** Options for {@link SpearClient}: discord.js options plus {@link SpearOptions}. `intents` may be omitted. */
969
+ type SpearClientOptions = Partial<ClientOptions> & SpearOptions;
970
+ /**
971
+ * A discord.js {@link Client} with batteries included: command, event and
972
+ * component registries plus interaction routing wired up automatically.
973
+ *
974
+ * @example
975
+ * ```ts
976
+ * const client = new SpearClient({ intents: Intents.default });
977
+ * client.register(ping, onReady, voteButton);
978
+ * await client.start(process.env.TOKEN);
979
+ * await client.deployCommands({ guildId: "123" });
980
+ * ```
981
+ */
982
+ declare class SpearClient extends Client {
983
+ /** Slash command registry and dispatcher. */
984
+ readonly commands: CommandRegistry;
985
+ /** Event listener registry. */
986
+ readonly events: EventRegistry;
987
+ /** Button / select / modal registry and router. */
988
+ readonly components: ComponentRegistry;
989
+ /** Structured logger shared across spearkit and available to your code. */
990
+ readonly logger: Logger;
991
+ /** Shared cooldown manager used by command dispatch; also usable directly. */
992
+ readonly cooldowns: CooldownManager;
993
+ /** Cron/interval task scheduler; started on ready and stopped on destroy. */
994
+ readonly scheduler: TaskScheduler;
995
+ /** Prefix (text) command registry, dispatched from `messageCreate`. */
996
+ readonly prefix: PrefixRegistry;
997
+ /** Usage tracker: records who used what to a store and/or a Discord channel. */
998
+ readonly usage: UsageTracker;
999
+ private readonly envConfig;
1000
+ constructor(options?: SpearClientOptions);
1001
+ /**
1002
+ * Register commands, events and components in one call. Each item is routed
1003
+ * to the matching registry based on its kind.
1004
+ */
1005
+ register(...items: Registerable[]): this;
1006
+ /** Install one or more plugins, running each plugin's `setup`. */
1007
+ use(...plugins: SpearPlugin[]): Promise<this>;
1008
+ /**
1009
+ * Recursively load a directory and register every command, event and
1010
+ * component it exports. Returns the number of items registered.
1011
+ */
1012
+ load(dir: string, options?: LoadOptions): Promise<number>;
1013
+ /**
1014
+ * Log in. Falls back to the `DISCORD_TOKEN` environment variable when no
1015
+ * token is passed.
1016
+ */
1017
+ start(token?: string): Promise<this>;
1018
+ /**
1019
+ * Push the registered slash commands to discord using the client's own
1020
+ * authenticated REST connection. Call after the client is ready.
1021
+ */
1022
+ deployCommands(options?: {
1023
+ guildId?: string;
1024
+ }): Promise<DeployResult>;
1025
+ private route;
1026
+ /** Define and register a scheduled task in one call. */
1027
+ schedule(config: TaskConfig): ScheduledTask;
1028
+ /** Stop the scheduler, then tear down the discord.js client. */
1029
+ destroy(): Promise<void>;
1030
+ }
1031
+
1032
+ /**
1033
+ * Scheduled tasks: run work on a cron schedule or a fixed interval.
1034
+ *
1035
+ * Dependency-free. Includes a standard 5-field cron parser (`*`, ranges,
1036
+ * lists, steps, `@daily` style aliases) evaluated in local time, plus a
1037
+ * {@link TaskScheduler} that the client starts on ready and stops on destroy.
1038
+ */
1039
+
1040
+ /**
1041
+ * A parsed cron expression. Evaluates in the host's local time.
1042
+ *
1043
+ * @example
1044
+ * ```ts
1045
+ * cron("*\u200b/5 * * * *").next(); // next 5-minute boundary
1046
+ * cron("@daily").next(new Date()); // next midnight
1047
+ * ```
1048
+ */
1049
+ declare class CronExpression {
1050
+ /** The original expression string. */
1051
+ readonly source: string;
1052
+ private readonly minutes;
1053
+ private readonly hours;
1054
+ private readonly daysOfMonth;
1055
+ private readonly months;
1056
+ private readonly daysOfWeek;
1057
+ private readonly domRestricted;
1058
+ private readonly dowRestricted;
1059
+ constructor(expression: string);
1060
+ private dayMatches;
1061
+ /** The next time strictly after `from` (default now) that matches. */
1062
+ next(from?: Date): Date;
1063
+ }
1064
+ /** Compile a cron expression. Throws on malformed input. */
1065
+ declare function cron(expression: string): CronExpression;
1066
+ /** Configuration for a scheduled task. Provide exactly one of `cron`/`interval`. */
1067
+ interface TaskConfig {
1068
+ /** Unique task name. */
1069
+ name: string;
1070
+ /** A cron expression (local time). */
1071
+ cron?: string;
1072
+ /** A fixed interval in milliseconds. */
1073
+ interval?: number;
1074
+ /** Also run once immediately when the scheduler starts. Default `false`. */
1075
+ runOnStart?: boolean;
1076
+ /** The work to perform. */
1077
+ run: (client: SpearClient) => Awaitable<void>;
1078
+ }
1079
+ /** A compiled, registrable scheduled task. Build it with {@link task}. */
1080
+ interface ScheduledTask {
1081
+ readonly kind: "task";
1082
+ readonly name: string;
1083
+ readonly interval?: number;
1084
+ readonly cron?: CronExpression;
1085
+ readonly runOnStart: boolean;
1086
+ readonly run: (client: SpearClient) => Awaitable<void>;
1087
+ }
1088
+ /** Define a scheduled task. Throws if neither `cron` nor `interval` is given. */
1089
+ declare function task(config: TaskConfig): ScheduledTask;
1090
+ /**
1091
+ * Runs {@link ScheduledTask}s. The client owns one as `client.scheduler`,
1092
+ * starts it on `clientReady` and stops it on `destroy`. Tasks added while
1093
+ * running are scheduled immediately.
1094
+ */
1095
+ declare class TaskScheduler {
1096
+ private readonly tasks;
1097
+ private readonly timers;
1098
+ private running;
1099
+ private client?;
1100
+ private logger?;
1101
+ /** Number of registered tasks. */
1102
+ get size(): number;
1103
+ /** Whether the scheduler is currently running. */
1104
+ get active(): boolean;
1105
+ /** Every registered task. */
1106
+ list(): ScheduledTask[];
1107
+ /** Attach a logger for task error reporting. */
1108
+ setLogger(logger: Logger): this;
1109
+ /** Register one or more tasks. If already running, they are scheduled now. */
1110
+ add(...tasks: ScheduledTask[]): this;
1111
+ /** Remove a task and cancel its timer. */
1112
+ remove(name: string): boolean;
1113
+ /** Start every task. Safe to call once; later calls are ignored. */
1114
+ start(client: SpearClient): void;
1115
+ /** Stop the scheduler and cancel every pending timer. */
1116
+ stop(): void;
1117
+ private cancel;
1118
+ private begin;
1119
+ private delayFor;
1120
+ private scheduleNext;
1121
+ private arm;
1122
+ private runTask;
1123
+ }
1124
+
410
1125
  /**
411
1126
  * Typed custom-id codec.
412
1127
  *
@@ -519,78 +1234,6 @@ declare class ModalContext<P, F extends string = string> extends BaseContext<Mod
519
1234
  get customId(): string;
520
1235
  }
521
1236
 
522
- /** Shared shape of every routed component. */
523
- interface RouteBase {
524
- readonly namespace: string;
525
- readonly paramNames: readonly string[];
526
- }
527
- /** Routing entry for a button. */
528
- interface ButtonRoute extends RouteBase {
529
- readonly kind: "button";
530
- handle(interaction: ButtonInteraction, params: Record<string, string>): Promise<void>;
531
- }
532
- /** Routing entry for a string select. */
533
- interface StringSelectRoute extends RouteBase {
534
- readonly kind: "stringSelect";
535
- handle(interaction: StringSelectMenuInteraction, params: Record<string, string>): Promise<void>;
536
- }
537
- /** Routing entry for a user select. */
538
- interface UserSelectRoute extends RouteBase {
539
- readonly kind: "userSelect";
540
- handle(interaction: UserSelectMenuInteraction, params: Record<string, string>): Promise<void>;
541
- }
542
- /** Routing entry for a role select. */
543
- interface RoleSelectRoute extends RouteBase {
544
- readonly kind: "roleSelect";
545
- handle(interaction: RoleSelectMenuInteraction, params: Record<string, string>): Promise<void>;
546
- }
547
- /** Routing entry for a channel select. */
548
- interface ChannelSelectRoute extends RouteBase {
549
- readonly kind: "channelSelect";
550
- handle(interaction: ChannelSelectMenuInteraction, params: Record<string, string>): Promise<void>;
551
- }
552
- /** Routing entry for a mentionable select. */
553
- interface MentionableSelectRoute extends RouteBase {
554
- readonly kind: "mentionableSelect";
555
- handle(interaction: MentionableSelectMenuInteraction, params: Record<string, string>): Promise<void>;
556
- }
557
- /** Routing entry for a modal submission. */
558
- interface ModalRoute extends RouteBase {
559
- readonly kind: "modal";
560
- handle(interaction: ModalSubmitInteraction, params: Record<string, string>): Promise<void>;
561
- }
562
- /** Any registrable component routing entry. */
563
- type ComponentDef = ButtonRoute | StringSelectRoute | UserSelectRoute | RoleSelectRoute | ChannelSelectRoute | MentionableSelectRoute | ModalRoute;
564
- /** Error hook invoked when a component handler throws. */
565
- type ComponentErrorHandler = (error: Error, interaction: RepliableInteraction) => Awaitable<void>;
566
- /**
567
- * Routes button, select and modal interactions to the handlers registered for
568
- * their custom-id namespace. Decodes the custom-id, extracts typed params, and
569
- * invokes the matching handler.
570
- */
571
- declare class ComponentRegistry {
572
- private readonly buttons;
573
- private readonly stringSelects;
574
- private readonly userSelects;
575
- private readonly roleSelects;
576
- private readonly channelSelects;
577
- private readonly mentionableSelects;
578
- private readonly modals;
579
- private errorHandler?;
580
- /** Register one or more components. Later registrations override by namespace. */
581
- add(...defs: ComponentDef[]): this;
582
- /** Set the handler used when a component throws. */
583
- onError(handler: ComponentErrorHandler): this;
584
- /** Total number of registered components. */
585
- get size(): number;
586
- /**
587
- * Dispatch an interaction to its component handler. Returns `true` if a
588
- * handler matched and ran, `false` otherwise.
589
- */
590
- handle(interaction: Interaction): Promise<boolean>;
591
- private exec;
592
- }
593
-
594
1237
  /** Accepted button styles for an interactive (custom-id) button. */
595
1238
  type ButtonStyleInput = "Primary" | "Secondary" | "Success" | "Danger" | ButtonStyle.Primary | ButtonStyle.Secondary | ButtonStyle.Success | ButtonStyle.Danger;
596
1239
  /** Config for an interactive button created with {@link button}. */
@@ -750,100 +1393,4 @@ declare function modal<const P extends string, F extends Record<string, TextInpu
750
1393
  */
751
1394
  declare function row<C extends MessageActionRowComponentBuilder>(...components: C[]): ActionRowBuilder<C>;
752
1395
 
753
- /**
754
- * A spearkit plugin: a named, reusable bundle of commands, events and components.
755
- * Its {@link setup} runs once when added to a client via `client.use(plugin)`.
756
- */
757
- interface SpearPlugin {
758
- readonly name: string;
759
- setup(client: SpearClient): Awaitable<void>;
760
- }
761
- /** Identity helper that gives a plugin object its type and editor hints. */
762
- declare function definePlugin(plugin: SpearPlugin): SpearPlugin;
763
-
764
- /** Options for the directory loader. */
765
- interface LoadOptions {
766
- /** File extensions to import. Default: `.js`, `.mjs`, `.cjs`. */
767
- extensions?: readonly string[];
768
- /** Recurse into subdirectories. Default: `true`. */
769
- recursive?: boolean;
770
- }
771
- /**
772
- * Recursively import a directory and collect every spearkit-registrable export
773
- * (commands, events, components) found in default or named exports.
774
- */
775
- declare function collectModules(dir: string, options?: LoadOptions): Promise<Registerable[]>;
776
- /**
777
- * Load a directory and register everything it exports into the client.
778
- * Returns the number of items registered.
779
- */
780
- declare function loadInto(client: SpearClient, dir: string, options?: LoadOptions): Promise<number>;
781
-
782
- /** Anything that can be handed to {@link SpearClient.register}. */
783
- type Registerable = SlashCommand | EventDef | ComponentDef;
784
- /**
785
- * Ready-made intent presets. Pass one to {@link SpearClient} as `intents`.
786
- * `all` includes privileged intents — enable them in the developer portal.
787
- */
788
- declare const Intents: {
789
- /** No intents. */
790
- readonly none: GatewayIntentBits[];
791
- /** Just `Guilds` — enough for slash commands and interactions. */
792
- readonly default: readonly [GatewayIntentBits.Guilds];
793
- /** Guild + member gateway data. */
794
- readonly guilds: readonly [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers];
795
- /** Read message content (privileged) alongside guild messages. */
796
- readonly messages: readonly [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent];
797
- /** Every intent, including privileged ones. */
798
- readonly all: GatewayIntentBits[];
799
- };
800
- /** Options for {@link SpearClient}. Identical to discord.js but `intents` may be omitted. */
801
- type SpearClientOptions = Partial<ClientOptions>;
802
- /**
803
- * A discord.js {@link Client} with batteries included: command, event and
804
- * component registries plus interaction routing wired up automatically.
805
- *
806
- * @example
807
- * ```ts
808
- * const client = new SpearClient({ intents: Intents.default });
809
- * client.register(ping, onReady, voteButton);
810
- * await client.start(process.env.TOKEN);
811
- * await client.deployCommands({ guildId: "123" });
812
- * ```
813
- */
814
- declare class SpearClient extends Client {
815
- /** Slash command registry and dispatcher. */
816
- readonly commands: CommandRegistry;
817
- /** Event listener registry. */
818
- readonly events: EventRegistry;
819
- /** Button / select / modal registry and router. */
820
- readonly components: ComponentRegistry;
821
- constructor(options?: SpearClientOptions);
822
- /**
823
- * Register commands, events and components in one call. Each item is routed
824
- * to the matching registry based on its kind.
825
- */
826
- register(...items: Registerable[]): this;
827
- /** Install one or more plugins, running each plugin's `setup`. */
828
- use(...plugins: SpearPlugin[]): Promise<this>;
829
- /**
830
- * Recursively load a directory and register every command, event and
831
- * component it exports. Returns the number of items registered.
832
- */
833
- load(dir: string, options?: LoadOptions): Promise<number>;
834
- /**
835
- * Log in. Falls back to the `DISCORD_TOKEN` environment variable when no
836
- * token is passed.
837
- */
838
- start(token?: string): Promise<this>;
839
- /**
840
- * Push the registered slash commands to discord using the client's own
841
- * authenticated REST connection. Call after the client is ready.
842
- */
843
- deployCommands(options?: {
844
- guildId?: string;
845
- }): Promise<DeployResult>;
846
- private route;
847
- }
848
-
849
- export { type AllowedChannelType, type AnyComponentInteraction, type AnyOptionDef, AutocompleteContext, type AutocompleteHandler, BaseContext, type BuildArgs, type Button, type ButtonConfig, ButtonContext, type ButtonRoute, type ButtonStyleInput, type ChannelSelect, ChannelSelectContext, type ChannelSelectRoute, type CommandConfig, CommandContext, type CommandErrorHandler, type CommandGroupConfig, CommandRegistry, type CompiledPattern, type ComponentDef, type ComponentErrorHandler, ComponentRegistry, type DeployOptions, type DeployResult, type EntitySelectConfig, type EventConfig, type EventDef, type EventHandler, EventRegistry, Intents, type LinkButtonConfig, type LoadOptions, MAX_CUSTOM_ID_LENGTH, type MentionableSelect, MentionableSelectContext, type MentionableSelectRoute, MessageComponentContext, type Modal, type ModalConfig, ModalContext, type ModalRoute, type OptionChoice, type OptionDef, type OptionMap, type OptionValue, type ParamNames, type Params, type ParsedCustomId, type Registerable, type ReplyData, type ReplyInput, type ResolvedOption, type ResolvedOptions, type RoleSelect, RoleSelectContext, type RoleSelectRoute, SlashCommand, SpearClient, type SpearClientOptions, type SpearPlugin, type StringSelect, type StringSelectConfig, StringSelectContext, type StringSelectRoute, type Subcommand, type SubcommandConfig, type SubcommandGroup, type SubcommandGroupConfig, type TextInputDef, type TextInputStyleInput, type UserSelect, UserSelectContext, type UserSelectRoute, asEphemeral, buildCustomId, button, channelSelect, collectModules, command, commandGroup, compilePattern, definePlugin, event, linkButton, loadInto, mentionableSelect, modal, normalizeReply, option, optionsHaveAutocomplete, paramsFromValues, parseCustomId, readOption, roleSelect, row, stringSelect, subcommand, subcommandGroup, textInput, toAPIOption, userSelect };
1396
+ export { type AllowedChannelType, type AnyComponentInteraction, type AnyOptionDef, AutocompleteContext, type AutocompleteHandler, BaseContext, type BuildArgs, type Button, type ButtonConfig, ButtonContext, type ButtonRoute, type ButtonStyleInput, type ChannelSelect, ChannelSelectContext, type ChannelSelectRoute, type CommandConfig, CommandContext, type CommandErrorHandler, type CommandGroupConfig, CommandRegistry, type CompiledPattern, type ComponentDef, type ComponentErrorHandler, ComponentRegistry, type CooldownActor, type CooldownConfig, type CooldownExemptions, type CooldownInput, CooldownManager, type CooldownOverrides, type CooldownResult, type CooldownScope, CronExpression, type DeployOptions, type DeployResult, type EntitySelectConfig, type EnvReader, type EventConfig, type EventDef, type EventHandler, EventRegistry, Intents, JsonFileUsageStore, type LinkButtonConfig, type LoadEnvOptions, type LoadOptions, type LogEntry, type LogLevel, type LogOptions, type LogSink, type LogThreshold, type LogValue, Logger, type LoggerOptions, MAX_CUSTOM_ID_LENGTH, MemoryUsageStore, type MentionableSelect, MentionableSelectContext, type MentionableSelectRoute, MessageComponentContext, type Modal, type ModalConfig, ModalContext, type ModalRoute, type OptionChoice, type OptionDef, type OptionMap, type OptionValue, type ParamNames, type Params, type ParsedCustomId, type ParsedEnv, type PrefixCommand, type PrefixCommandConfig, PrefixContext, type PrefixErrorHandler, type PrefixOptions, PrefixRegistry, type Registerable, type ReplyData, type ReplyInput, type ResolvedOption, type ResolvedOptions, type RoleSelect, RoleSelectContext, type RoleSelectRoute, type ScheduledTask, SlashCommand, SpearClient, type SpearClientOptions, type SpearOptions, type SpearPlugin, type StringSelect, type StringSelectConfig, StringSelectContext, type StringSelectRoute, type Subcommand, type SubcommandConfig, type SubcommandGroup, type SubcommandGroupConfig, type TaskConfig, TaskScheduler, type TextInputDef, type TextInputStyleInput, type UsageEvent, type UsageOptions, type UsageStore, UsageTracker, type UsageType, type UserSelect, UserSelectContext, type UserSelectRoute, asEphemeral, buildCustomId, button, channelSelect, collectModules, command, commandGroup, compilePattern, consoleSink, cron, definePlugin, effectiveDuration, env, event, formatCooldownMessage, formatUsage, linkButton, loadEnv, loadInto, mentionableSelect, modal, normalizeCooldown, normalizeReply, option, optionsHaveAutocomplete, paramsFromValues, parseCustomId, parseEnv, prefixCommand, readOption, roleSelect, row, stringSelect, subcommand, subcommandGroup, task, textInput, toAPIOption, toError, userSelect };