spearkit 0.3.1 → 0.4.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/.claude/skills/spearkit/SKILL.md +11 -0
- package/.claude/skills/spearkit/reference/cheatsheet.md +117 -6
- package/AGENTS.md +98 -1
- package/README.md +10 -3
- package/dist/index.cjs +599 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +524 -2
- package/dist/index.d.ts +524 -2
- package/dist/index.js +576 -19
- package/dist/index.js.map +1 -1
- package/docs/README.md +21 -12
- package/docs/api-reference.md +222 -34
- package/docs/auto-defer.md +74 -0
- package/docs/client.md +60 -22
- package/docs/collectors.md +65 -0
- package/docs/commands.md +5 -0
- package/docs/components.md +7 -0
- package/docs/context-menus.md +121 -0
- package/docs/context.md +94 -2
- package/docs/cooldown.md +2 -1
- package/docs/errors.md +73 -0
- package/docs/guards.md +146 -0
- package/docs/loading.md +7 -5
- package/docs/messages.md +35 -0
- package/docs/permissions.md +68 -0
- package/docs/prefix.md +54 -0
- package/docs/scheduler.md +26 -2
- package/docs/shutdown.md +42 -0
- package/docs/store.md +90 -0
- package/docs/usage.md +20 -10
- package/llms-full.txt +1337 -85
- package/llms.txt +91 -3
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -357,6 +357,58 @@ function discordTimestamp(date, style = "f") {
|
|
|
357
357
|
function relativeTimestamp(date) {
|
|
358
358
|
return discordTimestamp(date, "R");
|
|
359
359
|
}
|
|
360
|
+
var MESSAGE_CHARACTER_LIMIT = 2e3;
|
|
361
|
+
function truncate(text, max, suffix = "\u2026") {
|
|
362
|
+
if (max <= 0) return "";
|
|
363
|
+
if (text.length <= max) return text;
|
|
364
|
+
if (suffix.length >= max) return suffix.slice(0, max);
|
|
365
|
+
return text.slice(0, max - suffix.length) + suffix;
|
|
366
|
+
}
|
|
367
|
+
function hardSplit(text, max) {
|
|
368
|
+
const out = [];
|
|
369
|
+
let rest = text;
|
|
370
|
+
while (rest.length > max) {
|
|
371
|
+
const space = rest.lastIndexOf(" ", max);
|
|
372
|
+
const cut = space > Math.floor(max / 2) ? space : max;
|
|
373
|
+
out.push(rest.slice(0, cut));
|
|
374
|
+
rest = rest.slice(cut).replace(/^ /, "");
|
|
375
|
+
}
|
|
376
|
+
if (rest.length > 0) out.push(rest);
|
|
377
|
+
return out;
|
|
378
|
+
}
|
|
379
|
+
function chunkMessage(text, options = {}) {
|
|
380
|
+
const max = options.max ?? MESSAGE_CHARACTER_LIMIT;
|
|
381
|
+
if (max <= 0) throw new RangeError("spearkit: chunkMessage max must be positive");
|
|
382
|
+
if (text.length === 0) return [];
|
|
383
|
+
if (text.length <= max) return [text];
|
|
384
|
+
const chunks = [];
|
|
385
|
+
let current = "";
|
|
386
|
+
const flush = () => {
|
|
387
|
+
if (current.length > 0) {
|
|
388
|
+
chunks.push(current);
|
|
389
|
+
current = "";
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
for (const line of text.split("\n")) {
|
|
393
|
+
if (line.length > max) {
|
|
394
|
+
flush();
|
|
395
|
+
const pieces = hardSplit(line, max);
|
|
396
|
+
for (let i = 0; i < pieces.length - 1; i++) chunks.push(pieces[i]);
|
|
397
|
+
current = pieces[pieces.length - 1];
|
|
398
|
+
continue;
|
|
399
|
+
}
|
|
400
|
+
const candidate = current.length > 0 ? `${current}
|
|
401
|
+
${line}` : line;
|
|
402
|
+
if (candidate.length > max) {
|
|
403
|
+
flush();
|
|
404
|
+
current = line;
|
|
405
|
+
} else {
|
|
406
|
+
current = candidate;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
flush();
|
|
410
|
+
return chunks;
|
|
411
|
+
}
|
|
360
412
|
|
|
361
413
|
// src/cache.ts
|
|
362
414
|
var MemoryCache = class {
|
|
@@ -451,6 +503,130 @@ function lookup(table, resourceName = "key") {
|
|
|
451
503
|
function lookupOptional(table) {
|
|
452
504
|
return (key) => table[key];
|
|
453
505
|
}
|
|
506
|
+
function clone(value) {
|
|
507
|
+
if (value === void 0 || value === null) return value;
|
|
508
|
+
if (typeof structuredClone === "function") return structuredClone(value);
|
|
509
|
+
return JSON.parse(JSON.stringify(value));
|
|
510
|
+
}
|
|
511
|
+
var MemoryStore = class {
|
|
512
|
+
map = /* @__PURE__ */ new Map();
|
|
513
|
+
async get(key) {
|
|
514
|
+
return this.map.has(key) ? clone(this.map.get(key)) : void 0;
|
|
515
|
+
}
|
|
516
|
+
async set(key, value) {
|
|
517
|
+
this.map.set(key, clone(value));
|
|
518
|
+
}
|
|
519
|
+
async has(key) {
|
|
520
|
+
return this.map.has(key);
|
|
521
|
+
}
|
|
522
|
+
async delete(key) {
|
|
523
|
+
return this.map.delete(key);
|
|
524
|
+
}
|
|
525
|
+
async keys() {
|
|
526
|
+
return [...this.map.keys()];
|
|
527
|
+
}
|
|
528
|
+
async clear() {
|
|
529
|
+
this.map.clear();
|
|
530
|
+
}
|
|
531
|
+
};
|
|
532
|
+
var JsonStore = class {
|
|
533
|
+
constructor(path) {
|
|
534
|
+
this.path = path;
|
|
535
|
+
}
|
|
536
|
+
path;
|
|
537
|
+
cache = /* @__PURE__ */ new Map();
|
|
538
|
+
loading;
|
|
539
|
+
writeChain = Promise.resolve();
|
|
540
|
+
ensureLoaded() {
|
|
541
|
+
if (this.loading === void 0) this.loading = this.load();
|
|
542
|
+
return this.loading;
|
|
543
|
+
}
|
|
544
|
+
async load() {
|
|
545
|
+
try {
|
|
546
|
+
const raw = await promises.readFile(this.path, "utf8");
|
|
547
|
+
const parsed = JSON.parse(raw);
|
|
548
|
+
for (const [key, value] of Object.entries(parsed)) this.cache.set(key, value);
|
|
549
|
+
} catch (error) {
|
|
550
|
+
if (error.code !== "ENOENT") throw error;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
/** Queue an atomic write of the current cache; serialised against prior writes. */
|
|
554
|
+
persist() {
|
|
555
|
+
this.writeChain = this.writeChain.then(async () => {
|
|
556
|
+
const body = JSON.stringify(Object.fromEntries(this.cache), null, 2);
|
|
557
|
+
await promises.mkdir(path.dirname(this.path), { recursive: true });
|
|
558
|
+
const tmp = `${this.path}.${process.pid}.${Date.now()}.tmp`;
|
|
559
|
+
await promises.writeFile(tmp, body, "utf8");
|
|
560
|
+
await promises.rename(tmp, this.path);
|
|
561
|
+
});
|
|
562
|
+
return this.writeChain;
|
|
563
|
+
}
|
|
564
|
+
async get(key) {
|
|
565
|
+
await this.ensureLoaded();
|
|
566
|
+
return this.cache.has(key) ? clone(this.cache.get(key)) : void 0;
|
|
567
|
+
}
|
|
568
|
+
async set(key, value) {
|
|
569
|
+
await this.ensureLoaded();
|
|
570
|
+
this.cache.set(key, clone(value));
|
|
571
|
+
await this.persist();
|
|
572
|
+
}
|
|
573
|
+
async has(key) {
|
|
574
|
+
await this.ensureLoaded();
|
|
575
|
+
return this.cache.has(key);
|
|
576
|
+
}
|
|
577
|
+
async delete(key) {
|
|
578
|
+
await this.ensureLoaded();
|
|
579
|
+
const existed = this.cache.delete(key);
|
|
580
|
+
if (existed) await this.persist();
|
|
581
|
+
return existed;
|
|
582
|
+
}
|
|
583
|
+
async keys() {
|
|
584
|
+
await this.ensureLoaded();
|
|
585
|
+
return [...this.cache.keys()];
|
|
586
|
+
}
|
|
587
|
+
async clear() {
|
|
588
|
+
await this.ensureLoaded();
|
|
589
|
+
this.cache.clear();
|
|
590
|
+
await this.persist();
|
|
591
|
+
}
|
|
592
|
+
};
|
|
593
|
+
function namespaced(store, prefix) {
|
|
594
|
+
const tag = `${prefix}:`;
|
|
595
|
+
return {
|
|
596
|
+
get: (key) => store.get(tag + key),
|
|
597
|
+
set: (key, value) => store.set(tag + key, value),
|
|
598
|
+
has: (key) => store.has(tag + key),
|
|
599
|
+
delete: (key) => store.delete(tag + key),
|
|
600
|
+
async keys() {
|
|
601
|
+
return (await store.keys()).filter((k) => k.startsWith(tag)).map((k) => k.slice(tag.length));
|
|
602
|
+
},
|
|
603
|
+
async clear() {
|
|
604
|
+
for (const key of await this.keys()) await store.delete(tag + key);
|
|
605
|
+
}
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
function createSettings(options) {
|
|
609
|
+
const { store, defaults } = options;
|
|
610
|
+
const ns = options.namespace ?? "settings";
|
|
611
|
+
const keyFor2 = (id) => `${ns}:${id}`;
|
|
612
|
+
return {
|
|
613
|
+
defaults,
|
|
614
|
+
store,
|
|
615
|
+
async get(id) {
|
|
616
|
+
const stored = await store.get(keyFor2(id));
|
|
617
|
+
return { ...defaults, ...stored ?? {} };
|
|
618
|
+
},
|
|
619
|
+
async set(id, patch) {
|
|
620
|
+
const stored = await store.get(keyFor2(id)) ?? {};
|
|
621
|
+
const merged = { ...stored, ...patch };
|
|
622
|
+
await store.set(keyFor2(id), merged);
|
|
623
|
+
return { ...defaults, ...merged };
|
|
624
|
+
},
|
|
625
|
+
async reset(id) {
|
|
626
|
+
await store.delete(keyFor2(id));
|
|
627
|
+
}
|
|
628
|
+
};
|
|
629
|
+
}
|
|
454
630
|
function denied(reason) {
|
|
455
631
|
return { allowed: false, reason };
|
|
456
632
|
}
|
|
@@ -519,6 +695,278 @@ function requireBotPermissions(permission, reason = "I don't have permission to
|
|
|
519
695
|
function guard(predicate) {
|
|
520
696
|
return predicate;
|
|
521
697
|
}
|
|
698
|
+
function missingPermissions(channel, who, required) {
|
|
699
|
+
const held = channel.permissionsFor(who);
|
|
700
|
+
if (held === null) return new discord_js.PermissionsBitField(required).toArray();
|
|
701
|
+
return held.missing(required);
|
|
702
|
+
}
|
|
703
|
+
function botMissingPermissions(channel, required) {
|
|
704
|
+
const me = channel.guild.members.me;
|
|
705
|
+
if (me === null) return new discord_js.PermissionsBitField(required).toArray();
|
|
706
|
+
return missingPermissions(channel, me, required);
|
|
707
|
+
}
|
|
708
|
+
function hasPermissions(channel, who, required) {
|
|
709
|
+
return missingPermissions(channel, who, required).length === 0;
|
|
710
|
+
}
|
|
711
|
+
function compareRoles(a, b) {
|
|
712
|
+
return a.roles.highest.comparePositionTo(b.roles.highest);
|
|
713
|
+
}
|
|
714
|
+
function canActOn(actor, target) {
|
|
715
|
+
if (actor.id === target.id) return false;
|
|
716
|
+
if (target.id === target.guild.ownerId) return false;
|
|
717
|
+
if (actor.id === actor.guild.ownerId) return true;
|
|
718
|
+
return compareRoles(actor, target) > 0;
|
|
719
|
+
}
|
|
720
|
+
function moderationCheck(options) {
|
|
721
|
+
const { moderator, target } = options;
|
|
722
|
+
const action = options.action ?? "moderate";
|
|
723
|
+
const me = options.me === void 0 ? target.guild.members.me : options.me;
|
|
724
|
+
const name = target.user.username;
|
|
725
|
+
if (moderator.id === target.id) return { ok: false, reason: `You can't ${action} yourself.` };
|
|
726
|
+
if (target.id === target.guild.ownerId) {
|
|
727
|
+
return { ok: false, reason: `You can't ${action} the server owner.` };
|
|
728
|
+
}
|
|
729
|
+
if (moderator.id !== moderator.guild.ownerId && compareRoles(moderator, target) <= 0) {
|
|
730
|
+
return {
|
|
731
|
+
ok: false,
|
|
732
|
+
reason: `You can't ${action} **${name}** \u2014 their highest role is above or equal to yours.`
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
if (me !== null) {
|
|
736
|
+
if (me.id === target.id) return { ok: false, reason: `I can't ${action} myself.` };
|
|
737
|
+
if (me.id !== me.guild.ownerId && compareRoles(me, target) <= 0) {
|
|
738
|
+
return {
|
|
739
|
+
ok: false,
|
|
740
|
+
reason: `I can't ${action} **${name}** \u2014 move my role above theirs and try again.`
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
return { ok: true };
|
|
745
|
+
}
|
|
746
|
+
var PERMISSION_LABELS = {
|
|
747
|
+
CreateInstantInvite: "Create Invite",
|
|
748
|
+
KickMembers: "Kick Members",
|
|
749
|
+
BanMembers: "Ban Members",
|
|
750
|
+
Administrator: "Administrator",
|
|
751
|
+
ManageChannels: "Manage Channels",
|
|
752
|
+
ManageGuild: "Manage Server",
|
|
753
|
+
AddReactions: "Add Reactions",
|
|
754
|
+
ViewAuditLog: "View Audit Log",
|
|
755
|
+
PrioritySpeaker: "Priority Speaker",
|
|
756
|
+
Stream: "Video",
|
|
757
|
+
ViewChannel: "View Channel",
|
|
758
|
+
SendMessages: "Send Messages",
|
|
759
|
+
SendTTSMessages: "Send TTS Messages",
|
|
760
|
+
ManageMessages: "Manage Messages",
|
|
761
|
+
EmbedLinks: "Embed Links",
|
|
762
|
+
AttachFiles: "Attach Files",
|
|
763
|
+
ReadMessageHistory: "Read Message History",
|
|
764
|
+
MentionEveryone: "Mention Everyone",
|
|
765
|
+
UseExternalEmojis: "Use External Emojis",
|
|
766
|
+
ViewGuildInsights: "View Server Insights",
|
|
767
|
+
Connect: "Connect",
|
|
768
|
+
Speak: "Speak",
|
|
769
|
+
MuteMembers: "Mute Members",
|
|
770
|
+
DeafenMembers: "Deafen Members",
|
|
771
|
+
MoveMembers: "Move Members",
|
|
772
|
+
UseVAD: "Use Voice Activity",
|
|
773
|
+
ChangeNickname: "Change Nickname",
|
|
774
|
+
ManageNicknames: "Manage Nicknames",
|
|
775
|
+
ManageRoles: "Manage Roles",
|
|
776
|
+
ManageWebhooks: "Manage Webhooks",
|
|
777
|
+
ManageGuildExpressions: "Manage Expressions",
|
|
778
|
+
UseApplicationCommands: "Use Application Commands",
|
|
779
|
+
RequestToSpeak: "Request to Speak",
|
|
780
|
+
ManageEvents: "Manage Events",
|
|
781
|
+
ManageThreads: "Manage Threads",
|
|
782
|
+
CreatePublicThreads: "Create Public Threads",
|
|
783
|
+
CreatePrivateThreads: "Create Private Threads",
|
|
784
|
+
UseExternalStickers: "Use External Stickers",
|
|
785
|
+
SendMessagesInThreads: "Send Messages in Threads",
|
|
786
|
+
UseEmbeddedActivities: "Use Activities",
|
|
787
|
+
ModerateMembers: "Timeout Members"
|
|
788
|
+
};
|
|
789
|
+
function formatPermissions(permissions) {
|
|
790
|
+
const names = new discord_js.PermissionsBitField(permissions).toArray();
|
|
791
|
+
if (names.length === 0) return "none";
|
|
792
|
+
return names.map((flag) => PERMISSION_LABELS[flag] ?? flag).join(", ");
|
|
793
|
+
}
|
|
794
|
+
var DiscordErrorCode = {
|
|
795
|
+
/** A referenced channel no longer exists or is invisible to the bot. */
|
|
796
|
+
UnknownChannel: discord_js.RESTJSONErrorCodes.UnknownChannel,
|
|
797
|
+
// 10003
|
|
798
|
+
/** The targeted guild is gone or the bot was removed from it. */
|
|
799
|
+
UnknownGuild: discord_js.RESTJSONErrorCodes.UnknownGuild,
|
|
800
|
+
// 10004
|
|
801
|
+
/** The referenced member is not in the guild. */
|
|
802
|
+
UnknownMember: discord_js.RESTJSONErrorCodes.UnknownMember,
|
|
803
|
+
// 10007
|
|
804
|
+
/** The message was deleted (or never existed) before the action ran. */
|
|
805
|
+
UnknownMessage: discord_js.RESTJSONErrorCodes.UnknownMessage,
|
|
806
|
+
// 10008
|
|
807
|
+
/** The user could not be resolved. */
|
|
808
|
+
UnknownUser: discord_js.RESTJSONErrorCodes.UnknownUser,
|
|
809
|
+
// 10013
|
|
810
|
+
/** The interaction token expired (the classic 3-second-window failure). */
|
|
811
|
+
UnknownInteraction: discord_js.RESTJSONErrorCodes.UnknownInteraction,
|
|
812
|
+
// 10062
|
|
813
|
+
/** The bot lacks access to the resource entirely (not just one permission). */
|
|
814
|
+
MissingAccess: discord_js.RESTJSONErrorCodes.MissingAccess,
|
|
815
|
+
// 50001
|
|
816
|
+
/** Action attempted on a DM channel that does not support it. */
|
|
817
|
+
CannotExecuteActionOnDMChannel: discord_js.RESTJSONErrorCodes.CannotExecuteActionOnDMChannel,
|
|
818
|
+
// 50003
|
|
819
|
+
/** The target user has DMs closed or blocked the bot. */
|
|
820
|
+
CannotSendMessagesToThisUser: discord_js.RESTJSONErrorCodes.CannotSendMessagesToThisUser,
|
|
821
|
+
// 50007
|
|
822
|
+
/** The bot is missing one or more permissions required for the action. */
|
|
823
|
+
MissingPermissions: discord_js.RESTJSONErrorCodes.MissingPermissions,
|
|
824
|
+
// 50013
|
|
825
|
+
/** The request body failed Discord's validation. */
|
|
826
|
+
InvalidFormBodyOrContentType: discord_js.RESTJSONErrorCodes.InvalidFormBodyOrContentType,
|
|
827
|
+
// 50035
|
|
828
|
+
/** The interaction was already acknowledged elsewhere. */
|
|
829
|
+
InteractionHasAlreadyBeenAcknowledged: discord_js.RESTJSONErrorCodes.InteractionHasAlreadyBeenAcknowledged,
|
|
830
|
+
// 40060
|
|
831
|
+
/** The bot reached the maximum number of guilds it may join. */
|
|
832
|
+
MaximumNumberOfGuildsReached: discord_js.RESTJSONErrorCodes.MaximumNumberOfGuildsReached,
|
|
833
|
+
// 30001
|
|
834
|
+
/** Too many active reactions / pins / etc. of this kind. */
|
|
835
|
+
MaximumNumberOfReactionsReached: discord_js.RESTJSONErrorCodes.MaximumNumberOfReactionsReached
|
|
836
|
+
// 30010
|
|
837
|
+
};
|
|
838
|
+
function isDiscordError(error, code) {
|
|
839
|
+
if (!(error instanceof discord_js.DiscordAPIError)) return false;
|
|
840
|
+
if (code === void 0) return true;
|
|
841
|
+
const codes = Array.isArray(code) ? code : [code];
|
|
842
|
+
return codes.includes(error.code);
|
|
843
|
+
}
|
|
844
|
+
function isHTTPError(error) {
|
|
845
|
+
return error instanceof discord_js.HTTPError;
|
|
846
|
+
}
|
|
847
|
+
function isRateLimitError(error) {
|
|
848
|
+
return error instanceof discord_js.DiscordAPIError && error.status === 429;
|
|
849
|
+
}
|
|
850
|
+
var FRIENDLY = {
|
|
851
|
+
[DiscordErrorCode.UnknownChannel]: "That channel no longer exists.",
|
|
852
|
+
[DiscordErrorCode.UnknownMessage]: "That message no longer exists.",
|
|
853
|
+
[DiscordErrorCode.UnknownMember]: "That member isn't in this server.",
|
|
854
|
+
[DiscordErrorCode.UnknownUser]: "I couldn't find that user.",
|
|
855
|
+
[DiscordErrorCode.UnknownInteraction]: "This took too long and expired \u2014 please run it again.",
|
|
856
|
+
[DiscordErrorCode.InteractionHasAlreadyBeenAcknowledged]: "This took too long and expired \u2014 please run it again.",
|
|
857
|
+
[DiscordErrorCode.MissingAccess]: "I don't have access to do that here.",
|
|
858
|
+
[DiscordErrorCode.CannotSendMessagesToThisUser]: "I can't DM that user \u2014 they may have DMs disabled.",
|
|
859
|
+
[DiscordErrorCode.CannotExecuteActionOnDMChannel]: "That can't be done in a DM.",
|
|
860
|
+
[DiscordErrorCode.MissingPermissions]: "I'm missing the permissions needed to do that."
|
|
861
|
+
};
|
|
862
|
+
function explainDiscordError(error) {
|
|
863
|
+
if (isRateLimitError(error)) {
|
|
864
|
+
return "I'm being rate-limited right now \u2014 please try again in a moment.";
|
|
865
|
+
}
|
|
866
|
+
if (!(error instanceof discord_js.DiscordAPIError)) return null;
|
|
867
|
+
if (typeof error.code === "number") {
|
|
868
|
+
const known = FRIENDLY[error.code];
|
|
869
|
+
if (known !== void 0) return known;
|
|
870
|
+
}
|
|
871
|
+
return null;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
// src/shutdown.ts
|
|
875
|
+
function gracefulShutdown(client, options = {}) {
|
|
876
|
+
const signals = options.signals ?? ["SIGINT", "SIGTERM"];
|
|
877
|
+
const timeoutMs = options.timeoutMs ?? 1e4;
|
|
878
|
+
const exit = options.exit ?? true;
|
|
879
|
+
let shuttingDown = false;
|
|
880
|
+
const handler = (signal) => {
|
|
881
|
+
if (shuttingDown) return;
|
|
882
|
+
shuttingDown = true;
|
|
883
|
+
options.logger?.info?.(`received ${signal}, shutting down`);
|
|
884
|
+
const force = setTimeout(() => {
|
|
885
|
+
options.logger?.error?.("shutdown timed out \u2014 forcing exit");
|
|
886
|
+
if (exit) process.exit(1);
|
|
887
|
+
}, timeoutMs);
|
|
888
|
+
if (typeof force.unref === "function") force.unref();
|
|
889
|
+
void (async () => {
|
|
890
|
+
try {
|
|
891
|
+
await options.onShutdown?.(signal);
|
|
892
|
+
await client.destroy();
|
|
893
|
+
clearTimeout(force);
|
|
894
|
+
options.logger?.info?.("shutdown complete");
|
|
895
|
+
if (exit) process.exit(0);
|
|
896
|
+
} catch (error) {
|
|
897
|
+
clearTimeout(force);
|
|
898
|
+
options.logger?.error?.("shutdown failed", error);
|
|
899
|
+
if (exit) process.exit(1);
|
|
900
|
+
}
|
|
901
|
+
})();
|
|
902
|
+
};
|
|
903
|
+
for (const signal of signals) process.on(signal, handler);
|
|
904
|
+
return () => {
|
|
905
|
+
for (const signal of signals) process.off(signal, handler);
|
|
906
|
+
};
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
// src/collectors.ts
|
|
910
|
+
async function awaitMessage(channel, options = {}) {
|
|
911
|
+
const { filter, time = 6e4 } = options;
|
|
912
|
+
try {
|
|
913
|
+
const collected = await channel.awaitMessages({
|
|
914
|
+
filter,
|
|
915
|
+
max: 1,
|
|
916
|
+
time,
|
|
917
|
+
errors: ["time"]
|
|
918
|
+
});
|
|
919
|
+
return collected.first() ?? null;
|
|
920
|
+
} catch {
|
|
921
|
+
return null;
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
async function awaitComponent(message, options = {}) {
|
|
925
|
+
const { filter, time = 6e4, componentType } = options;
|
|
926
|
+
try {
|
|
927
|
+
return await message.awaitMessageComponent({
|
|
928
|
+
time,
|
|
929
|
+
filter: (interaction) => (componentType === void 0 || interaction.componentType === componentType) && (filter?.(interaction) ?? true)
|
|
930
|
+
});
|
|
931
|
+
} catch {
|
|
932
|
+
return null;
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
function resolveModalCustomId(modal2) {
|
|
936
|
+
const m = modal2;
|
|
937
|
+
return m.data?.custom_id ?? m.custom_id ?? m.customId;
|
|
938
|
+
}
|
|
939
|
+
async function showAndAwaitModal(interaction, modal2, options = {}) {
|
|
940
|
+
const customId = resolveModalCustomId(modal2);
|
|
941
|
+
await interaction.showModal(modal2);
|
|
942
|
+
const { time = 12e4, filter } = options;
|
|
943
|
+
try {
|
|
944
|
+
return await interaction.awaitModalSubmit({
|
|
945
|
+
time,
|
|
946
|
+
filter: (submitted) => submitted.user.id === interaction.user.id && (customId === void 0 || submitted.customId === customId) && (filter?.(submitted) ?? true)
|
|
947
|
+
});
|
|
948
|
+
} catch {
|
|
949
|
+
return null;
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
var DEFAULT_AUTO_DEFER_DELAY_MS = 2e3;
|
|
953
|
+
function normalizeAutoDefer(input) {
|
|
954
|
+
if (input === void 0 || input === false) return void 0;
|
|
955
|
+
if (input === true) return { ephemeral: false, delayMs: DEFAULT_AUTO_DEFER_DELAY_MS };
|
|
956
|
+
return {
|
|
957
|
+
ephemeral: input.ephemeral ?? false,
|
|
958
|
+
delayMs: input.delayMs ?? DEFAULT_AUTO_DEFER_DELAY_MS
|
|
959
|
+
};
|
|
960
|
+
}
|
|
961
|
+
function armAutoDefer(interaction, config) {
|
|
962
|
+
const timer = setTimeout(() => {
|
|
963
|
+
if (!interaction.replied && !interaction.deferred) {
|
|
964
|
+
void interaction.deferReply(config.ephemeral ? { flags: discord_js.MessageFlags.Ephemeral } : {}).catch(() => void 0);
|
|
965
|
+
}
|
|
966
|
+
}, config.delayMs);
|
|
967
|
+
if (typeof timer.unref === "function") timer.unref();
|
|
968
|
+
return () => clearTimeout(timer);
|
|
969
|
+
}
|
|
522
970
|
function withEphemeralFlag(flags) {
|
|
523
971
|
if (flags == null) return discord_js.MessageFlags.Ephemeral;
|
|
524
972
|
if (typeof flags === "number" || typeof flags === "bigint") {
|
|
@@ -615,6 +1063,38 @@ var BaseContext = class {
|
|
|
615
1063
|
await this.reply(input);
|
|
616
1064
|
}
|
|
617
1065
|
}
|
|
1066
|
+
/** The bot's resolved permissions in the current channel. */
|
|
1067
|
+
get botPermissions() {
|
|
1068
|
+
return this.interaction.appPermissions;
|
|
1069
|
+
}
|
|
1070
|
+
/**
|
|
1071
|
+
* Permission flag names the BOT is missing in the current channel — empty when
|
|
1072
|
+
* it has them all. Zero-fetch: reads the permissions Discord attached to the
|
|
1073
|
+
* interaction. Use before an action that needs elevated permissions.
|
|
1074
|
+
*/
|
|
1075
|
+
botMissing(required) {
|
|
1076
|
+
return this.interaction.appPermissions.missing(required);
|
|
1077
|
+
}
|
|
1078
|
+
/** Permission flag names the invoking USER is missing in the current channel. */
|
|
1079
|
+
userMissing(required) {
|
|
1080
|
+
const held = this.interaction.memberPermissions;
|
|
1081
|
+
if (held === null) return new discord_js.PermissionsBitField(required).toArray();
|
|
1082
|
+
return held.missing(required);
|
|
1083
|
+
}
|
|
1084
|
+
/**
|
|
1085
|
+
* Wait for the next message in this channel from `userId` (defaults to the
|
|
1086
|
+
* invoking user), resolving to it or `null` on timeout. The "type your answer"
|
|
1087
|
+
* flow without hand-rolling a collector.
|
|
1088
|
+
*/
|
|
1089
|
+
awaitMessageFrom(userId = this.user.id, options = {}) {
|
|
1090
|
+
const channel = this.interaction.channel;
|
|
1091
|
+
if (channel === null || !("awaitMessages" in channel)) return Promise.resolve(null);
|
|
1092
|
+
const extra = options.filter;
|
|
1093
|
+
return awaitMessage(channel, {
|
|
1094
|
+
time: options.time,
|
|
1095
|
+
filter: (message) => message.author.id === userId && (extra?.(message) ?? true)
|
|
1096
|
+
});
|
|
1097
|
+
}
|
|
618
1098
|
/** Get the configured {@link Embeds} factory — `client.embeds` or the default. */
|
|
619
1099
|
getEmbeds() {
|
|
620
1100
|
return this.interaction.client.embeds ?? defaultEmbeds;
|
|
@@ -779,6 +1259,7 @@ function userCommand(config) {
|
|
|
779
1259
|
name: config.name,
|
|
780
1260
|
cooldown,
|
|
781
1261
|
guards: config.guards,
|
|
1262
|
+
autoDefer: normalizeAutoDefer(config.autoDefer),
|
|
782
1263
|
toJSON: () => baseJSON(config, discord_js.ApplicationCommandType.User),
|
|
783
1264
|
execute: async (interaction) => {
|
|
784
1265
|
await config.run(new UserContextMenuContext(interaction));
|
|
@@ -792,6 +1273,7 @@ function messageCommand(config) {
|
|
|
792
1273
|
name: config.name,
|
|
793
1274
|
cooldown,
|
|
794
1275
|
guards: config.guards,
|
|
1276
|
+
autoDefer: normalizeAutoDefer(config.autoDefer),
|
|
795
1277
|
toJSON: () => baseJSON(config, discord_js.ApplicationCommandType.Message),
|
|
796
1278
|
execute: async (interaction) => {
|
|
797
1279
|
await config.run(new MessageContextMenuContext(interaction));
|
|
@@ -806,6 +1288,7 @@ var ContextMenuRegistry = class {
|
|
|
806
1288
|
defaultCooldown;
|
|
807
1289
|
defaultGuards = [];
|
|
808
1290
|
onUsage;
|
|
1291
|
+
defaultAutoDefer;
|
|
809
1292
|
/** Register one or more context-menu commands. */
|
|
810
1293
|
add(...commands) {
|
|
811
1294
|
for (const command2 of commands) {
|
|
@@ -839,6 +1322,11 @@ var ContextMenuRegistry = class {
|
|
|
839
1322
|
this.defaultGuards = guards;
|
|
840
1323
|
return this;
|
|
841
1324
|
}
|
|
1325
|
+
/** Default auto-defer applied to menus that don't set their own. */
|
|
1326
|
+
setAutoDefer(config) {
|
|
1327
|
+
this.defaultAutoDefer = config;
|
|
1328
|
+
return this;
|
|
1329
|
+
}
|
|
842
1330
|
setUsageHook(hook) {
|
|
843
1331
|
this.onUsage = hook;
|
|
844
1332
|
return this;
|
|
@@ -882,6 +1370,8 @@ var ContextMenuRegistry = class {
|
|
|
882
1370
|
return;
|
|
883
1371
|
}
|
|
884
1372
|
}
|
|
1373
|
+
const autoDefer = command2.autoDefer ?? this.defaultAutoDefer;
|
|
1374
|
+
const cancelAutoDefer = autoDefer !== void 0 ? armAutoDefer(interaction, autoDefer) : void 0;
|
|
885
1375
|
const start = Date.now();
|
|
886
1376
|
try {
|
|
887
1377
|
if (command2.kind === "userMenu") {
|
|
@@ -917,15 +1407,19 @@ var ContextMenuRegistry = class {
|
|
|
917
1407
|
timestamp: /* @__PURE__ */ new Date()
|
|
918
1408
|
});
|
|
919
1409
|
interaction.client.emit("error", err);
|
|
1410
|
+
const content = explainDiscordError(err) ?? "Something went wrong.";
|
|
920
1411
|
try {
|
|
921
|
-
if (
|
|
922
|
-
await interaction.
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
1412
|
+
if (interaction.deferred) {
|
|
1413
|
+
await interaction.editReply({ content });
|
|
1414
|
+
} else if (interaction.replied) {
|
|
1415
|
+
await interaction.followUp({ content, flags: discord_js.MessageFlags.Ephemeral });
|
|
1416
|
+
} else {
|
|
1417
|
+
await interaction.reply({ content, flags: discord_js.MessageFlags.Ephemeral });
|
|
926
1418
|
}
|
|
927
1419
|
} catch {
|
|
928
1420
|
}
|
|
1421
|
+
} finally {
|
|
1422
|
+
cancelAutoDefer?.();
|
|
929
1423
|
}
|
|
930
1424
|
}
|
|
931
1425
|
};
|
|
@@ -1861,7 +2355,8 @@ function resolveOptions(input) {
|
|
|
1861
2355
|
prefixes: typeof prefix === "string" ? [prefix] : [...prefix],
|
|
1862
2356
|
mention: options.mention ?? true,
|
|
1863
2357
|
ignoreBots: options.ignoreBots ?? true,
|
|
1864
|
-
caseInsensitive: options.caseInsensitive ?? true
|
|
2358
|
+
caseInsensitive: options.caseInsensitive ?? true,
|
|
2359
|
+
dynamic: options.dynamic
|
|
1865
2360
|
};
|
|
1866
2361
|
}
|
|
1867
2362
|
function actorFromMessage(message) {
|
|
@@ -1945,8 +2440,8 @@ var PrefixRegistry = class {
|
|
|
1945
2440
|
return [...this.commands.values()];
|
|
1946
2441
|
}
|
|
1947
2442
|
/** Strip a matching prefix (or bot mention) from `content`, or return `null`. */
|
|
1948
|
-
stripPrefix(content, botId) {
|
|
1949
|
-
for (const prefix of
|
|
2443
|
+
stripPrefix(content, botId, prefixes) {
|
|
2444
|
+
for (const prefix of prefixes) {
|
|
1950
2445
|
if (prefix.length > 0 && content.startsWith(prefix)) return content.slice(prefix.length);
|
|
1951
2446
|
}
|
|
1952
2447
|
if (this.options.mention && botId !== void 0) {
|
|
@@ -1955,14 +2450,24 @@ var PrefixRegistry = class {
|
|
|
1955
2450
|
}
|
|
1956
2451
|
return null;
|
|
1957
2452
|
}
|
|
2453
|
+
/** Resolve the effective prefixes for a message: static plus any dynamic ones. */
|
|
2454
|
+
async resolvePrefixes(message) {
|
|
2455
|
+
if (this.options.dynamic === void 0) return this.options.prefixes;
|
|
2456
|
+
const extra = await this.options.dynamic(message);
|
|
2457
|
+
if (extra === null || extra === void 0) return this.options.prefixes;
|
|
2458
|
+
const list = typeof extra === "string" ? [extra] : extra;
|
|
2459
|
+
return [...this.options.prefixes, ...list];
|
|
2460
|
+
}
|
|
1958
2461
|
/**
|
|
1959
2462
|
* Parse and dispatch a message. Returns `true` when a command ran (or was
|
|
1960
2463
|
* blocked by a cooldown), `false` when the message was not a prefix command.
|
|
1961
2464
|
*/
|
|
1962
2465
|
async handle(message) {
|
|
1963
|
-
|
|
2466
|
+
const hasMatcher = this.options.prefixes.length > 0 || this.options.mention || this.options.dynamic !== void 0;
|
|
2467
|
+
if (!hasMatcher) return false;
|
|
1964
2468
|
if (this.options.ignoreBots && message.author.bot) return false;
|
|
1965
|
-
const
|
|
2469
|
+
const prefixes = await this.resolvePrefixes(message);
|
|
2470
|
+
const stripped = this.stripPrefix(message.content, message.client.user?.id, prefixes);
|
|
1966
2471
|
if (stripped === null) return false;
|
|
1967
2472
|
const trimmed = stripped.trimStart();
|
|
1968
2473
|
const match = /^(\S+)\s*([\s\S]*)$/.exec(trimmed);
|
|
@@ -2310,6 +2815,13 @@ var CommandContext = class extends BaseContext {
|
|
|
2310
2815
|
async showModal(modal2) {
|
|
2311
2816
|
await this.interaction.showModal(modal2);
|
|
2312
2817
|
}
|
|
2818
|
+
/**
|
|
2819
|
+
* Show a modal and wait for the user to submit it, resolving to the submission
|
|
2820
|
+
* or `null` if they dismiss it / it times out. Scoped to this user and modal.
|
|
2821
|
+
*/
|
|
2822
|
+
awaitModal(modal2, options) {
|
|
2823
|
+
return showAndAwaitModal(this.interaction, modal2, options);
|
|
2824
|
+
}
|
|
2313
2825
|
};
|
|
2314
2826
|
var AutocompleteContext = class {
|
|
2315
2827
|
constructor(interaction) {
|
|
@@ -2364,6 +2876,8 @@ var SlashCommand = class {
|
|
|
2364
2876
|
cooldown;
|
|
2365
2877
|
/** Resolved guard list for this command, if any. */
|
|
2366
2878
|
guards;
|
|
2879
|
+
/** Resolved auto-defer configuration for this command, if any. */
|
|
2880
|
+
autoDefer;
|
|
2367
2881
|
/** @internal */
|
|
2368
2882
|
constructor(spec) {
|
|
2369
2883
|
this.name = spec.name;
|
|
@@ -2373,6 +2887,7 @@ var SlashCommand = class {
|
|
|
2373
2887
|
this.autocompleter = spec.autocompleter;
|
|
2374
2888
|
this.cooldown = spec.cooldown;
|
|
2375
2889
|
this.guards = spec.guards;
|
|
2890
|
+
this.autoDefer = spec.autoDefer;
|
|
2376
2891
|
}
|
|
2377
2892
|
/** Serialise to the discord REST chat-input command payload. */
|
|
2378
2893
|
toJSON() {
|
|
@@ -2452,7 +2967,8 @@ function command(config) {
|
|
|
2452
2967
|
executor,
|
|
2453
2968
|
autocompleter: makeAutocompleter(options),
|
|
2454
2969
|
cooldown: config.cooldown !== void 0 ? normalizeCooldown(config.cooldown) : void 0,
|
|
2455
|
-
guards: config.guards
|
|
2970
|
+
guards: config.guards,
|
|
2971
|
+
autoDefer: normalizeAutoDefer(config.autoDefer)
|
|
2456
2972
|
});
|
|
2457
2973
|
}
|
|
2458
2974
|
function subcommand(config) {
|
|
@@ -2525,7 +3041,8 @@ function commandGroup(config) {
|
|
|
2525
3041
|
executor,
|
|
2526
3042
|
autocompleter,
|
|
2527
3043
|
cooldown: config.cooldown !== void 0 ? normalizeCooldown(config.cooldown) : void 0,
|
|
2528
|
-
guards: config.guards
|
|
3044
|
+
guards: config.guards,
|
|
3045
|
+
autoDefer: normalizeAutoDefer(config.autoDefer)
|
|
2529
3046
|
});
|
|
2530
3047
|
}
|
|
2531
3048
|
var CommandRegistry = class {
|
|
@@ -2536,6 +3053,7 @@ var CommandRegistry = class {
|
|
|
2536
3053
|
defaultCooldown;
|
|
2537
3054
|
defaultGuards = [];
|
|
2538
3055
|
onUsage;
|
|
3056
|
+
defaultAutoDefer;
|
|
2539
3057
|
/** Register one or more commands. Later registrations override by name. */
|
|
2540
3058
|
add(...commands) {
|
|
2541
3059
|
for (const command2 of commands) this.commands.set(command2.name, command2);
|
|
@@ -2582,6 +3100,11 @@ var CommandRegistry = class {
|
|
|
2582
3100
|
this.defaultGuards = guards;
|
|
2583
3101
|
return this;
|
|
2584
3102
|
}
|
|
3103
|
+
/** Default auto-defer applied to commands that don't set their own. */
|
|
3104
|
+
setAutoDefer(config) {
|
|
3105
|
+
this.defaultAutoDefer = config;
|
|
3106
|
+
return this;
|
|
3107
|
+
}
|
|
2585
3108
|
/** Attach a hook called after each successful command execution. */
|
|
2586
3109
|
setUsageHook(hook) {
|
|
2587
3110
|
this.onUsage = hook;
|
|
@@ -2621,6 +3144,8 @@ var CommandRegistry = class {
|
|
|
2621
3144
|
return;
|
|
2622
3145
|
}
|
|
2623
3146
|
}
|
|
3147
|
+
const autoDefer = command2.autoDefer ?? this.defaultAutoDefer;
|
|
3148
|
+
const cancelAutoDefer = autoDefer !== void 0 ? armAutoDefer(interaction, autoDefer) : void 0;
|
|
2624
3149
|
const start = Date.now();
|
|
2625
3150
|
try {
|
|
2626
3151
|
await command2.execute(interaction);
|
|
@@ -2654,6 +3179,8 @@ var CommandRegistry = class {
|
|
|
2654
3179
|
} else {
|
|
2655
3180
|
await this.defaultErrorReply(err, interaction);
|
|
2656
3181
|
}
|
|
3182
|
+
} finally {
|
|
3183
|
+
cancelAutoDefer?.();
|
|
2657
3184
|
}
|
|
2658
3185
|
}
|
|
2659
3186
|
/** Dispatch an autocomplete interaction to its command. */
|
|
@@ -2686,7 +3213,7 @@ var CommandRegistry = class {
|
|
|
2686
3213
|
}
|
|
2687
3214
|
async defaultErrorReply(error, interaction) {
|
|
2688
3215
|
interaction.client.emit("error", error);
|
|
2689
|
-
const content = "Something went wrong while running that command.";
|
|
3216
|
+
const content = explainDiscordError(error) ?? "Something went wrong while running that command.";
|
|
2690
3217
|
try {
|
|
2691
3218
|
if (interaction.deferred) {
|
|
2692
3219
|
await interaction.editReply({ content });
|
|
@@ -2902,6 +3429,13 @@ var MessageComponentContext = class extends BaseContext {
|
|
|
2902
3429
|
async showModal(modal2) {
|
|
2903
3430
|
await this.interaction.showModal(modal2);
|
|
2904
3431
|
}
|
|
3432
|
+
/**
|
|
3433
|
+
* Show a modal and wait for the user to submit it, resolving to the submission
|
|
3434
|
+
* or `null` if they dismiss it / it times out. Scoped to this user and modal.
|
|
3435
|
+
*/
|
|
3436
|
+
awaitModal(modal2, options) {
|
|
3437
|
+
return showAndAwaitModal(this.interaction, modal2, options);
|
|
3438
|
+
}
|
|
2905
3439
|
};
|
|
2906
3440
|
var ButtonContext = class extends MessageComponentContext {
|
|
2907
3441
|
};
|
|
@@ -3113,8 +3647,13 @@ var ComponentRegistry = class {
|
|
|
3113
3647
|
await this.errorHandler(err, interaction);
|
|
3114
3648
|
} else {
|
|
3115
3649
|
interaction.client.emit("error", err);
|
|
3116
|
-
|
|
3117
|
-
|
|
3650
|
+
const content = explainDiscordError(err) ?? "Something went wrong.";
|
|
3651
|
+
if (interaction.deferred) {
|
|
3652
|
+
await interaction.editReply({ content }).catch(() => void 0);
|
|
3653
|
+
} else if (interaction.replied) {
|
|
3654
|
+
await interaction.followUp({ content, flags: discord_js.MessageFlags.Ephemeral }).catch(() => void 0);
|
|
3655
|
+
} else {
|
|
3656
|
+
await interaction.reply({ content, flags: discord_js.MessageFlags.Ephemeral }).catch(() => void 0);
|
|
3118
3657
|
}
|
|
3119
3658
|
}
|
|
3120
3659
|
}
|
|
@@ -3416,17 +3955,20 @@ var SpearClient = class extends discord_js.Client {
|
|
|
3416
3955
|
contextMenus = new ContextMenuRegistry();
|
|
3417
3956
|
envConfig;
|
|
3418
3957
|
constructor(options = {}) {
|
|
3419
|
-
const { intents, logger, dotenv, cooldown, prefix, usage, embeds, guards, ...rest } = options;
|
|
3958
|
+
const { intents, logger, dotenv, cooldown, prefix, usage, embeds, guards, autoDefer, ...rest } = options;
|
|
3420
3959
|
super({ ...rest, intents: intents ?? Intents.default });
|
|
3421
3960
|
this.embeds = embeds instanceof Embeds ? embeds : new Embeds(embeds);
|
|
3422
3961
|
this.envConfig = dotenv === false ? false : dotenv === void 0 || dotenv === true ? {} : dotenv;
|
|
3423
3962
|
this.logger = logger instanceof Logger ? logger : new Logger(logger);
|
|
3424
3963
|
const defaultCooldown = cooldown !== void 0 ? normalizeCooldown(cooldown) : void 0;
|
|
3964
|
+
const defaultAutoDefer = normalizeAutoDefer(autoDefer);
|
|
3425
3965
|
this.commands.setLogger(this.logger.child("commands"));
|
|
3426
3966
|
this.commands.setCooldowns(this.cooldowns, defaultCooldown);
|
|
3967
|
+
this.commands.setAutoDefer(defaultAutoDefer);
|
|
3427
3968
|
this.components.setLogger(this.logger.child("components"));
|
|
3428
3969
|
this.contextMenus.setLogger(this.logger.child("contextMenus"));
|
|
3429
3970
|
this.contextMenus.setCooldowns(this.cooldowns, defaultCooldown);
|
|
3971
|
+
this.contextMenus.setAutoDefer(defaultAutoDefer);
|
|
3430
3972
|
this.prefix.setLogger(this.logger.child("prefix"));
|
|
3431
3973
|
this.prefix.setCooldowns(this.cooldowns, defaultCooldown);
|
|
3432
3974
|
if (prefix !== void 0) this.prefix.setOptions(prefix);
|
|
@@ -3560,6 +4102,21 @@ var SpearClient = class extends discord_js.Client {
|
|
|
3560
4102
|
this.scheduler.stop();
|
|
3561
4103
|
await super.destroy();
|
|
3562
4104
|
}
|
|
4105
|
+
/**
|
|
4106
|
+
* Close the bot cleanly on `SIGINT`/`SIGTERM`: run an optional hook, then
|
|
4107
|
+
* `destroy()` (stopping the scheduler and gateway), then exit. Returns a
|
|
4108
|
+
* disposer that removes the signal handlers. Logs progress via `client.logger`.
|
|
4109
|
+
*/
|
|
4110
|
+
enableGracefulShutdown(options = {}) {
|
|
4111
|
+
const log = this.logger.child("shutdown");
|
|
4112
|
+
return gracefulShutdown(this, {
|
|
4113
|
+
logger: {
|
|
4114
|
+
info: (message) => log.info(message),
|
|
4115
|
+
error: (message, meta) => log.error(message, { error: toError(meta) })
|
|
4116
|
+
},
|
|
4117
|
+
...options
|
|
4118
|
+
});
|
|
4119
|
+
}
|
|
3563
4120
|
async route(interaction) {
|
|
3564
4121
|
if (interaction.isChatInputCommand()) {
|
|
3565
4122
|
await this.commands.handle(interaction);
|
|
@@ -3630,16 +4187,21 @@ exports.ComponentRegistry = ComponentRegistry;
|
|
|
3630
4187
|
exports.ContextMenuRegistry = ContextMenuRegistry;
|
|
3631
4188
|
exports.CooldownManager = CooldownManager;
|
|
3632
4189
|
exports.CronExpression = CronExpression;
|
|
4190
|
+
exports.DEFAULT_AUTO_DEFER_DELAY_MS = DEFAULT_AUTO_DEFER_DELAY_MS;
|
|
3633
4191
|
exports.DEFAULT_EMBED_COLORS = DEFAULT_EMBED_COLORS;
|
|
3634
4192
|
exports.DEFAULT_EMBED_ICONS = DEFAULT_EMBED_ICONS;
|
|
4193
|
+
exports.DiscordErrorCode = DiscordErrorCode;
|
|
3635
4194
|
exports.Embeds = Embeds;
|
|
3636
4195
|
exports.EventRegistry = EventRegistry;
|
|
3637
4196
|
exports.Intents = Intents;
|
|
3638
4197
|
exports.JsonFileUsageStore = JsonFileUsageStore;
|
|
4198
|
+
exports.JsonStore = JsonStore;
|
|
3639
4199
|
exports.KeyedLock = KeyedLock;
|
|
3640
4200
|
exports.Logger = Logger;
|
|
3641
4201
|
exports.MAX_CUSTOM_ID_LENGTH = MAX_CUSTOM_ID_LENGTH;
|
|
4202
|
+
exports.MESSAGE_CHARACTER_LIMIT = MESSAGE_CHARACTER_LIMIT;
|
|
3642
4203
|
exports.MemoryCache = MemoryCache;
|
|
4204
|
+
exports.MemoryStore = MemoryStore;
|
|
3643
4205
|
exports.MemoryUsageStore = MemoryUsageStore;
|
|
3644
4206
|
exports.MentionableSelectContext = MentionableSelectContext;
|
|
3645
4207
|
exports.MessageComponentContext = MessageComponentContext;
|
|
@@ -3656,18 +4218,26 @@ exports.TaskScheduler = TaskScheduler;
|
|
|
3656
4218
|
exports.UsageTracker = UsageTracker;
|
|
3657
4219
|
exports.UserContextMenuContext = UserContextMenuContext;
|
|
3658
4220
|
exports.UserSelectContext = UserSelectContext;
|
|
4221
|
+
exports.armAutoDefer = armAutoDefer;
|
|
3659
4222
|
exports.asEphemeral = asEphemeral;
|
|
4223
|
+
exports.awaitComponent = awaitComponent;
|
|
4224
|
+
exports.awaitMessage = awaitMessage;
|
|
4225
|
+
exports.botMissingPermissions = botMissingPermissions;
|
|
3660
4226
|
exports.buildCustomId = buildCustomId;
|
|
3661
4227
|
exports.buildPaginatorPage = buildPaginatorPage;
|
|
3662
4228
|
exports.button = button;
|
|
4229
|
+
exports.canActOn = canActOn;
|
|
3663
4230
|
exports.channelSelect = channelSelect;
|
|
4231
|
+
exports.chunkMessage = chunkMessage;
|
|
3664
4232
|
exports.collectModules = collectModules;
|
|
3665
4233
|
exports.command = command;
|
|
3666
4234
|
exports.commandGroup = commandGroup;
|
|
4235
|
+
exports.compareRoles = compareRoles;
|
|
3667
4236
|
exports.compilePattern = compilePattern;
|
|
3668
4237
|
exports.confirm = confirm;
|
|
3669
4238
|
exports.consoleSink = consoleSink;
|
|
3670
4239
|
exports.createCache = createCache;
|
|
4240
|
+
exports.createSettings = createSettings;
|
|
3671
4241
|
exports.cron = cron;
|
|
3672
4242
|
exports.defaultEmbeds = defaultEmbeds;
|
|
3673
4243
|
exports.definePlugin = definePlugin;
|
|
@@ -3677,6 +4247,7 @@ exports.dmOnly = dmOnly;
|
|
|
3677
4247
|
exports.effectiveDuration = effectiveDuration;
|
|
3678
4248
|
exports.env = env;
|
|
3679
4249
|
exports.event = event;
|
|
4250
|
+
exports.explainDiscordError = explainDiscordError;
|
|
3680
4251
|
exports.fetchChannel = fetchChannel;
|
|
3681
4252
|
exports.fetchGuild = fetchGuild;
|
|
3682
4253
|
exports.fetchMember = fetchMember;
|
|
@@ -3685,9 +4256,15 @@ exports.fetchRole = fetchRole;
|
|
|
3685
4256
|
exports.fetchUser = fetchUser;
|
|
3686
4257
|
exports.formatCooldownMessage = formatCooldownMessage;
|
|
3687
4258
|
exports.formatDuration = formatDuration;
|
|
4259
|
+
exports.formatPermissions = formatPermissions;
|
|
3688
4260
|
exports.formatUsage = formatUsage;
|
|
4261
|
+
exports.gracefulShutdown = gracefulShutdown;
|
|
3689
4262
|
exports.guard = guard;
|
|
3690
4263
|
exports.guildOnly = guildOnly;
|
|
4264
|
+
exports.hasPermissions = hasPermissions;
|
|
4265
|
+
exports.isDiscordError = isDiscordError;
|
|
4266
|
+
exports.isHTTPError = isHTTPError;
|
|
4267
|
+
exports.isRateLimitError = isRateLimitError;
|
|
3691
4268
|
exports.jsonlSink = jsonlSink;
|
|
3692
4269
|
exports.linkButton = linkButton;
|
|
3693
4270
|
exports.loadConfig = loadConfig;
|
|
@@ -3698,7 +4275,11 @@ exports.lookup = lookup;
|
|
|
3698
4275
|
exports.lookupOptional = lookupOptional;
|
|
3699
4276
|
exports.mentionableSelect = mentionableSelect;
|
|
3700
4277
|
exports.messageCommand = messageCommand;
|
|
4278
|
+
exports.missingPermissions = missingPermissions;
|
|
3701
4279
|
exports.modal = modal;
|
|
4280
|
+
exports.moderationCheck = moderationCheck;
|
|
4281
|
+
exports.namespaced = namespaced;
|
|
4282
|
+
exports.normalizeAutoDefer = normalizeAutoDefer;
|
|
3702
4283
|
exports.normalizeCooldown = normalizeCooldown;
|
|
3703
4284
|
exports.normalizeReply = normalizeReply;
|
|
3704
4285
|
exports.option = option;
|
|
@@ -3722,6 +4303,7 @@ exports.row = row;
|
|
|
3722
4303
|
exports.runGuards = runGuards;
|
|
3723
4304
|
exports.safeFetch = safeFetch;
|
|
3724
4305
|
exports.safeTry = safeTry;
|
|
4306
|
+
exports.showAndAwaitModal = showAndAwaitModal;
|
|
3725
4307
|
exports.stringSelect = stringSelect;
|
|
3726
4308
|
exports.subcommand = subcommand;
|
|
3727
4309
|
exports.subcommandGroup = subcommandGroup;
|
|
@@ -3729,6 +4311,7 @@ exports.task = task;
|
|
|
3729
4311
|
exports.textInput = textInput;
|
|
3730
4312
|
exports.toAPIOption = toAPIOption;
|
|
3731
4313
|
exports.toError = toError;
|
|
4314
|
+
exports.truncate = truncate;
|
|
3732
4315
|
exports.userCommand = userCommand;
|
|
3733
4316
|
exports.userSelect = userSelect;
|
|
3734
4317
|
exports.webhookSink = webhookSink;
|