@seedcord/gateway 0.3.1 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +247 -48
- package/dist/index.mjs +231 -104
- package/dist/index.mjs.map +1 -1
- package/package.json +14 -13
package/dist/index.mjs
CHANGED
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
import "reflect-metadata";
|
|
2
|
-
import { installNodeDefaults } from "@seedcord/logger/node";
|
|
3
|
-
import {
|
|
2
|
+
import { WinstonConsoleSink, WinstonFileSink, installNodeDefaults } from "@seedcord/logger/node";
|
|
3
|
+
import { EventMetadataKey, GateNotice, GatedMetadataKey, HmrManager, InteractionMetadataKey, InteractionRouteKeys, InteractionRoutes, MiddlewareMetadataKey, MissingPermissions, NoticeCard, PermissionNames, PublishDefault, accessorStore, areRoutes, asError, busLoggerOf, clearStore, decodeComponentRoute, deferFlags, guardedAccessor, isEmojiTuple, outcomeFor, pickNotice, queuedMsFor, reportDispatch, reportedWrite, routeIdOf, runHandlerGates, sendFlags, setBotColor, slowGateMonitor } from "@seedcord/core/internal";
|
|
4
4
|
import { SeedcordErrorCode, paint } from "@seedcord/errors";
|
|
5
5
|
import { SeedcordError, SeedcordTypeError, validateDiscordToken } from "@seedcord/errors/internal";
|
|
6
|
-
import {
|
|
6
|
+
import { ArraySourceBase, BaseHandler as BaseHandler$1, BaseReplySender, BuilderComponent, Bus, CursorSourceBase, CustomId, DispatchContext, Fault, Notice, PaginatorBase, RepliableHandler as RepliableHandler$1, ResolvedEmoji, RowComponent, Silence, Subscriber as Subscriber$1, WebhookLog as WebhookLog$1, assertPermissions, defineGate } from "@seedcord/core";
|
|
7
7
|
import { Logger, LoggerChannelRegistry } from "@seedcord/logger";
|
|
8
8
|
import { ButtonStyle, Client, ComponentType, DiscordAPIError, Events, GatewayIntentBits, Guild, GuildMember, IntentsBitField, InteractionContextType, Message, MessageFlags, PermissionFlagsBits, RESTJSONErrorCodes, Role, User } from "discord.js";
|
|
9
9
|
import { ButtonBuilder, TextDisplayBuilder } from "@discordjs/builders";
|
|
10
10
|
import { buildSlashRoute } from "@seedcord/utils/internal";
|
|
11
11
|
import { Plugin as Plugin$1 } from "@seedcord/core/plugin";
|
|
12
|
-
import {
|
|
12
|
+
import { CoordinatedShutdown, CoordinatedStartup, Pluggable } from "@seedcord/core/node";
|
|
13
|
+
import { CommandRegistry, HealthCheck, ShutdownPhase, StartupPhase, SubscriberLoader, settleWithin, shutdownOf } from "@seedcord/core/node/internal";
|
|
13
14
|
import { MemoryRateLimiter } from "@seedcord/rate-limiter";
|
|
14
|
-
import { SeedcordBrand } from "@seedcord/types/internal";
|
|
15
|
+
import { HostAugmentTarget, HostVersion, SeedcordBrand } from "@seedcord/types/internal";
|
|
15
16
|
import { Envapter } from "envapt";
|
|
16
17
|
import { Envapt } from "envapt/legacy";
|
|
17
18
|
import { HmrModuleHandler } from "@seedcord/core/hmr";
|
|
18
19
|
import { formatFilePath, hasKeys } from "@seedcord/utils";
|
|
19
20
|
import { traverseDirectory } from "@seedcord/utils/node";
|
|
20
21
|
import * as crypto from "node:crypto";
|
|
22
|
+
import { prefixOf } from "@seedcord/custom-id";
|
|
21
23
|
|
|
22
24
|
export * from "@seedcord/errors"
|
|
23
25
|
|
|
@@ -160,7 +162,7 @@ function Middleware(type, priority = 0, options = {}) {
|
|
|
160
162
|
return (ctor) => {
|
|
161
163
|
const normalizedPriority = Number(priority);
|
|
162
164
|
if (!Number.isFinite(normalizedPriority)) throw new SeedcordTypeError(SeedcordErrorCode.DecoratorInvalidMiddlewarePriority);
|
|
163
|
-
if (type === "interaction" && Array.isArray(options.events) && options.events.length > 0) throw new
|
|
165
|
+
if (type === "interaction" && Array.isArray(options.events) && options.events.length > 0) throw new SeedcordTypeError(SeedcordErrorCode.DecoratorInteractionEventFilter);
|
|
164
166
|
const metadata = {
|
|
165
167
|
priority: normalizedPriority,
|
|
166
168
|
type,
|
|
@@ -200,6 +202,7 @@ const IgnoreBots = defineGate("IgnoreBots", (ctx) => {
|
|
|
200
202
|
var NotNsfw = class extends GateNotice {
|
|
201
203
|
constructor(message = "This can only be used in an age-restricted channel.") {
|
|
202
204
|
super(message);
|
|
205
|
+
this.summary = "be in an age-restricted channel";
|
|
203
206
|
}
|
|
204
207
|
};
|
|
205
208
|
var RoleHigherThanMe = class extends Notice {
|
|
@@ -486,7 +489,7 @@ async function getConfirmation(handler, prompt, options) {
|
|
|
486
489
|
const { ephemeral = true, timeoutMs = DEFAULT_TIMEOUT_MS } = options ?? {};
|
|
487
490
|
const response = typeof prompt === "string" ? defaultPrompt(prompt, options) : await prompt(CONFIRM_IDS);
|
|
488
491
|
const interaction = handler.getEvent();
|
|
489
|
-
const sender = handler
|
|
492
|
+
const { sender } = handler;
|
|
490
493
|
const message = await sender.send(response, { ephemeral });
|
|
491
494
|
const winner = await collectChoice(message, interaction.user.id, timeoutMs);
|
|
492
495
|
if (!winner) {
|
|
@@ -724,9 +727,15 @@ function slashRouteOf(interaction) {
|
|
|
724
727
|
*/
|
|
725
728
|
var SlashHandler = class extends InteractionHandler {
|
|
726
729
|
/**
|
|
727
|
-
* The typed options for this command's route.
|
|
728
|
-
*
|
|
729
|
-
*
|
|
730
|
+
* The typed options for this command's route. Only the getters for kinds this command declares appear.
|
|
731
|
+
* Each getter's name argument is restricted to that command's options of its kind.
|
|
732
|
+
*
|
|
733
|
+
* - A required option drops the null.
|
|
734
|
+
* - Choices narrow to their literal union.
|
|
735
|
+
* - A channel option declared with `addChannelTypes` narrows to those types.
|
|
736
|
+
*
|
|
737
|
+
* Use `this.event.options` directly for anything outside this view, such as `getSubcommand()`.
|
|
738
|
+
* However, for the common case, the methods in this would suffice.
|
|
730
739
|
*/
|
|
731
740
|
get options() {
|
|
732
741
|
return this.event.options;
|
|
@@ -748,8 +757,8 @@ var SlashHandler = class extends InteractionHandler {
|
|
|
748
757
|
* class ModHandler extends SlashHandler<'ban' | 'kick'> {
|
|
749
758
|
* async execute() {
|
|
750
759
|
* await this.match({
|
|
751
|
-
* ban: (options) => this.event.guild?.members.ban(options.getUser('target'
|
|
752
|
-
* kick: (options) => this.event.guild?.members.kick(options.getUser('member'
|
|
760
|
+
* ban: (options) => this.event.guild?.members.ban(options.getUser('target').id),
|
|
761
|
+
* kick: (options) => this.event.guild?.members.kick(options.getUser('member').id)
|
|
753
762
|
* });
|
|
754
763
|
* }
|
|
755
764
|
* }
|
|
@@ -758,13 +767,18 @@ var SlashHandler = class extends InteractionHandler {
|
|
|
758
767
|
async match(arms) {
|
|
759
768
|
const route = slashRouteOf(this.event);
|
|
760
769
|
const arm = Object.hasOwn(arms, route) ? arms[route] : void 0;
|
|
761
|
-
if (!arm) throw new
|
|
770
|
+
if (!arm) throw new SeedcordTypeError(SeedcordErrorCode.SlashMatchArmMissing, [route]);
|
|
762
771
|
return await arm(this.options);
|
|
763
772
|
}
|
|
764
773
|
};
|
|
765
774
|
|
|
766
775
|
//#endregion
|
|
767
776
|
//#region src/handlers/interaction/ContextMenuHandler.ts
|
|
777
|
+
function armFor(arms, name) {
|
|
778
|
+
const arm = Object.hasOwn(arms, name) ? arms[name] : void 0;
|
|
779
|
+
if (typeof arm !== "function") throw new SeedcordTypeError(SeedcordErrorCode.ContextMenuMatchArmMissing, [name]);
|
|
780
|
+
return arm;
|
|
781
|
+
}
|
|
768
782
|
/**
|
|
769
783
|
* Base class for a user context-menu command handler (right-click a user).
|
|
770
784
|
*
|
|
@@ -792,6 +806,38 @@ var UserContextMenuHandler = class extends InteractionHandler {
|
|
|
792
806
|
get targetMember() {
|
|
793
807
|
return this.event.targetMember;
|
|
794
808
|
}
|
|
809
|
+
/** Which of the registered commands fired. */
|
|
810
|
+
get commandName() {
|
|
811
|
+
return this.event.commandName;
|
|
812
|
+
}
|
|
813
|
+
/**
|
|
814
|
+
* Run the arm for whichever command fired. Use this only when the handler is registered for several
|
|
815
|
+
* names.
|
|
816
|
+
*
|
|
817
|
+
* Provide one arm per registered name, keyed by the name Discord shows in the right-click menu. Each
|
|
818
|
+
* arm receives the clicked user and that user's guild member, both narrowed to the cache state of that
|
|
819
|
+
* one command. The arms must cover every name in the generic. A missing name or an unknown key is a
|
|
820
|
+
* compile error.
|
|
821
|
+
*
|
|
822
|
+
* @param arms - One handler per registered command name.
|
|
823
|
+
* @returns The result of the arm that ran.
|
|
824
|
+
*
|
|
825
|
+
* @example
|
|
826
|
+
* ```ts
|
|
827
|
+
* \@UserContextMenuRoute('View Profile', 'Warn')
|
|
828
|
+
* class Moderation extends UserContextMenuHandler<'View Profile' | 'Warn'> {
|
|
829
|
+
* async execute() {
|
|
830
|
+
* await this.match({
|
|
831
|
+
* 'View Profile': (user) => this.reply(`Profile for ${user.tag}.`),
|
|
832
|
+
* Warn: (user, member) => this.reply(`Warned ${member?.displayName ?? user.tag}.`)
|
|
833
|
+
* });
|
|
834
|
+
* }
|
|
835
|
+
* }
|
|
836
|
+
* ```
|
|
837
|
+
*/
|
|
838
|
+
async match(arms) {
|
|
839
|
+
return await armFor(arms, this.event.commandName)(this.target, this.targetMember);
|
|
840
|
+
}
|
|
795
841
|
};
|
|
796
842
|
/**
|
|
797
843
|
* Base class for a message context-menu command handler (right-click a message).
|
|
@@ -816,6 +862,37 @@ var MessageContextMenuHandler = class extends InteractionHandler {
|
|
|
816
862
|
get target() {
|
|
817
863
|
return this.event.targetMessage;
|
|
818
864
|
}
|
|
865
|
+
/** Which of the registered commands fired. */
|
|
866
|
+
get commandName() {
|
|
867
|
+
return this.event.commandName;
|
|
868
|
+
}
|
|
869
|
+
/**
|
|
870
|
+
* Run the arm for whichever command fired. Use this only when the handler is registered for several
|
|
871
|
+
* names.
|
|
872
|
+
*
|
|
873
|
+
* Provide one arm per registered name, keyed by the name Discord shows in the right-click menu. Each
|
|
874
|
+
* arm receives the clicked message, narrowed to the cache state of that one command. The arms must
|
|
875
|
+
* cover every name in the generic. A missing name or an unknown key is a compile error.
|
|
876
|
+
*
|
|
877
|
+
* @param arms - One handler per registered command name.
|
|
878
|
+
* @returns The result of the arm that ran.
|
|
879
|
+
*
|
|
880
|
+
* @example
|
|
881
|
+
* ```ts
|
|
882
|
+
* \@MessageContextMenuRoute('Report Message', 'Pin Message')
|
|
883
|
+
* class MessageTools extends MessageContextMenuHandler<'Report Message' | 'Pin Message'> {
|
|
884
|
+
* async execute() {
|
|
885
|
+
* await this.match({
|
|
886
|
+
* 'Report Message': (message) => this.report(message),
|
|
887
|
+
* 'Pin Message': (message) => message.pin()
|
|
888
|
+
* });
|
|
889
|
+
* }
|
|
890
|
+
* }
|
|
891
|
+
* ```
|
|
892
|
+
*/
|
|
893
|
+
async match(arms) {
|
|
894
|
+
return await armFor(arms, this.event.commandName)(this.target);
|
|
895
|
+
}
|
|
819
896
|
};
|
|
820
897
|
|
|
821
898
|
//#endregion
|
|
@@ -890,7 +967,7 @@ var AutocompleteHandler = class extends BaseHandler {
|
|
|
890
967
|
const { name, value } = this.focused;
|
|
891
968
|
const respond = (choices) => this.respond(choices);
|
|
892
969
|
const arm = Object.hasOwn(arms, name) ? arms[name] : void 0;
|
|
893
|
-
if (!arm) throw new
|
|
970
|
+
if (!arm) throw new SeedcordTypeError(SeedcordErrorCode.AutocompleteMatchArmMissing, [name]);
|
|
894
971
|
return await arm(value, respond);
|
|
895
972
|
}
|
|
896
973
|
/** Send autocomplete suggestions, callback type 8. Prefer {@link match}, which restricts each field's choices to its declared type. */
|
|
@@ -980,7 +1057,7 @@ var ComponentHandler = class extends InteractionHandler {
|
|
|
980
1057
|
async match(arms) {
|
|
981
1058
|
const { prefix, params } = this.route;
|
|
982
1059
|
const arm = Object.hasOwn(arms, prefix) ? arms[prefix] : void 0;
|
|
983
|
-
if (!arm) throw new
|
|
1060
|
+
if (!arm) throw new SeedcordTypeError(SeedcordErrorCode.CustomIdMatchArmMissing, [prefix]);
|
|
984
1061
|
return await arm(params);
|
|
985
1062
|
}
|
|
986
1063
|
};
|
|
@@ -1018,8 +1095,8 @@ var ButtonHandler = class extends ComponentHandler {};
|
|
|
1018
1095
|
*
|
|
1019
1096
|
* Register the customId definitions this handler decodes with `@ModalRoute`, list the same ones in the
|
|
1020
1097
|
* generic, then read `this.params` for a single route or `this.match` for several. Read the submitted
|
|
1021
|
-
* inputs from `this.
|
|
1022
|
-
*
|
|
1098
|
+
* inputs from `this.fields`. Reply through the handler members. `showModal` fails compilation on this kind,
|
|
1099
|
+
* because Discord forbids opening a modal in response to a modal.
|
|
1023
1100
|
*
|
|
1024
1101
|
* @typeParam Defs - The customId definitions this handler decodes, e.g. `[typeof ConfigId]`.
|
|
1025
1102
|
* @typeParam Cache - The interaction cache state, `'cached'` by default.
|
|
@@ -1030,13 +1107,17 @@ var ButtonHandler = class extends ComponentHandler {};
|
|
|
1030
1107
|
* class ConfigModal extends ModalHandler<[typeof ConfigId]> {
|
|
1031
1108
|
* async execute() {
|
|
1032
1109
|
* const { guildId } = this.params;
|
|
1033
|
-
* const name = this.
|
|
1110
|
+
* const name = this.fields.getTextInputValue('name');
|
|
1034
1111
|
* await this.reply(`saved ${name} for ${guildId}`);
|
|
1035
1112
|
* }
|
|
1036
1113
|
* }
|
|
1037
1114
|
* ```
|
|
1038
1115
|
*/
|
|
1039
1116
|
var ModalHandler = class extends ComponentHandler {
|
|
1117
|
+
/** The fields this modal submitted, read by custom id. */
|
|
1118
|
+
get fields() {
|
|
1119
|
+
return this.event.fields;
|
|
1120
|
+
}
|
|
1040
1121
|
/** Rewrite the message this modal was opened from. Throws when a command opened the modal. */
|
|
1041
1122
|
update(response) {
|
|
1042
1123
|
this.ensureSourceMessage("update");
|
|
@@ -1058,10 +1139,12 @@ var ModalHandler = class extends ComponentHandler {
|
|
|
1058
1139
|
/**
|
|
1059
1140
|
* Base class for a select menu handler.
|
|
1060
1141
|
*
|
|
1061
|
-
* Pass the select kind first and the customId definitions second, the same order as `@SelectMenuRoute
|
|
1062
|
-
*
|
|
1063
|
-
*
|
|
1064
|
-
*
|
|
1142
|
+
* Pass the select kind first and the customId definitions second, the same order as `@SelectMenuRoute`.
|
|
1143
|
+
* Read the picked ids from `this.values`. The kind decides which of `this.users`, `this.members`,
|
|
1144
|
+
* `this.roles`, and `this.channels` this handler carries. The rest are `never`.
|
|
1145
|
+
*
|
|
1146
|
+
* Read `this.params` for a single route or `this.match` for several. Reply through the handler members, or
|
|
1147
|
+
* rewrite the source message with `this.update`.
|
|
1065
1148
|
*
|
|
1066
1149
|
* @typeParam Kind - The select kind from {@link SelectMenuKind}, e.g. `SelectMenuKind.User`.
|
|
1067
1150
|
* @typeParam Defs - The customId definitions this handler decodes, e.g. `[typeof AssignId]`.
|
|
@@ -1073,12 +1156,36 @@ var ModalHandler = class extends ComponentHandler {
|
|
|
1073
1156
|
* class AssignSelect extends SelectMenuHandler<SelectMenuKind.User, [typeof AssignId]> {
|
|
1074
1157
|
* async execute() {
|
|
1075
1158
|
* const { roleId } = this.params;
|
|
1076
|
-
* await this.reply(`assigning ${this.
|
|
1159
|
+
* await this.reply(`assigning ${this.users.size} member(s) to <@&${roleId}>`);
|
|
1077
1160
|
* }
|
|
1078
1161
|
* }
|
|
1079
1162
|
* ```
|
|
1080
1163
|
*/
|
|
1081
|
-
var SelectMenuHandler = class extends ComponentHandler {
|
|
1164
|
+
var SelectMenuHandler = class extends ComponentHandler {
|
|
1165
|
+
eventMember(key) {
|
|
1166
|
+
return this.event[key];
|
|
1167
|
+
}
|
|
1168
|
+
/** The ids this select picked. */
|
|
1169
|
+
get values() {
|
|
1170
|
+
return this.eventMember("values");
|
|
1171
|
+
}
|
|
1172
|
+
/** The users this select picked. */
|
|
1173
|
+
get users() {
|
|
1174
|
+
return this.eventMember("users");
|
|
1175
|
+
}
|
|
1176
|
+
/** The guild members behind the picked users. Discord resolves these only inside a guild. */
|
|
1177
|
+
get members() {
|
|
1178
|
+
return this.eventMember("members");
|
|
1179
|
+
}
|
|
1180
|
+
/** The roles this select picked. */
|
|
1181
|
+
get roles() {
|
|
1182
|
+
return this.eventMember("roles");
|
|
1183
|
+
}
|
|
1184
|
+
/** The channels this select picked. */
|
|
1185
|
+
get channels() {
|
|
1186
|
+
return this.eventMember("channels");
|
|
1187
|
+
}
|
|
1188
|
+
};
|
|
1082
1189
|
|
|
1083
1190
|
//#endregion
|
|
1084
1191
|
//#region src/handlers/event/EventHandler.ts
|
|
@@ -1137,9 +1244,9 @@ var EventHandler = class extends BaseHandler {
|
|
|
1137
1244
|
*/
|
|
1138
1245
|
async match(arms) {
|
|
1139
1246
|
const name = this.firedEvent;
|
|
1140
|
-
if (name === void 0) throw new
|
|
1141
|
-
const arm = arms[name];
|
|
1142
|
-
if (typeof arm !== "function") throw new
|
|
1247
|
+
if (name === void 0) throw new SeedcordTypeError(SeedcordErrorCode.EventMatchArmMissing, ["<no event>"]);
|
|
1248
|
+
const arm = Object.hasOwn(arms, name) ? arms[name] : void 0;
|
|
1249
|
+
if (typeof arm !== "function") throw new SeedcordTypeError(SeedcordErrorCode.EventMatchArmMissing, [name]);
|
|
1143
1250
|
return await arm(...this.getEvent());
|
|
1144
1251
|
}
|
|
1145
1252
|
};
|
|
@@ -1208,25 +1315,33 @@ var Paginator = class extends PaginatorBase {
|
|
|
1208
1315
|
Handler;
|
|
1209
1316
|
constructor(config) {
|
|
1210
1317
|
super(config);
|
|
1211
|
-
const
|
|
1318
|
+
const pageFor = (handler, n) => this.page(handler, n);
|
|
1212
1319
|
this.Handler = class Nav extends ButtonHandler {
|
|
1213
1320
|
async execute() {
|
|
1214
1321
|
await this.deferUpdate();
|
|
1215
|
-
const response = await
|
|
1322
|
+
const response = await pageFor(this, this.params.page);
|
|
1216
1323
|
await this.update(response);
|
|
1217
1324
|
}
|
|
1218
1325
|
};
|
|
1219
1326
|
}
|
|
1220
1327
|
/**
|
|
1221
|
-
* Render page
|
|
1222
|
-
*
|
|
1328
|
+
* Render a page as a {@link ReplyResponse} without sending anything.
|
|
1329
|
+
*
|
|
1330
|
+
* @param handler - The handler rendering the page, normally `this`.
|
|
1331
|
+
* @param n - The zero-based page.
|
|
1332
|
+
*/
|
|
1333
|
+
async page(handler, n) {
|
|
1334
|
+
return this.buildPage(contextOf(handler.getEvent(), handler.core), n);
|
|
1335
|
+
}
|
|
1336
|
+
/**
|
|
1337
|
+
* Send a page through the handler's sender. The sender picks reply, edit, or followUp from its ack state.
|
|
1223
1338
|
*
|
|
1224
1339
|
* @param handler - The handler starting the paginator, normally `this`.
|
|
1340
|
+
* @param n - The zero-based page to open on.
|
|
1225
1341
|
*/
|
|
1226
|
-
async start(handler) {
|
|
1227
|
-
const
|
|
1228
|
-
|
|
1229
|
-
return handler.getSender().send(response, { ephemeral: this.config.ephemeral ?? false });
|
|
1342
|
+
async start(handler, n = 0) {
|
|
1343
|
+
const response = await this.page(handler, n);
|
|
1344
|
+
return handler.sender.send(response, { ephemeral: this.config.ephemeral ?? false });
|
|
1230
1345
|
}
|
|
1231
1346
|
};
|
|
1232
1347
|
|
|
@@ -1239,7 +1354,7 @@ var Paginator = class extends PaginatorBase {
|
|
|
1239
1354
|
*
|
|
1240
1355
|
* @typeParam Item - The item type, inferred from the loader's return.
|
|
1241
1356
|
*/
|
|
1242
|
-
var ArraySource = class extends
|
|
1357
|
+
var ArraySource = class extends ArraySourceBase {};
|
|
1243
1358
|
/**
|
|
1244
1359
|
* A source for a large or unknown-length set you fetch one page at a time (SQL LIMIT/OFFSET, a paged API).
|
|
1245
1360
|
* The fetcher receives the page index and the page size and reports whether a next page exists. A cursor
|
|
@@ -1248,7 +1363,7 @@ var ArraySource = class extends ArraySource$1 {};
|
|
|
1248
1363
|
*
|
|
1249
1364
|
* @typeParam Item - The item type, inferred from the fetcher's slice.
|
|
1250
1365
|
*/
|
|
1251
|
-
var CursorSource = class extends
|
|
1366
|
+
var CursorSource = class extends CursorSourceBase {};
|
|
1252
1367
|
|
|
1253
1368
|
//#endregion
|
|
1254
1369
|
//#region src/subscribers/index.ts
|
|
@@ -1682,7 +1797,7 @@ var EventDispatcher = class {
|
|
|
1682
1797
|
this.logger.debug(`Attaching ${paint.sky.bold(eventName)} to the client with ${paint.mute(handlerEntries?.length ?? 0)} handler(s)`);
|
|
1683
1798
|
this.core.bot.client.on(eventName, (...args) => {
|
|
1684
1799
|
if (this.draining) return;
|
|
1685
|
-
this.core.bus[PublishDefault]("
|
|
1800
|
+
this.core.bus[PublishDefault]("eventDispatching", {
|
|
1686
1801
|
name: eventName,
|
|
1687
1802
|
args
|
|
1688
1803
|
});
|
|
@@ -2070,7 +2185,7 @@ var InteractionDispatcher = class {
|
|
|
2070
2185
|
const dispatch = new DispatchContext(routeIdOf(HandlerCtor) ?? routeId);
|
|
2071
2186
|
routeId = dispatch.routeId ?? routeId;
|
|
2072
2187
|
const handler = this.buildHandler(HandlerCtor, interaction, dispatch, key, !matched);
|
|
2073
|
-
if (handler instanceof RepliableHandler) sender = handler.
|
|
2188
|
+
if (handler instanceof RepliableHandler) sender = handler.sender;
|
|
2074
2189
|
const refusal = interaction.isAutocomplete() ? null : await this.gateRefusal(HandlerCtor, interaction, dispatch.routeId);
|
|
2075
2190
|
if (refusal) {
|
|
2076
2191
|
await this.answer(refusal.caught, interaction, routeId, sender, report);
|
|
@@ -2213,105 +2328,116 @@ const DRAIN_TASK_TIMEOUT_MS = 6e3;
|
|
|
2213
2328
|
const UNBIND_TIMEOUT_MS = 2e3;
|
|
2214
2329
|
const LOGOUT_TIMEOUT_MS = 2e3;
|
|
2215
2330
|
const loggerSlot = Symbol("seedcord:bot:logger");
|
|
2331
|
+
const initSlot = Symbol("seedcord:bot:init");
|
|
2332
|
+
const controllersSlot = Symbol("seedcord:bot:controllers");
|
|
2216
2333
|
/**
|
|
2217
2334
|
* The Discord client and its controllers. Access it through `core.bot`.
|
|
2218
2335
|
*/
|
|
2219
2336
|
var Bot = class {
|
|
2220
|
-
logger = new Logger("Bot", { channel: "bot" });
|
|
2337
|
+
#logger = new Logger("Bot", { channel: "bot" });
|
|
2221
2338
|
/** @internal */
|
|
2222
|
-
[loggerSlot] = this
|
|
2223
|
-
isInitialized = false;
|
|
2224
|
-
intents;
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
commandRegistry;
|
|
2339
|
+
[loggerSlot] = this.#logger;
|
|
2340
|
+
#isInitialized = false;
|
|
2341
|
+
#intents;
|
|
2342
|
+
#client;
|
|
2343
|
+
#emojiInjector;
|
|
2344
|
+
#interactions;
|
|
2345
|
+
#events;
|
|
2346
|
+
#commandRegistry;
|
|
2230
2347
|
/** @internal */
|
|
2231
2348
|
async onHmr(event) {
|
|
2232
|
-
if (this
|
|
2233
|
-
if (this
|
|
2234
|
-
if (this
|
|
2349
|
+
if (this.#interactions) await this.#interactions.onHmr(event);
|
|
2350
|
+
if (this.#events) await this.#events.onHmr(event);
|
|
2351
|
+
if (this.#commandRegistry) await this.#commandRegistry.onHmr(event);
|
|
2235
2352
|
}
|
|
2236
2353
|
/** @internal */
|
|
2237
2354
|
constructor(core) {
|
|
2238
|
-
this
|
|
2239
|
-
this
|
|
2240
|
-
if (core.config.bot.interactions.path) this
|
|
2241
|
-
if (core.config.bot.events.path) this
|
|
2242
|
-
if (core.config.bot.commands.path) this
|
|
2243
|
-
this
|
|
2244
|
-
this
|
|
2355
|
+
this.#intents = core.config.bot.clientOptions.intents;
|
|
2356
|
+
this.#client = new Client(core.config.bot.clientOptions);
|
|
2357
|
+
if (core.config.bot.interactions.path) this.#interactions = new InteractionDispatcher(core);
|
|
2358
|
+
if (core.config.bot.events.path) this.#events = new EventDispatcher(core);
|
|
2359
|
+
if (core.config.bot.commands.path) this.#commandRegistry = new CommandRegistry(core);
|
|
2360
|
+
this.#emojiInjector = new EmojiInjector(core);
|
|
2361
|
+
this.#registerShutdownTasks(core);
|
|
2362
|
+
}
|
|
2363
|
+
/** @internal */
|
|
2364
|
+
get [controllersSlot]() {
|
|
2365
|
+
return {
|
|
2366
|
+
interactions: this.#interactions,
|
|
2367
|
+
events: this.#events,
|
|
2368
|
+
commandRegistry: this.#commandRegistry
|
|
2369
|
+
};
|
|
2245
2370
|
}
|
|
2246
2371
|
/** @internal */
|
|
2247
2372
|
get applicationId() {
|
|
2248
|
-
const { application } = this
|
|
2373
|
+
const { application } = this.#client;
|
|
2249
2374
|
if (!application) throw new SeedcordError(SeedcordErrorCode.CoreApplicationUnavailable);
|
|
2250
2375
|
return application.id;
|
|
2251
2376
|
}
|
|
2252
|
-
registerShutdownTasks(core) {
|
|
2377
|
+
#registerShutdownTasks(core) {
|
|
2253
2378
|
core.shutdown.addTask(ShutdownPhase.Unbind, "stop-dispatch", () => {
|
|
2254
|
-
this
|
|
2379
|
+
this.#stopAccepting();
|
|
2255
2380
|
return Promise.resolve();
|
|
2256
2381
|
}, UNBIND_TIMEOUT_MS);
|
|
2257
|
-
core.shutdown.addTask(ShutdownPhase.Drain, "drain-dispatch", () => this
|
|
2258
|
-
core.shutdown.addTask(ShutdownPhase.Logout, "stop-bot", async () => await this
|
|
2382
|
+
core.shutdown.addTask(ShutdownPhase.Drain, "drain-dispatch", () => this.#drain(), DRAIN_TASK_TIMEOUT_MS);
|
|
2383
|
+
core.shutdown.addTask(ShutdownPhase.Logout, "stop-bot", async () => await this.#stop(), LOGOUT_TIMEOUT_MS);
|
|
2259
2384
|
}
|
|
2260
|
-
stopAccepting() {
|
|
2261
|
-
this
|
|
2262
|
-
this
|
|
2385
|
+
#stopAccepting() {
|
|
2386
|
+
this.#interactions?.stopAccepting();
|
|
2387
|
+
this.#events?.stopAccepting();
|
|
2263
2388
|
}
|
|
2264
|
-
async drain() {
|
|
2265
|
-
await Promise.allSettled([this
|
|
2389
|
+
async #drain() {
|
|
2390
|
+
await Promise.allSettled([this.#interactions?.drain(DISPATCH_DRAIN_TIMEOUT_MS), this.#events?.drain(DISPATCH_DRAIN_TIMEOUT_MS)]);
|
|
2266
2391
|
}
|
|
2267
2392
|
/** @internal */
|
|
2268
|
-
async
|
|
2269
|
-
if (this
|
|
2270
|
-
this
|
|
2393
|
+
async [initSlot]() {
|
|
2394
|
+
if (this.#isInitialized) return;
|
|
2395
|
+
this.#isInitialized = true;
|
|
2271
2396
|
const token = this.botToken;
|
|
2272
|
-
if (this
|
|
2273
|
-
if (this
|
|
2274
|
-
await this
|
|
2275
|
-
await this
|
|
2276
|
-
if (this
|
|
2277
|
-
await this
|
|
2278
|
-
assertGuildsIntent(this
|
|
2279
|
-
await this
|
|
2280
|
-
this
|
|
2281
|
-
this
|
|
2397
|
+
if (this.#interactions) await this.#interactions.init();
|
|
2398
|
+
if (this.#events) await this.#events.init();
|
|
2399
|
+
await this.#login(token);
|
|
2400
|
+
await this.#emojiInjector.init();
|
|
2401
|
+
if (this.#commandRegistry) {
|
|
2402
|
+
await this.#commandRegistry.init();
|
|
2403
|
+
assertGuildsIntent(this.#intents, this.#commandRegistry.allCommands());
|
|
2404
|
+
await this.#commandRegistry.setCommands();
|
|
2405
|
+
this.#interactions?.warnUnhandledRoutes(this.#commandRegistry.routeLeaves());
|
|
2406
|
+
this.#interactions?.warnUnhandledContextMenuRoutes(this.#commandRegistry.contextMenuLeaves());
|
|
2282
2407
|
}
|
|
2283
2408
|
}
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
this
|
|
2287
|
-
await this.logout();
|
|
2409
|
+
async #stop() {
|
|
2410
|
+
this.#client.removeAllListeners();
|
|
2411
|
+
await this.#logout();
|
|
2288
2412
|
}
|
|
2289
|
-
async login(token) {
|
|
2413
|
+
async #login(token) {
|
|
2290
2414
|
const ready = Promise.withResolvers();
|
|
2291
|
-
this.
|
|
2292
|
-
this.
|
|
2415
|
+
this.#client.once(Events.ClientReady, () => ready.resolve());
|
|
2416
|
+
this.#client.login(token);
|
|
2293
2417
|
await ready.promise;
|
|
2294
|
-
this
|
|
2418
|
+
this.#logger.info(`Logged in as ${paint.sky.bold(this.#client.user?.username)}!`);
|
|
2295
2419
|
return this;
|
|
2296
2420
|
}
|
|
2297
|
-
async logout() {
|
|
2298
|
-
await this.
|
|
2299
|
-
this
|
|
2421
|
+
async #logout() {
|
|
2422
|
+
await this.#client.destroy();
|
|
2423
|
+
this.#logger.info(paint.coral.bold("Logged out of Discord!"));
|
|
2300
2424
|
}
|
|
2301
2425
|
get client() {
|
|
2302
|
-
return this
|
|
2426
|
+
return this.#client;
|
|
2303
2427
|
}
|
|
2304
2428
|
};
|
|
2305
2429
|
__decorate([Envapt("DISCORD_BOT_TOKEN", { converter: (raw) => validateDiscordToken(raw) })], Bot.prototype, "botToken", void 0);
|
|
2306
|
-
/** @internal */
|
|
2307
2430
|
function botLoggerOf(bot) {
|
|
2308
2431
|
return bot[loggerSlot];
|
|
2309
2432
|
}
|
|
2433
|
+
function initBot(bot) {
|
|
2434
|
+
return bot[initSlot]();
|
|
2435
|
+
}
|
|
2310
2436
|
|
|
2311
2437
|
//#endregion
|
|
2312
2438
|
//#region src/version.ts
|
|
2313
2439
|
/** Package version */
|
|
2314
|
-
const version = "0.
|
|
2440
|
+
const version = "0.4.1";
|
|
2315
2441
|
|
|
2316
2442
|
//#endregion
|
|
2317
2443
|
//#region src/Seedcord.ts
|
|
@@ -2324,9 +2450,9 @@ var Seedcord = class Seedcord extends Pluggable {
|
|
|
2324
2450
|
/** @internal */
|
|
2325
2451
|
[SeedcordBrand] = true;
|
|
2326
2452
|
/** @internal */
|
|
2327
|
-
|
|
2453
|
+
[HostAugmentTarget] = "@seedcord/gateway";
|
|
2328
2454
|
/** @internal */
|
|
2329
|
-
|
|
2455
|
+
[HostVersion] = version;
|
|
2330
2456
|
healthCheck;
|
|
2331
2457
|
hmrManager;
|
|
2332
2458
|
/** @see {@link Bus} */
|
|
@@ -2358,16 +2484,17 @@ var Seedcord = class Seedcord extends Pluggable {
|
|
|
2358
2484
|
this.bot = new Bot(this);
|
|
2359
2485
|
this.rest = this.bot.client.rest;
|
|
2360
2486
|
this.rateLimiter = config.store ?? new MemoryRateLimiter();
|
|
2361
|
-
this.healthCheck = HealthCheck.fromOption(this
|
|
2487
|
+
this.healthCheck = HealthCheck.fromOption(shutdownOf(this), config.healthCheck);
|
|
2362
2488
|
this.registerStartupTasks();
|
|
2363
2489
|
}
|
|
2364
2490
|
/** The bot's discord username, populated after login. */
|
|
2365
2491
|
get username() {
|
|
2366
2492
|
return this.bot.client.user?.username;
|
|
2367
2493
|
}
|
|
2368
|
-
static reset() {
|
|
2369
|
-
super.reset();
|
|
2494
|
+
static reset(host) {
|
|
2495
|
+
if (!super.reset(host)) return false;
|
|
2370
2496
|
LoggerChannelRegistry.instance.configure({});
|
|
2497
|
+
return true;
|
|
2371
2498
|
}
|
|
2372
2499
|
registerStartupTasks() {
|
|
2373
2500
|
if (Envapter.isDevelopment || Envapter.isTest) this.registerHmrAwareModules();
|
|
@@ -2378,7 +2505,7 @@ var Seedcord = class Seedcord extends Pluggable {
|
|
|
2378
2505
|
});
|
|
2379
2506
|
this.startup.addTask(StartupPhase.Login, "bot-initialization", async () => {
|
|
2380
2507
|
botLoggerOf(this.bot).utils.initialization("Bot", "start");
|
|
2381
|
-
await this.bot
|
|
2508
|
+
await initBot(this.bot);
|
|
2382
2509
|
botLoggerOf(this.bot).utils.initialization("Bot", "end");
|
|
2383
2510
|
});
|
|
2384
2511
|
const { healthCheck } = this;
|
|
@@ -2397,8 +2524,8 @@ var Seedcord = class Seedcord extends Pluggable {
|
|
|
2397
2524
|
try {
|
|
2398
2525
|
await super.init();
|
|
2399
2526
|
} catch (caught) {
|
|
2400
|
-
await this.
|
|
2401
|
-
Seedcord.reset();
|
|
2527
|
+
await shutdownOf(this).run(1, false);
|
|
2528
|
+
Seedcord.reset(this);
|
|
2402
2529
|
throw caught;
|
|
2403
2530
|
}
|
|
2404
2531
|
return this;
|
|
@@ -2414,5 +2541,5 @@ var Seedcord = class Seedcord extends Pluggable {
|
|
|
2414
2541
|
};
|
|
2415
2542
|
|
|
2416
2543
|
//#endregion
|
|
2417
|
-
export { ArraySource, AutocompleteHandler, BaseHandler, ButtonHandler, ComponentHandler, CursorSource, Emojis, EventHandler, EventMiddleware, Gated, GatewayEmoji, IgnoreBots, InteractionHandler, InteractionMiddleware, MessageContextMenuHandler, Middleware, MiddlewareType, ModalHandler, Nsfw, Paginator, Plugin, RegisterEvent, RepliableHandler, Seedcord, SelectMenuHandler, SlashHandler, Subscriber, UserContextMenuHandler, WebhookLog, checkBotPermissions, checkPermissions, getConfirmation, hasPermsToAssign, version };
|
|
2544
|
+
export { ArraySource, AutocompleteHandler, BaseHandler, ButtonHandler, ComponentHandler, CursorSource, Emojis, EventHandler, EventMiddleware, Gated, GatewayEmoji, IgnoreBots, InteractionHandler, InteractionMiddleware, MessageContextMenuHandler, Middleware, MiddlewareType, ModalHandler, Nsfw, Paginator, Plugin, RegisterEvent, RepliableHandler, ReplySender, Seedcord, SelectMenuHandler, SlashHandler, Subscriber, UserContextMenuHandler, WebhookLog, WinstonConsoleSink, WinstonFileSink, checkBotPermissions, checkPermissions, getConfirmation, hasPermsToAssign, version };
|
|
2418
2545
|
//# sourceMappingURL=index.mjs.map
|