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/README.md +11 -0
- package/dist/index.cjs +974 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +753 -206
- package/dist/index.d.ts +753 -206
- package/dist/index.js +955 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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,
|
|
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 };
|