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