@seedcord/gateway 0.1.0-next.3 → 0.1.0-next.5
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.cjs +281 -794
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +111 -476
- package/dist/index.mjs +282 -781
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -9
package/dist/index.mjs
CHANGED
|
@@ -1,26 +1,24 @@
|
|
|
1
1
|
import "reflect-metadata";
|
|
2
2
|
import { installNodeDefaults } from "@seedcord/logger/node";
|
|
3
|
-
import { BaseReplySender, CommandMetadataKey, EventMetadataKey, GateNotice, GatedMetadataKey, HmrManager, InteractionMetadataKey, InteractionRouteKeys, InteractionRoutes, MiddlewareMetadataKey, MissingPermissions, NoticeCard, PAGE_MAX, PermissionNames,
|
|
3
|
+
import { BaseReplySender, CommandMetadataKey, EventMetadataKey, FaultThrottle, GateNotice, GatedMetadataKey, HmrManager, InteractionMetadataKey, InteractionRouteKeys, InteractionRoutes, MiddlewareMetadataKey, MissingPermissions, NoticeCard, PAGE_MAX, PermissionNames, PublishDefault, areRoutes, asError, decodeComponentRoute, deferFlags, dispatchedPayload, getDevChannel, outcomeFor, pageCursor, pickNotice, prefixOf, queuedMsFor, reportedWrite, routeIdOf, runHandlerGates, sendFlags, setBotColor, slowGateMonitor } from "@seedcord/core/internal";
|
|
4
4
|
import { SeedcordErrorCode } from "@seedcord/errors";
|
|
5
5
|
import { SeedcordError, SeedcordRangeError, SeedcordTypeError, validateDiscordToken } from "@seedcord/errors/internal";
|
|
6
|
-
import { BaseHandler, BuilderComponent, CustomId, DispatchContext, Fault, Notice, RepliableHandler as RepliableHandler$1, RowComponent, Silence, defineGate, paginate } from "@seedcord/core";
|
|
6
|
+
import { BaseHandler, BuilderComponent, Bus, CustomId, DispatchContext, Fault, Notice, RepliableHandler as RepliableHandler$1, RowComponent, Silence, Subscriber as Subscriber$1, WebhookLog as WebhookLog$1, defineGate, paginate } from "@seedcord/core";
|
|
7
7
|
import { Logger, LoggerChannelRegistry, paint } from "@seedcord/logger";
|
|
8
8
|
import { ActionRowBuilder, ButtonBuilder, ContextMenuCommandBuilder, SlashCommandBuilder, TextDisplayBuilder } from "@discordjs/builders";
|
|
9
9
|
import { buildSlashRoute, routeLeavesOf } from "@seedcord/utils/internal";
|
|
10
10
|
import { ApplicationCommandType, ButtonStyle, Client, Collection, ComponentType, DiscordAPIError, Events, Guild, GuildMember, Message, MessageFlags, PermissionFlagsBits, RESTJSONErrorCodes, Role, TextChannel, User, chatInputApplicationCommandMention } from "discord.js";
|
|
11
11
|
import * as crypto from "node:crypto";
|
|
12
|
-
import {
|
|
13
|
-
import
|
|
14
|
-
import { Converters, Envapter } from "envapt";
|
|
15
|
-
import { MessageFlags as MessageFlags$1, Routes, SeparatorSpacingSize } from "discord-api-types/v10";
|
|
16
|
-
import { filterCirculars, formatFilePath, hasKeys, stripAnsi, timestampFromSnowflake } from "@seedcord/utils";
|
|
17
|
-
import { DiscordAPIError as DiscordAPIError$1, REST } from "@discordjs/rest";
|
|
18
|
-
import { CoordinatedShutdown, CoordinatedStartup, HealthCheck, ShutdownPhase, ShutdownPhase as ShutdownPhase$1, StartupPhase, StartupPhase as StartupPhase$1 } from "@seedcord/core/node";
|
|
12
|
+
import { Plugin as Plugin$1 } from "@seedcord/core/plugin";
|
|
13
|
+
import { CoordinatedShutdown, CoordinatedStartup, HealthCheck, Pluggable, ShutdownPhase, StartupPhase, SubscriberLoader, settleWithin } from "@seedcord/core/node/internal";
|
|
19
14
|
import { MemoryRateLimiter } from "@seedcord/rate-limiter";
|
|
20
15
|
import { SeedcordBrand } from "@seedcord/types/internal";
|
|
16
|
+
import { Envapter } from "envapt";
|
|
17
|
+
import chalk from "chalk";
|
|
21
18
|
import { Envapt } from "envapt/legacy";
|
|
22
19
|
import { resolve } from "node:path";
|
|
23
20
|
import { HmrModuleHandler } from "@seedcord/core/hmr";
|
|
21
|
+
import { formatFilePath, hasKeys } from "@seedcord/utils";
|
|
24
22
|
import { traverseDirectory } from "@seedcord/utils/node";
|
|
25
23
|
|
|
26
24
|
export * from "@seedcord/errors"
|
|
@@ -29,11 +27,11 @@ export * from "@seedcord/core"
|
|
|
29
27
|
|
|
30
28
|
export * from "@seedcord/logger"
|
|
31
29
|
|
|
32
|
-
export * from "@seedcord/
|
|
30
|
+
export * from "@seedcord/rate-limiter"
|
|
33
31
|
|
|
34
32
|
export * from "@seedcord/utils"
|
|
35
33
|
|
|
36
|
-
export * from "@seedcord/
|
|
34
|
+
export * from "@seedcord/event-emitter"
|
|
37
35
|
|
|
38
36
|
//#region src/logger-bootstrap.ts
|
|
39
37
|
installNodeDefaults();
|
|
@@ -147,7 +145,7 @@ let MiddlewareType = /* @__PURE__ */ function(MiddlewareType) {
|
|
|
147
145
|
* Interaction middleware cannot specify event filters.
|
|
148
146
|
*
|
|
149
147
|
* @param type - Middleware kind from {@link MiddlewareType}
|
|
150
|
-
* @param priority - Ordering value where lower runs earlier. {@default `0`}
|
|
148
|
+
* @param priority - Ordering value where lower runs earlier. {@default `0` }
|
|
151
149
|
* @param options - Additional registration options
|
|
152
150
|
*
|
|
153
151
|
* @decorator
|
|
@@ -335,7 +333,7 @@ const Emojis = emojiStorage;
|
|
|
335
333
|
*/
|
|
336
334
|
var EmojiInjector = class {
|
|
337
335
|
core;
|
|
338
|
-
logger = new Logger("Emojis");
|
|
336
|
+
logger = new Logger("Emojis", { channel: "bot" });
|
|
339
337
|
constructor(core) {
|
|
340
338
|
this.core = core;
|
|
341
339
|
}
|
|
@@ -410,7 +408,7 @@ const MULTI_GUILD = Symbol("seedcord:commandmentions:multi-guild");
|
|
|
410
408
|
*/
|
|
411
409
|
var CommandMentionInjector = class {
|
|
412
410
|
core;
|
|
413
|
-
logger = new Logger("CommandMentions");
|
|
411
|
+
logger = new Logger("CommandMentions", { channel: "bot" });
|
|
414
412
|
constructor(core) {
|
|
415
413
|
this.core = core;
|
|
416
414
|
}
|
|
@@ -568,7 +566,7 @@ function checkBotPermissions(target, scope, inverse = false, errors) {
|
|
|
568
566
|
}
|
|
569
567
|
const me = target.guild.members.me;
|
|
570
568
|
if (!me) {
|
|
571
|
-
new Logger("checkBotPermissions").warn(`Bot member is unavailable in guild ${target.guild.id} while checking permissions in channel ${target.id}`);
|
|
569
|
+
new Logger("checkBotPermissions", { channel: "errors" }).warn(`Bot member is unavailable in guild ${target.guild.id} while checking permissions in channel ${target.id}`);
|
|
572
570
|
const names = scope.map((bit) => PermissionNames.get(bit) ?? String(bit));
|
|
573
571
|
throw new ((errors?.missingNotice) ?? MissingPermissions)(void 0, "The bot", names);
|
|
574
572
|
}
|
|
@@ -767,8 +765,11 @@ function seedState(interaction) {
|
|
|
767
765
|
*/
|
|
768
766
|
var ReplySender = class extends BaseReplySender {
|
|
769
767
|
interaction;
|
|
770
|
-
constructor(interaction, routeId) {
|
|
771
|
-
super(routeId, seedState(interaction)
|
|
768
|
+
constructor(interaction, routeId, bus) {
|
|
769
|
+
super(routeId, seedState(interaction), bus && {
|
|
770
|
+
bus,
|
|
771
|
+
interactionId: interaction.id
|
|
772
|
+
});
|
|
772
773
|
this.interaction = interaction;
|
|
773
774
|
}
|
|
774
775
|
async writeReply(response, opts) {
|
|
@@ -875,43 +876,9 @@ function slashRouteOf(interaction) {
|
|
|
875
876
|
return buildSlashRoute(interaction.commandName, subcommand, group);
|
|
876
877
|
}
|
|
877
878
|
|
|
878
|
-
//#endregion
|
|
879
|
-
//#region src/miscellaneous/FaultThrottle.ts
|
|
880
|
-
/**
|
|
881
|
-
* Drops duplicate fault reports inside a fixed window so a recurring fault (a database outage on a
|
|
882
|
-
* hot path) reports once per window instead of flooding the webhook. Keyed on a structural string, so
|
|
883
|
-
* the map stays small, and a stale entry is evicted on the next read of its key.
|
|
884
|
-
*
|
|
885
|
-
* @internal
|
|
886
|
-
*/
|
|
887
|
-
var FaultThrottle = class {
|
|
888
|
-
windowMs;
|
|
889
|
-
now;
|
|
890
|
-
lastReportedAt = /* @__PURE__ */ new Map();
|
|
891
|
-
constructor(windowMs = 6e4, now = () => Date.now()) {
|
|
892
|
-
this.windowMs = windowMs;
|
|
893
|
-
this.now = now;
|
|
894
|
-
}
|
|
895
|
-
/** Evicts the entry for this key if it has aged out, so the map never holds stale keys forever. */
|
|
896
|
-
shouldReport(key) {
|
|
897
|
-
const last = this.lastReportedAt.get(key);
|
|
898
|
-
if (last === void 0) return true;
|
|
899
|
-
if (this.now() - last < this.windowMs) return false;
|
|
900
|
-
this.lastReportedAt.delete(key);
|
|
901
|
-
return true;
|
|
902
|
-
}
|
|
903
|
-
markReported(key) {
|
|
904
|
-
this.lastReportedAt.set(key, this.now());
|
|
905
|
-
}
|
|
906
|
-
clear() {
|
|
907
|
-
this.lastReportedAt.clear();
|
|
908
|
-
}
|
|
909
|
-
};
|
|
910
|
-
|
|
911
879
|
//#endregion
|
|
912
880
|
//#region src/miscellaneous/extractErrorResponse.ts
|
|
913
|
-
const logger$2 = new Logger("ErrorsHandling");
|
|
914
|
-
const faultThrottle = new FaultThrottle();
|
|
881
|
+
const logger$2 = new Logger("ErrorsHandling", { channel: "errors" });
|
|
915
882
|
/**
|
|
916
883
|
* Processes an error into a user-facing reply and routes its report.
|
|
917
884
|
*
|
|
@@ -941,29 +908,30 @@ function extractErrorResponse(error, core, origin) {
|
|
|
941
908
|
response: override ? new override(uuid).render(ctx) : new Fault().render(ctx)
|
|
942
909
|
};
|
|
943
910
|
}
|
|
944
|
-
function withThrottle(origin, error, publish) {
|
|
911
|
+
function withThrottle(core, origin, error, publish) {
|
|
912
|
+
const throttle = FaultThrottle.for(core);
|
|
945
913
|
const key = faultKey(origin, error);
|
|
946
|
-
if (!
|
|
947
|
-
logger$2.
|
|
914
|
+
if (!throttle.shouldReport(key)) {
|
|
915
|
+
logger$2.debug(`throttled duplicate fault ${key}`);
|
|
948
916
|
return;
|
|
949
917
|
}
|
|
950
918
|
publish();
|
|
951
|
-
|
|
919
|
+
throttle.markReported(key);
|
|
952
920
|
}
|
|
953
921
|
function reportFault(denial, core, origin, uuid) {
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
if (origin.interaction) core.bus
|
|
922
|
+
logger$2.error(`${denial.name}: ${uuid}`, denial);
|
|
923
|
+
withThrottle(core, origin, denial, () => {
|
|
924
|
+
if (origin.interaction) core.bus[PublishDefault]("handledException", {
|
|
957
925
|
denial,
|
|
958
926
|
uuid,
|
|
959
927
|
source: buildInteractionSource(origin.interaction)
|
|
960
928
|
});
|
|
961
|
-
else if (origin.event) core.bus
|
|
929
|
+
else if (origin.event) core.bus[PublishDefault]("handledException", {
|
|
962
930
|
denial,
|
|
963
931
|
uuid,
|
|
964
932
|
source: buildEventSource(origin.event, origin)
|
|
965
933
|
});
|
|
966
|
-
else core.bus
|
|
934
|
+
else core.bus[PublishDefault]("unknownException", {
|
|
967
935
|
uuid,
|
|
968
936
|
error: denial,
|
|
969
937
|
...scalarActors(origin),
|
|
@@ -978,10 +946,10 @@ function causeLine(error) {
|
|
|
978
946
|
return `\ncaused by ${cause.name}: ${first ?? cause.message}`;
|
|
979
947
|
}
|
|
980
948
|
function reportRawFault(error, core, origin, uuid) {
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
core.bus
|
|
949
|
+
if (core.config.errors?.errorStack ?? false) logger$2.error(uuid, error);
|
|
950
|
+
else logger$2.error(`${uuid} | ${error.message}${causeLine(error)}`);
|
|
951
|
+
withThrottle(core, origin, error, () => {
|
|
952
|
+
core.bus[PublishDefault]("unknownException", {
|
|
985
953
|
uuid,
|
|
986
954
|
error,
|
|
987
955
|
...scalarActors(origin),
|
|
@@ -1124,112 +1092,6 @@ async function getConfirmation(interaction, prompt, options) {
|
|
|
1124
1092
|
return confirmed;
|
|
1125
1093
|
}
|
|
1126
1094
|
|
|
1127
|
-
//#endregion
|
|
1128
|
-
//#region src/interfaces/Plugin.ts
|
|
1129
|
-
/**
|
|
1130
|
-
* Base class for Seedcord plugins
|
|
1131
|
-
*
|
|
1132
|
-
* Extend this class to create plugins that integrate with the Seedcord lifecycle.
|
|
1133
|
-
* Plugins have access to the core instance and must implement initialization logic.
|
|
1134
|
-
*/
|
|
1135
|
-
var Plugin = class extends TypedEventEmitter {
|
|
1136
|
-
pluggable;
|
|
1137
|
-
name = this.constructor.name;
|
|
1138
|
-
constructor(pluggable) {
|
|
1139
|
-
super();
|
|
1140
|
-
this.pluggable = pluggable;
|
|
1141
|
-
}
|
|
1142
|
-
/**
|
|
1143
|
-
* Reloads plugin state on an HMR update. The base implementation is a no-op, override it in your plugin.
|
|
1144
|
-
* @virtual
|
|
1145
|
-
*/
|
|
1146
|
-
onHmr(_event) {
|
|
1147
|
-
return Promise.resolve();
|
|
1148
|
-
}
|
|
1149
|
-
/**
|
|
1150
|
-
* Registers critical file patterns that should trigger a full restart when changed in Dev HMR.
|
|
1151
|
-
* @param patterns - Glob patterns relative to the project root
|
|
1152
|
-
*/
|
|
1153
|
-
registerCriticalFiles(patterns) {
|
|
1154
|
-
getDevChannel()?.send("seedcord:register-critical-files", { patterns });
|
|
1155
|
-
}
|
|
1156
|
-
/** @internal */
|
|
1157
|
-
removeListener(event, listener) {
|
|
1158
|
-
return super.removeListener(event, listener);
|
|
1159
|
-
}
|
|
1160
|
-
/** @internal */
|
|
1161
|
-
removeAllListeners(event) {
|
|
1162
|
-
return super.removeAllListeners(event);
|
|
1163
|
-
}
|
|
1164
|
-
};
|
|
1165
|
-
/**
|
|
1166
|
-
* Base class for objects that can have plugins attached.
|
|
1167
|
-
*
|
|
1168
|
-
* Provides plugin attachment capabilities and lifecycle management. Plugins are attached during
|
|
1169
|
-
* configuration and initialized during startup. Not constructed directly, the host is a {@link Seedcord}.
|
|
1170
|
-
*/
|
|
1171
|
-
var Pluggable = class Pluggable extends TypedEventEmitter {
|
|
1172
|
-
isInitialized = false;
|
|
1173
|
-
shutdown;
|
|
1174
|
-
startup;
|
|
1175
|
-
plugins = [];
|
|
1176
|
-
static PLUGIN_INIT_TIMEOUT_MS = 15e3;
|
|
1177
|
-
constructor(shutdown, startup) {
|
|
1178
|
-
super();
|
|
1179
|
-
this.shutdown = shutdown;
|
|
1180
|
-
this.startup = startup;
|
|
1181
|
-
}
|
|
1182
|
-
/** @internal */
|
|
1183
|
-
async init() {
|
|
1184
|
-
if (this.isInitialized) return this;
|
|
1185
|
-
await this.startup.run();
|
|
1186
|
-
this.isInitialized = true;
|
|
1187
|
-
return this;
|
|
1188
|
-
}
|
|
1189
|
-
/**
|
|
1190
|
-
* Attaches a plugin to this instance
|
|
1191
|
-
*
|
|
1192
|
-
* Plugins provide external functionality and are initialized during the specified startup phase.
|
|
1193
|
-
* The plugin instance becomes available as a property in `core` wherever it's available.
|
|
1194
|
-
*
|
|
1195
|
-
* Make sure to augment the {@link Core} interface with the plugin type to ensure TypeScript recognizes it and provides intellisense.
|
|
1196
|
-
*
|
|
1197
|
-
* @typeParam Key - The property name for accessing the plugin
|
|
1198
|
-
* @typeParam Ctor - The plugin constructor type
|
|
1199
|
-
* @param key - Property name to access the plugin instance
|
|
1200
|
-
* @param Plugin - Plugin constructor class
|
|
1201
|
-
* @param startupPhase - When during startup to initialize this plugin ({@link StartupPhase})
|
|
1202
|
-
* @param args - Additional arguments to pass to the plugin constructor
|
|
1203
|
-
* @returns This instance with the plugin attached as a typed property
|
|
1204
|
-
* @throws A **SeedcordError** When called after initialization or if key already exists
|
|
1205
|
-
* @example
|
|
1206
|
-
* ```typescript
|
|
1207
|
-
* seedcord.attach('db', Mongo, StartupPhase.Configuration, { uri: 'mongodb://...', name: 'seedcord', dir: ... })
|
|
1208
|
-
* ```
|
|
1209
|
-
*/
|
|
1210
|
-
attach(key, Plugin, startupPhase, ...args) {
|
|
1211
|
-
if (this.isInitialized) throw new SeedcordError(SeedcordErrorCode.CorePluginAfterInit);
|
|
1212
|
-
if (key in this) throw new SeedcordError(SeedcordErrorCode.CorePluginKeyExists, [key]);
|
|
1213
|
-
const instance = new Plugin(this, ...args);
|
|
1214
|
-
this.plugins.push(instance);
|
|
1215
|
-
const entry = { [key]: instance };
|
|
1216
|
-
this.startup.addTask(startupPhase, `Plugin:${key}`, async () => {
|
|
1217
|
-
instance.logger.utils.initialization(key, "start");
|
|
1218
|
-
await instance.init();
|
|
1219
|
-
instance.logger.utils.initialization(key, "end");
|
|
1220
|
-
}, Pluggable.PLUGIN_INIT_TIMEOUT_MS);
|
|
1221
|
-
return Object.assign(this, entry);
|
|
1222
|
-
}
|
|
1223
|
-
/** @internal */
|
|
1224
|
-
removeListener(event, listener) {
|
|
1225
|
-
return super.removeListener(event, listener);
|
|
1226
|
-
}
|
|
1227
|
-
/** @internal */
|
|
1228
|
-
removeAllListeners(event) {
|
|
1229
|
-
return super.removeAllListeners(event);
|
|
1230
|
-
}
|
|
1231
|
-
};
|
|
1232
|
-
|
|
1233
1095
|
//#endregion
|
|
1234
1096
|
//#region src/handlers/BaseHandler.ts
|
|
1235
1097
|
/**
|
|
@@ -1255,7 +1117,7 @@ var BaseHandler$1 = class extends BaseHandler {
|
|
|
1255
1117
|
*/
|
|
1256
1118
|
var InteractionMiddleware = class extends BaseHandler$1 {
|
|
1257
1119
|
constructor(event, core) {
|
|
1258
|
-
super(event, core);
|
|
1120
|
+
super(event, core, void 0, "interactions");
|
|
1259
1121
|
}
|
|
1260
1122
|
};
|
|
1261
1123
|
|
|
@@ -1275,8 +1137,8 @@ var RepliableHandler = class extends RepliableHandler$1 {
|
|
|
1275
1137
|
getEvent() {
|
|
1276
1138
|
return this.event;
|
|
1277
1139
|
}
|
|
1278
|
-
buildSender(event,
|
|
1279
|
-
return new ReplySender(event, routeId);
|
|
1140
|
+
buildSender(event, core, routeId) {
|
|
1141
|
+
return new ReplySender(event, routeId, core.bus);
|
|
1280
1142
|
}
|
|
1281
1143
|
};
|
|
1282
1144
|
|
|
@@ -1431,7 +1293,7 @@ var ContextMenuHandler = class extends InteractionHandler {
|
|
|
1431
1293
|
*/
|
|
1432
1294
|
var AutocompleteHandler = class extends BaseHandler$1 {
|
|
1433
1295
|
constructor(event, core, dispatch) {
|
|
1434
|
-
super(event, core, dispatch);
|
|
1296
|
+
super(event, core, dispatch, "interactions");
|
|
1435
1297
|
}
|
|
1436
1298
|
decodedFocused;
|
|
1437
1299
|
/**
|
|
@@ -1482,7 +1344,11 @@ var AutocompleteHandler = class extends BaseHandler$1 {
|
|
|
1482
1344
|
}
|
|
1483
1345
|
/** Send autocomplete suggestions, callback type 8. Prefer {@link match}, which restricts each field's choices to its declared type. */
|
|
1484
1346
|
async respond(choices) {
|
|
1485
|
-
|
|
1347
|
+
const routeId = this.dispatch?.routeId ?? "autocomplete";
|
|
1348
|
+
await reportedWrite({
|
|
1349
|
+
bus: this.core.bus,
|
|
1350
|
+
interactionId: this.event.id
|
|
1351
|
+
}, routeId, "respond", () => this.event.respond(choices));
|
|
1486
1352
|
}
|
|
1487
1353
|
/** The firing command route, for a field whose completion differs per registered command. */
|
|
1488
1354
|
get route() {
|
|
@@ -1675,7 +1541,7 @@ var SelectMenuHandler = class extends ComponentHandler {};
|
|
|
1675
1541
|
var EventHandler = class extends BaseHandler$1 {
|
|
1676
1542
|
firedEvent;
|
|
1677
1543
|
constructor(event, core, eventName) {
|
|
1678
|
-
super(event, core);
|
|
1544
|
+
super(event, core, void 0, "events");
|
|
1679
1545
|
this.firedEvent = eventName;
|
|
1680
1546
|
}
|
|
1681
1547
|
/**
|
|
@@ -1728,7 +1594,7 @@ var EventHandler = class extends BaseHandler$1 {
|
|
|
1728
1594
|
var EventMiddleware = class extends BaseHandler$1 {
|
|
1729
1595
|
firedEvent;
|
|
1730
1596
|
constructor(event, core, eventName) {
|
|
1731
|
-
super(event, core);
|
|
1597
|
+
super(event, core, void 0, "events");
|
|
1732
1598
|
this.firedEvent = eventName;
|
|
1733
1599
|
}
|
|
1734
1600
|
/**
|
|
@@ -1913,10 +1779,15 @@ var Paginator = class {
|
|
|
1913
1779
|
}
|
|
1914
1780
|
};
|
|
1915
1781
|
}
|
|
1916
|
-
/**
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1782
|
+
/**
|
|
1783
|
+
* Render page 0 and send it through the handler's sender, picking reply or followUp from its ack state.
|
|
1784
|
+
*
|
|
1785
|
+
* @param handler - The handler starting the paginator, normally `this`.
|
|
1786
|
+
*/
|
|
1787
|
+
async start(handler) {
|
|
1788
|
+
const interaction = handler.getEvent();
|
|
1789
|
+
const response = await this.page(contextOf(interaction, handler.core), 0);
|
|
1790
|
+
return handler.getSender().send(response, { ephemeral: this.config.ephemeral ?? false });
|
|
1920
1791
|
}
|
|
1921
1792
|
/** Render a page as a {@link ReplyResponse}. To post it elsewhere, add `flags: MessageFlags.IsComponentsV2`. */
|
|
1922
1793
|
async page(ctx, n) {
|
|
@@ -1976,250 +1847,32 @@ var CursorSource = class {
|
|
|
1976
1847
|
};
|
|
1977
1848
|
|
|
1978
1849
|
//#endregion
|
|
1979
|
-
//#region src/subscribers/
|
|
1980
|
-
/**
|
|
1981
|
-
* Abstract base class for handling application subscribers.
|
|
1982
|
-
*
|
|
1983
|
-
* Provides type-safe access to subscribers data and the core framework instance.
|
|
1984
|
-
* Extend this class to create custom subscriber handlers.
|
|
1985
|
-
*
|
|
1986
|
-
* @typeParam KeyOfSubscribers - The specific subscriber type this handler processes
|
|
1987
|
-
*/
|
|
1988
|
-
var Subscriber = class {
|
|
1989
|
-
data;
|
|
1990
|
-
core;
|
|
1991
|
-
logger;
|
|
1992
|
-
/**
|
|
1993
|
-
* Instantiates the subscriber with event data and core.
|
|
1994
|
-
*
|
|
1995
|
-
* @param data - The subscriber event data
|
|
1996
|
-
* @param core - The core framework instance
|
|
1997
|
-
*/
|
|
1998
|
-
constructor(data, core) {
|
|
1999
|
-
this.data = data;
|
|
2000
|
-
this.core = core;
|
|
2001
|
-
this.logger = new Logger(this.constructor.name);
|
|
2002
|
-
}
|
|
2003
|
-
};
|
|
2004
|
-
|
|
2005
|
-
//#endregion
|
|
2006
|
-
//#region src/subscribers/decorators/Subscribe.ts
|
|
1850
|
+
//#region src/subscribers/index.ts
|
|
2007
1851
|
/**
|
|
2008
|
-
*
|
|
2009
|
-
*
|
|
2010
|
-
* Associates the decorated class with a subscriber event type for automatic
|
|
2011
|
-
* registration and execution when the subscriber is emitted.
|
|
1852
|
+
* Base class for a gateway subscriber, binding `this.core` to the gateway {@link Core}.
|
|
2012
1853
|
*
|
|
2013
|
-
* @typeParam
|
|
2014
|
-
* @param subscriber - The subscriber event name to register for
|
|
2015
|
-
* @param options - Options to configure the subscriber handler registration.
|
|
2016
|
-
* @decorator
|
|
2017
|
-
* @example
|
|
2018
|
-
* ```typescript
|
|
2019
|
-
* \@Subscribe('userJoin')
|
|
2020
|
-
* class WelcomeHandler extends Subscriber<'userJoin'> {
|
|
2021
|
-
* async execute() {
|
|
2022
|
-
* // Handle user join event
|
|
2023
|
-
* }
|
|
2024
|
-
* }
|
|
2025
|
-
* ```
|
|
2026
|
-
* @example
|
|
2027
|
-
* ```ts
|
|
2028
|
-
* \@Subscribe('userJoin', { frequency: 'once' })
|
|
2029
|
-
* // or
|
|
2030
|
-
* \@Subscribe('userJoin')
|
|
2031
|
-
* ```
|
|
1854
|
+
* @typeParam KeyOfSubscribers - The subscription key this handler receives
|
|
2032
1855
|
*/
|
|
2033
|
-
|
|
2034
|
-
return function(constructor) {
|
|
2035
|
-
const meta = {
|
|
2036
|
-
subscriber,
|
|
2037
|
-
frequency: options?.frequency
|
|
2038
|
-
};
|
|
2039
|
-
Reflect.defineMetadata(SubscribeMetadataKey, meta, constructor);
|
|
2040
|
-
};
|
|
2041
|
-
}
|
|
2042
|
-
|
|
2043
|
-
//#endregion
|
|
2044
|
-
//#region src/subscribers/decorators/WebhookUrl.ts
|
|
2045
|
-
/**
|
|
2046
|
-
* Declares the environment variable a {@link WebhookLog} reporter reads its webhook url from.
|
|
2047
|
-
*
|
|
2048
|
-
* An unset variable disables the reporter with a boot warning. A set but malformed value throws
|
|
2049
|
-
* at boot.
|
|
2050
|
-
*
|
|
2051
|
-
* @param envKey - The environment variable name holding the webhook url
|
|
2052
|
-
* @decorator
|
|
2053
|
-
* @example
|
|
2054
|
-
* ```ts
|
|
2055
|
-
* \@Subscribe('memberBanned')
|
|
2056
|
-
* \@WebhookUrl('BAN_LOG_WEBHOOK_URL')
|
|
2057
|
-
* class BanLog extends WebhookLog<'memberBanned'> {
|
|
2058
|
-
* report() {
|
|
2059
|
-
* return { components: [new BanCard(this.data).component] };
|
|
2060
|
-
* }
|
|
2061
|
-
* }
|
|
2062
|
-
* ```
|
|
2063
|
-
*/
|
|
2064
|
-
function WebhookUrl(envKey) {
|
|
2065
|
-
return function(constructor) {
|
|
2066
|
-
Reflect.defineMetadata(WebhookUrlMetadataKey, envKey, constructor);
|
|
2067
|
-
};
|
|
2068
|
-
}
|
|
2069
|
-
|
|
2070
|
-
//#endregion
|
|
2071
|
-
//#region src/miscellaneous/flagsFor.ts
|
|
2072
|
-
/** @internal */
|
|
2073
|
-
function flagsFor(ephemeral) {
|
|
2074
|
-
let flags = MessageFlags$1.IsComponentsV2;
|
|
2075
|
-
if (ephemeral) flags |= MessageFlags$1.Ephemeral;
|
|
2076
|
-
return flags;
|
|
2077
|
-
}
|
|
2078
|
-
|
|
2079
|
-
//#endregion
|
|
2080
|
-
//#region src/subscribers/bases/webhookHelpers.ts
|
|
2081
|
-
const DISCORD_WEBHOOK_REGEX = new RegExp(String.raw`^https?:\/\/(?:canary\.|ptb\.)?discord(?:app)?\.com\/api\/webhooks\/\d+\/[\w$-]+$`);
|
|
2082
|
-
function isDiscordWebhookUrl(value) {
|
|
2083
|
-
return URL.canParse(value) && DISCORD_WEBHOOK_REGEX.test(value);
|
|
2084
|
-
}
|
|
2085
|
-
function jsonAttachment(name, description, data) {
|
|
2086
|
-
const content = filterCirculars(data);
|
|
2087
|
-
return {
|
|
2088
|
-
name,
|
|
2089
|
-
description,
|
|
2090
|
-
data: Buffer.from(JSON.stringify(content, void 0, 2), "utf8")
|
|
2091
|
-
};
|
|
2092
|
-
}
|
|
2093
|
-
function neutralizeFences(text) {
|
|
2094
|
-
return text.replaceAll("```", "```");
|
|
2095
|
-
}
|
|
2096
|
-
/** An error's stack with its direct cause appended (one level), ANSI-stripped for the webhook payload. */
|
|
2097
|
-
function errorReport(error) {
|
|
2098
|
-
let report = error.stack ?? `${error.name}: ${error.message}`;
|
|
2099
|
-
if (Error.isError(error.cause)) {
|
|
2100
|
-
const cause = error.cause.stack ?? `${error.cause.name}: ${error.cause.message}`;
|
|
2101
|
-
report += `\n\nCaused by:\n${cause}`;
|
|
2102
|
-
}
|
|
2103
|
-
const stripped = neutralizeFences(stripAnsi(report));
|
|
2104
|
-
return stripped.length > 1800 ? `${stripped.slice(0, 1799)}…` : stripped;
|
|
2105
|
-
}
|
|
2106
|
-
var WebhookSeparator = class extends BuilderComponent {
|
|
2107
|
-
constructor() {
|
|
2108
|
-
super("separator");
|
|
2109
|
-
this.instance.setSpacing(SeparatorSpacingSize.Small).setDivider(true);
|
|
2110
|
-
}
|
|
2111
|
-
};
|
|
2112
|
-
|
|
2113
|
-
//#endregion
|
|
2114
|
-
//#region src/subscribers/bases/WebhookSender.ts
|
|
2115
|
-
const HTTP_NOT_FOUND = 404;
|
|
2116
|
-
const HTTP_UNAUTHORIZED = 401;
|
|
1856
|
+
var Subscriber = class extends Subscriber$1 {};
|
|
2117
1857
|
/**
|
|
2118
|
-
*
|
|
1858
|
+
* Base class for a gateway subscriber that delivers its event to a Discord webhook, binding
|
|
1859
|
+
* `this.core` to the gateway {@link Core}.
|
|
2119
1860
|
*
|
|
2120
|
-
* @
|
|
1861
|
+
* @typeParam KeyOfSubscribers - The subscription key this reporter receives
|
|
2121
1862
|
*/
|
|
2122
|
-
var
|
|
2123
|
-
rest = new REST();
|
|
2124
|
-
route;
|
|
2125
|
-
constructor(url) {
|
|
2126
|
-
const match = /\/webhooks\/(\d+)\/([\w$-]+)$/.exec(url);
|
|
2127
|
-
if (!match?.[1] || !match[2]) throw new SeedcordError(SeedcordErrorCode.ConfigWebhookUrlInvalid, ["The url"]);
|
|
2128
|
-
this.route = Routes.webhook(match[1], match[2]);
|
|
2129
|
-
}
|
|
2130
|
-
/**
|
|
2131
|
-
* Fetches the webhook object, sending no message. Returns `missing` on a 404 or 401 from
|
|
2132
|
-
* Discord and `unreachable` on any other failure.
|
|
2133
|
-
*/
|
|
2134
|
-
async verify() {
|
|
2135
|
-
try {
|
|
2136
|
-
await this.rest.get(this.route, { auth: false });
|
|
2137
|
-
return "ok";
|
|
2138
|
-
} catch (error) {
|
|
2139
|
-
return error instanceof DiscordAPIError$1 && (error.status === HTTP_NOT_FOUND || error.status === HTTP_UNAUTHORIZED) ? "missing" : "unreachable";
|
|
2140
|
-
}
|
|
2141
|
-
}
|
|
2142
|
-
async send(options) {
|
|
2143
|
-
const { flags, username, components, files } = options;
|
|
2144
|
-
await this.rest.post(this.route, {
|
|
2145
|
-
auth: false,
|
|
2146
|
-
query: new URLSearchParams({
|
|
2147
|
-
with_components: "true",
|
|
2148
|
-
wait: "true"
|
|
2149
|
-
}),
|
|
2150
|
-
body: {
|
|
2151
|
-
flags,
|
|
2152
|
-
username,
|
|
2153
|
-
components: components.map((component) => component.toJSON()),
|
|
2154
|
-
...files?.length && { attachments: files.map((file, id) => ({
|
|
2155
|
-
id,
|
|
2156
|
-
filename: file.name,
|
|
2157
|
-
description: file.description
|
|
2158
|
-
})) }
|
|
2159
|
-
},
|
|
2160
|
-
...files?.length && { files: files.map((file) => ({
|
|
2161
|
-
name: file.name,
|
|
2162
|
-
data: file.data
|
|
2163
|
-
})) }
|
|
2164
|
-
});
|
|
2165
|
-
}
|
|
2166
|
-
};
|
|
1863
|
+
var WebhookLog = class extends WebhookLog$1 {};
|
|
2167
1864
|
|
|
2168
1865
|
//#endregion
|
|
2169
|
-
//#region src/
|
|
1866
|
+
//#region src/plugin.ts
|
|
2170
1867
|
/**
|
|
2171
|
-
* Base class for
|
|
1868
|
+
* Base class for a gateway plugin, binding `this.core` to the gateway {@link Core}.
|
|
2172
1869
|
*
|
|
2173
|
-
*
|
|
2174
|
-
*
|
|
2175
|
-
* the reporter is skipped at registration with a boot warning.
|
|
1870
|
+
* A plugin that runs on either transport extends the base from `@seedcord/core/plugin`, whose
|
|
1871
|
+
* `this.core` carries the shared members only.
|
|
2176
1872
|
*
|
|
2177
|
-
* @typeParam
|
|
1873
|
+
* @typeParam Opts - The transport, runtime, and capabilities the plugin declares.
|
|
2178
1874
|
*/
|
|
2179
|
-
var
|
|
2180
|
-
static senders = /* @__PURE__ */ new Map();
|
|
2181
|
-
async execute() {
|
|
2182
|
-
const url = WebhookLog.urlOf(WebhookLog.envKeyOf(this.constructor));
|
|
2183
|
-
if (url === null) {
|
|
2184
|
-
this.logger.warn(`${chalk.bold(this.constructor.name)} has no webhook url set, this reporter is disabled`);
|
|
2185
|
-
return;
|
|
2186
|
-
}
|
|
2187
|
-
const { username, components, files } = await this.report();
|
|
2188
|
-
try {
|
|
2189
|
-
await WebhookLog.senderFor(url).send({
|
|
2190
|
-
flags: flagsFor(false),
|
|
2191
|
-
username: username ?? this.constructor.name,
|
|
2192
|
-
components,
|
|
2193
|
-
files
|
|
2194
|
-
});
|
|
2195
|
-
} catch (error) {
|
|
2196
|
-
this.logger.error("Failed to send webhook report", error);
|
|
2197
|
-
}
|
|
2198
|
-
}
|
|
2199
|
-
/** @internal */
|
|
2200
|
-
static senderFor(url) {
|
|
2201
|
-
let sender = WebhookLog.senders.get(url);
|
|
2202
|
-
if (!sender) {
|
|
2203
|
-
sender = new WebhookSender(url);
|
|
2204
|
-
WebhookLog.senders.set(url, sender);
|
|
2205
|
-
}
|
|
2206
|
-
return sender;
|
|
2207
|
-
}
|
|
2208
|
-
/** @internal */
|
|
2209
|
-
static envKeyOf(ctor) {
|
|
2210
|
-
const envKey = Reflect.getMetadata(WebhookUrlMetadataKey, ctor);
|
|
2211
|
-
if (!envKey) throw new SeedcordError(SeedcordErrorCode.DecoratorWebhookUrlMissing, [ctor.name]);
|
|
2212
|
-
return envKey;
|
|
2213
|
-
}
|
|
2214
|
-
/** @internal Returns null when the env var is unset, throws when set but malformed. */
|
|
2215
|
-
static urlOf(envKey) {
|
|
2216
|
-
if (!Envapter.has(envKey)) return null;
|
|
2217
|
-
const raw = Envapter.getRequired(envKey, Converters.String).trim();
|
|
2218
|
-
if (raw === "") return null;
|
|
2219
|
-
if (!isDiscordWebhookUrl(raw)) throw new SeedcordError(SeedcordErrorCode.ConfigWebhookUrlInvalid, [envKey]);
|
|
2220
|
-
return raw;
|
|
2221
|
-
}
|
|
2222
|
-
};
|
|
1875
|
+
var Plugin = class extends Plugin$1 {};
|
|
2223
1876
|
|
|
2224
1877
|
//#endregion
|
|
2225
1878
|
//#region src/bot/utilities/miscellaneous/contextMenuLeaves.ts
|
|
@@ -2275,8 +1928,7 @@ function slashRouteLeaves(commands) {
|
|
|
2275
1928
|
var CommandRegistry = class {
|
|
2276
1929
|
core;
|
|
2277
1930
|
onDeployed;
|
|
2278
|
-
|
|
2279
|
-
logger = new Logger("Commands");
|
|
1931
|
+
logger = new Logger("Commands", { channel: "commands" });
|
|
2280
1932
|
isInitialised = false;
|
|
2281
1933
|
globalCommands = [];
|
|
2282
1934
|
guildCommands = new Collection();
|
|
@@ -2363,7 +2015,7 @@ var CommandRegistry = class {
|
|
|
2363
2015
|
this.registerCommand(exported, rel);
|
|
2364
2016
|
this.hmrHandler?.trackHandler(fullPath, exported);
|
|
2365
2017
|
}
|
|
2366
|
-
}
|
|
2018
|
+
});
|
|
2367
2019
|
}
|
|
2368
2020
|
isCommandClass(obj) {
|
|
2369
2021
|
if (typeof obj !== "function") return false;
|
|
@@ -2525,7 +2177,7 @@ function eventGateContext(eventName, args, core) {
|
|
|
2525
2177
|
|
|
2526
2178
|
//#endregion
|
|
2527
2179
|
//#region src/bot/handleEventFault.ts
|
|
2528
|
-
const logger$1 = new Logger("EventBoundary");
|
|
2180
|
+
const logger$1 = new Logger("EventBoundary", { channel: "errors" });
|
|
2529
2181
|
/**
|
|
2530
2182
|
* The event controller boundary. Reports a throw from an event handler and never replies, since a
|
|
2531
2183
|
* generic event has no reply target. A {@link Silence} or a non-reporting {@link Notice} stops quietly, a
|
|
@@ -2538,15 +2190,15 @@ function handleEventFault(caught, eventName, handlerName, args, core) {
|
|
|
2538
2190
|
if (caught.reason !== void 0 && (core.config.errors?.logSilences ?? true)) logger$1.debug(`Silence: ${caught.reason}`);
|
|
2539
2191
|
return;
|
|
2540
2192
|
}
|
|
2541
|
-
|
|
2193
|
+
const error = asError(caught);
|
|
2542
2194
|
const ignore = new Set(core.config.errors?.ignoreEventApiCodes ?? []);
|
|
2543
|
-
if (
|
|
2544
|
-
logger$1.debug(`swallowed api code ${
|
|
2195
|
+
if (error instanceof DiscordAPIError && ignore.has(error.code)) {
|
|
2196
|
+
logger$1.debug(`swallowed api code ${error.code}`);
|
|
2545
2197
|
return;
|
|
2546
2198
|
}
|
|
2547
|
-
if (
|
|
2199
|
+
if (error instanceof Notice && !error.report) return;
|
|
2548
2200
|
const actor = deriveEventActor(args);
|
|
2549
|
-
extractErrorResponse(
|
|
2201
|
+
extractErrorResponse(error, core, {
|
|
2550
2202
|
event: {
|
|
2551
2203
|
name: eventName,
|
|
2552
2204
|
handler: handlerName,
|
|
@@ -2561,21 +2213,22 @@ function handleEventFault(caught, eventName, handlerName, args, core) {
|
|
|
2561
2213
|
//#endregion
|
|
2562
2214
|
//#region src/bot/controllers/EventDispatcher.ts
|
|
2563
2215
|
/**
|
|
2564
|
-
* Scans event handler directories,
|
|
2565
|
-
*
|
|
2566
|
-
*
|
|
2216
|
+
* Scans event handler directories, attaches one client listener per registered event, and runs the
|
|
2217
|
+
* handlers for each fire. Interactions route through InteractionDispatcher. Multiple handlers can
|
|
2218
|
+
* point to one event.
|
|
2567
2219
|
*
|
|
2568
2220
|
* @internal
|
|
2569
2221
|
*/
|
|
2570
2222
|
var EventDispatcher = class {
|
|
2571
2223
|
core;
|
|
2572
|
-
logger = new Logger("Events");
|
|
2224
|
+
logger = new Logger("Events", { channel: "events" });
|
|
2573
2225
|
isInitialized = false;
|
|
2574
|
-
name = "Events";
|
|
2575
2226
|
eventMap = /* @__PURE__ */ new Map();
|
|
2576
2227
|
middlewares = [];
|
|
2577
2228
|
executedOnceHandlers = /* @__PURE__ */ new Set();
|
|
2578
2229
|
attachedEvents = /* @__PURE__ */ new Set();
|
|
2230
|
+
inFlight = /* @__PURE__ */ new Set();
|
|
2231
|
+
draining = false;
|
|
2579
2232
|
loading = false;
|
|
2580
2233
|
loadedHandlers = [];
|
|
2581
2234
|
loadedMiddlewares = [];
|
|
@@ -2639,7 +2292,7 @@ var EventDispatcher = class {
|
|
|
2639
2292
|
this.registerHandler(val, relativePath);
|
|
2640
2293
|
this.hmrHandler?.trackHandler(fullPath, val);
|
|
2641
2294
|
}
|
|
2642
|
-
}
|
|
2295
|
+
});
|
|
2643
2296
|
}
|
|
2644
2297
|
async loadMiddlewares(dir) {
|
|
2645
2298
|
await traverseDirectory(dir, (fullPath, relativePath, imported) => {
|
|
@@ -2648,7 +2301,7 @@ var EventDispatcher = class {
|
|
|
2648
2301
|
this.registerMiddleware(val, relativePath);
|
|
2649
2302
|
this.hmrHandler?.trackMiddleware(fullPath, val);
|
|
2650
2303
|
}
|
|
2651
|
-
}
|
|
2304
|
+
});
|
|
2652
2305
|
}
|
|
2653
2306
|
/** @internal For use in dev mode */
|
|
2654
2307
|
async onHmr(event) {
|
|
@@ -2744,15 +2397,26 @@ var EventDispatcher = class {
|
|
|
2744
2397
|
const handlerEntries = this.eventMap.get(eventName);
|
|
2745
2398
|
this.logger.debug(`Attaching ${paint.mint.bold(eventName)} to the client with ${paint.mute(handlerEntries?.length ?? 0)} handler(s)`);
|
|
2746
2399
|
this.core.bot.client.on(eventName, (...args) => {
|
|
2747
|
-
this.
|
|
2748
|
-
(
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2400
|
+
if (this.draining) return;
|
|
2401
|
+
this.core.bus[PublishDefault]("anyEvent", {
|
|
2402
|
+
name: eventName,
|
|
2403
|
+
args
|
|
2404
|
+
});
|
|
2405
|
+
const run = this.processEvent(eventName, args).catch((caught) => {
|
|
2406
|
+
const error = asError(caught);
|
|
2407
|
+
this.logger.error(`[${paint.coral.bold("UNHANDLED ERROR AT ROOT")}] ${error.name}`, error.stack);
|
|
2408
|
+
this.core.bus[PublishDefault]("unhandledEventError", { error });
|
|
2409
|
+
});
|
|
2410
|
+
this.inFlight.add(run);
|
|
2411
|
+
run.finally(() => this.inFlight.delete(run));
|
|
2754
2412
|
});
|
|
2755
2413
|
}
|
|
2414
|
+
stopAccepting() {
|
|
2415
|
+
this.draining = true;
|
|
2416
|
+
}
|
|
2417
|
+
drain(timeoutMs) {
|
|
2418
|
+
return settleWithin(Promise.allSettled(this.inFlight), timeoutMs);
|
|
2419
|
+
}
|
|
2756
2420
|
async processEvent(eventName, args) {
|
|
2757
2421
|
const handlerEntries = this.eventMap.get(eventName);
|
|
2758
2422
|
if (!handlerEntries || handlerEntries.length === 0) return;
|
|
@@ -2817,7 +2481,7 @@ const HARMLESS_API_CODES = new Set([
|
|
|
2817
2481
|
|
|
2818
2482
|
//#endregion
|
|
2819
2483
|
//#region src/bot/handleInteractionFault.ts
|
|
2820
|
-
const logger = new Logger("InteractionBoundary");
|
|
2484
|
+
const logger = new Logger("InteractionBoundary", { channel: "errors" });
|
|
2821
2485
|
/**
|
|
2822
2486
|
* The interaction controller boundary. Sorts a throw from any handler-lifecycle phase into the right
|
|
2823
2487
|
* user reply plus the right bus event. When a handler is built its sender is passed in, so the boundary
|
|
@@ -2826,33 +2490,45 @@ const logger = new Logger("InteractionBoundary");
|
|
|
2826
2490
|
*
|
|
2827
2491
|
* @internal
|
|
2828
2492
|
*/
|
|
2829
|
-
async function handleInteractionFault(caught, interaction, core, sender) {
|
|
2493
|
+
async function handleInteractionFault(caught, interaction, core, routeId, sender) {
|
|
2830
2494
|
if (caught instanceof Silence) {
|
|
2831
2495
|
if (caught.reason !== void 0 && (core.config.errors?.logSilences ?? true)) logger.debug(`Silence: ${caught.reason}`);
|
|
2832
2496
|
return;
|
|
2833
2497
|
}
|
|
2834
|
-
|
|
2498
|
+
const error = asError(caught);
|
|
2835
2499
|
const ignore = new Set(core.config.errors?.ignoreApiCodes ?? []);
|
|
2836
|
-
if (
|
|
2837
|
-
logger.debug(`swallowed api code ${
|
|
2500
|
+
if (error instanceof DiscordAPIError && ignore.has(error.code)) {
|
|
2501
|
+
logger.debug(`swallowed api code ${error.code}`);
|
|
2838
2502
|
return;
|
|
2839
2503
|
}
|
|
2840
2504
|
if (interaction.isAutocomplete()) {
|
|
2841
|
-
extractErrorResponse(
|
|
2505
|
+
extractErrorResponse(error, core, {
|
|
2842
2506
|
guild: interaction.guild,
|
|
2843
2507
|
user: interaction.user,
|
|
2844
2508
|
metadata: interaction,
|
|
2845
2509
|
route: slashRouteOf(interaction)
|
|
2846
2510
|
});
|
|
2511
|
+
await sendEmptyChoices(interaction, core, routeId);
|
|
2847
2512
|
return;
|
|
2848
2513
|
}
|
|
2849
|
-
const { response } = extractErrorResponse(
|
|
2514
|
+
const { response } = extractErrorResponse(error, core, {
|
|
2850
2515
|
interaction,
|
|
2851
2516
|
guild: interaction.guild,
|
|
2852
2517
|
user: interaction.user,
|
|
2853
2518
|
metadata: interaction
|
|
2854
2519
|
});
|
|
2855
|
-
await sendGuarded(sender ?? new ReplySender(interaction,
|
|
2520
|
+
await sendGuarded(sender ?? new ReplySender(interaction, routeId, core.bus), response, error instanceof Notice ? error.ephemeral : true);
|
|
2521
|
+
}
|
|
2522
|
+
async function sendEmptyChoices(interaction, core, routeId) {
|
|
2523
|
+
const telemetry = {
|
|
2524
|
+
bus: core.bus,
|
|
2525
|
+
interactionId: interaction.id
|
|
2526
|
+
};
|
|
2527
|
+
try {
|
|
2528
|
+
await reportedWrite(telemetry, routeId, "respond", () => interaction.respond([]));
|
|
2529
|
+
} catch (error) {
|
|
2530
|
+
logger.debug(`autocomplete empty-choices send failed: ${String(error)}`);
|
|
2531
|
+
}
|
|
2856
2532
|
}
|
|
2857
2533
|
async function sendGuarded(sender, response, ephemeral) {
|
|
2858
2534
|
try {
|
|
@@ -2876,9 +2552,8 @@ async function sendGuarded(sender, response, ephemeral) {
|
|
|
2876
2552
|
*/
|
|
2877
2553
|
var InteractionDispatcher = class {
|
|
2878
2554
|
core;
|
|
2879
|
-
logger = new Logger("Interactions");
|
|
2555
|
+
logger = new Logger("Interactions", { channel: "interactions" });
|
|
2880
2556
|
isInitialized = false;
|
|
2881
|
-
name = "Interactions";
|
|
2882
2557
|
slashMap = /* @__PURE__ */ new Map();
|
|
2883
2558
|
buttonMap = /* @__PURE__ */ new Map();
|
|
2884
2559
|
modalMap = /* @__PURE__ */ new Map();
|
|
@@ -2890,8 +2565,11 @@ var InteractionDispatcher = class {
|
|
|
2890
2565
|
messageContextMenuMap = /* @__PURE__ */ new Map();
|
|
2891
2566
|
userContextMenuMap = /* @__PURE__ */ new Map();
|
|
2892
2567
|
autocompleteMap = /* @__PURE__ */ new Map();
|
|
2568
|
+
handlerFiles = /* @__PURE__ */ new Map();
|
|
2893
2569
|
keysToIgnore = /* @__PURE__ */ new Set();
|
|
2894
2570
|
middlewares = [];
|
|
2571
|
+
inFlight = /* @__PURE__ */ new Set();
|
|
2572
|
+
draining = false;
|
|
2895
2573
|
loading = false;
|
|
2896
2574
|
loadedHandlers = [];
|
|
2897
2575
|
loadedMiddlewares = [];
|
|
@@ -3006,7 +2684,7 @@ var InteractionDispatcher = class {
|
|
|
3006
2684
|
this.registerHandler(val, relativePath);
|
|
3007
2685
|
this.hmrHandler?.trackHandler(fullPath, val);
|
|
3008
2686
|
}
|
|
3009
|
-
}
|
|
2687
|
+
});
|
|
3010
2688
|
}
|
|
3011
2689
|
async loadMiddlewares(dir) {
|
|
3012
2690
|
await traverseDirectory(dir, (fullPath, relativePath, imported) => {
|
|
@@ -3015,7 +2693,7 @@ var InteractionDispatcher = class {
|
|
|
3015
2693
|
this.registerMiddleware(val, relativePath);
|
|
3016
2694
|
this.hmrHandler?.trackMiddleware(fullPath, val);
|
|
3017
2695
|
}
|
|
3018
|
-
}
|
|
2696
|
+
});
|
|
3019
2697
|
}
|
|
3020
2698
|
registerMiddleware(middlewareCtor, relativePath) {
|
|
3021
2699
|
const metadata = Reflect.getMetadata(MiddlewareMetadataKey, middlewareCtor);
|
|
@@ -3045,6 +2723,7 @@ var InteractionDispatcher = class {
|
|
|
3045
2723
|
return obj.prototype instanceof InteractionMiddleware && Reflect.hasMetadata(MiddlewareMetadataKey, obj);
|
|
3046
2724
|
}
|
|
3047
2725
|
registerHandler(handlerClass, relativePath) {
|
|
2726
|
+
const from = formatFilePath(relativePath);
|
|
3048
2727
|
const writes = [];
|
|
3049
2728
|
for (const [routeType, map] of this.routeTypes) {
|
|
3050
2729
|
const meta = Reflect.getMetadata(InteractionRouteKeys[routeType], handlerClass);
|
|
@@ -3052,16 +2731,16 @@ var InteractionDispatcher = class {
|
|
|
3052
2731
|
for (const route of meta) {
|
|
3053
2732
|
const existing = map.get(route);
|
|
3054
2733
|
if (existing && existing !== handlerClass) throw new SeedcordError(SeedcordErrorCode.InteractionDuplicateRoute, [
|
|
3055
|
-
route
|
|
3056
|
-
existing.name
|
|
3057
|
-
handlerClass.name
|
|
2734
|
+
`${routeType}:${route}`,
|
|
2735
|
+
`${existing.name} (${this.handlerFiles.get(existing) ?? "unknown file"})`,
|
|
2736
|
+
`${handlerClass.name} (${from})`
|
|
3058
2737
|
]);
|
|
3059
2738
|
writes.push([map, route]);
|
|
3060
2739
|
}
|
|
3061
2740
|
}
|
|
3062
2741
|
if (writes.length === 0) return;
|
|
3063
2742
|
for (const [map, route] of writes) map.set(route, handlerClass);
|
|
3064
|
-
|
|
2743
|
+
this.handlerFiles.set(handlerClass, from);
|
|
3065
2744
|
if (this.loading) this.loadedHandlers.push({
|
|
3066
2745
|
name: handlerClass.name,
|
|
3067
2746
|
from
|
|
@@ -3073,6 +2752,7 @@ var InteractionDispatcher = class {
|
|
|
3073
2752
|
await this.hmrHandler?.handle(event);
|
|
3074
2753
|
}
|
|
3075
2754
|
unregisterHandler(handlerClass, artifacts) {
|
|
2755
|
+
this.handlerFiles.delete(handlerClass);
|
|
3076
2756
|
if (artifacts) {
|
|
3077
2757
|
for (const { routeType, routes } of artifacts) {
|
|
3078
2758
|
const map = this.routeTypes.find(([type]) => type === routeType)?.[1];
|
|
@@ -3092,44 +2772,92 @@ var InteractionDispatcher = class {
|
|
|
3092
2772
|
}
|
|
3093
2773
|
attachToClient() {
|
|
3094
2774
|
this.core.bot.client.on(Events.InteractionCreate, (interaction) => {
|
|
3095
|
-
this.
|
|
3096
|
-
this.
|
|
3097
|
-
|
|
3098
|
-
|
|
2775
|
+
if (this.draining) return;
|
|
2776
|
+
this.core.bus[PublishDefault]("anyInteraction", { interaction });
|
|
2777
|
+
const run = this.handleInteraction(interaction).catch((caught) => {
|
|
2778
|
+
const error = asError(caught);
|
|
2779
|
+
this.logger.error(`[${paint.coral.bold("UNHANDLED ERROR AT ROOT")}] ${error.name}`, error.stack);
|
|
2780
|
+
this.core.bus[PublishDefault]("unhandledInteractionError", { error });
|
|
3099
2781
|
});
|
|
2782
|
+
this.inFlight.add(run);
|
|
2783
|
+
run.finally(() => this.inFlight.delete(run));
|
|
3100
2784
|
});
|
|
3101
2785
|
}
|
|
3102
|
-
|
|
2786
|
+
stopAccepting() {
|
|
2787
|
+
this.draining = true;
|
|
2788
|
+
}
|
|
2789
|
+
drain(timeoutMs) {
|
|
2790
|
+
return settleWithin(Promise.allSettled(this.inFlight), timeoutMs);
|
|
2791
|
+
}
|
|
2792
|
+
async handleCustomIdInteraction(interaction, kind, getMap) {
|
|
3103
2793
|
if ([...this.keysToIgnore].some((matcher) => matcher.owns(interaction.customId))) return;
|
|
3104
2794
|
const prefix = prefixOf(interaction.customId);
|
|
3105
|
-
if (!prefix)
|
|
3106
|
-
await this.processInteraction(interaction, () => prefix, (key) => getMap().get(key));
|
|
2795
|
+
if (!prefix) this.logger.warn(`${paint.sky.bold(kind)} has invalid customId: ${paint.mute(interaction.customId)}`);
|
|
2796
|
+
await this.processInteraction(interaction, kind, () => prefix, (key) => getMap().get(key));
|
|
3107
2797
|
}
|
|
3108
|
-
async processInteraction(interaction, extractKey, getHandler, fallback = UnhandledRepliable) {
|
|
2798
|
+
async processInteraction(interaction, kind, extractKey, getHandler, fallback = UnhandledRepliable) {
|
|
3109
2799
|
const key = extractKey(interaction);
|
|
2800
|
+
const startedAt = performance.now();
|
|
2801
|
+
const queuedMs = queuedMsFor(interaction.id);
|
|
2802
|
+
const matched = getHandler(key);
|
|
2803
|
+
let routeId = `${kind}:${key || "unrouted"}`;
|
|
2804
|
+
let reported = false;
|
|
2805
|
+
const report = (outcome) => {
|
|
2806
|
+
if (reported) return;
|
|
2807
|
+
reported = true;
|
|
2808
|
+
this.core.bus[PublishDefault]("interactionDispatched", dispatchedPayload({
|
|
2809
|
+
routeId,
|
|
2810
|
+
interactionId: interaction.id,
|
|
2811
|
+
kind,
|
|
2812
|
+
outcome,
|
|
2813
|
+
startedAt,
|
|
2814
|
+
fallback: !matched,
|
|
2815
|
+
queuedMs
|
|
2816
|
+
}));
|
|
2817
|
+
};
|
|
3110
2818
|
let sender;
|
|
3111
2819
|
try {
|
|
3112
|
-
if (!interaction.isAutocomplete())
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
}
|
|
3118
|
-
this.logger.debug(`Processing ${paint.mint.bold(key)} with ${paint.mute(HandlerCtor.name)}`);
|
|
3119
|
-
const dispatch = new DispatchContext(routeIdOf(HandlerCtor));
|
|
3120
|
-
const handler = new HandlerCtor(interaction, this.core, dispatch);
|
|
2820
|
+
if (!interaction.isAutocomplete()) await this.runMiddlewares(interaction);
|
|
2821
|
+
const HandlerCtor = matched ?? fallback;
|
|
2822
|
+
const dispatch = new DispatchContext(routeIdOf(HandlerCtor) ?? routeId);
|
|
2823
|
+
routeId = dispatch.routeId ?? routeId;
|
|
2824
|
+
const handler = this.buildHandler(HandlerCtor, interaction, dispatch, key, !matched);
|
|
3121
2825
|
if (handler instanceof RepliableHandler) sender = handler.getSender();
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
} finally {
|
|
3127
|
-
monitor?.report(dispatch.routeId);
|
|
3128
|
-
}
|
|
2826
|
+
const refusal = interaction.isAutocomplete() ? null : await this.gateRefusal(HandlerCtor, interaction, dispatch.routeId);
|
|
2827
|
+
if (refusal) {
|
|
2828
|
+
await this.answer(refusal.caught, interaction, routeId, sender, report);
|
|
2829
|
+
return;
|
|
3129
2830
|
}
|
|
3130
2831
|
await handler.execute();
|
|
2832
|
+
report("handled");
|
|
2833
|
+
} catch (caught) {
|
|
2834
|
+
await this.answer(caught, interaction, routeId, sender, report);
|
|
2835
|
+
}
|
|
2836
|
+
}
|
|
2837
|
+
async answer(caught, interaction, routeId, sender, report) {
|
|
2838
|
+
try {
|
|
2839
|
+
await handleInteractionFault(caught, interaction, this.core, routeId, sender);
|
|
2840
|
+
} finally {
|
|
2841
|
+
report(outcomeFor(caught));
|
|
2842
|
+
}
|
|
2843
|
+
}
|
|
2844
|
+
buildHandler(HandlerCtor, interaction, dispatch, key, isFallback) {
|
|
2845
|
+
if (isFallback) this.logger.warn(`No handler found for key ${paint.sky.bold(key)}. Falling back to ${HandlerCtor.name}.`);
|
|
2846
|
+
this.logger.debug(`Processing ${paint.mint.bold(key)} with ${paint.mute(HandlerCtor.name)}`);
|
|
2847
|
+
return new HandlerCtor(interaction, this.core, dispatch);
|
|
2848
|
+
}
|
|
2849
|
+
async runMiddlewares(interaction) {
|
|
2850
|
+
for (const { ctor } of this.middlewares) await new ctor(interaction, this.core).execute();
|
|
2851
|
+
}
|
|
2852
|
+
async gateRefusal(HandlerCtor, interaction, routeId) {
|
|
2853
|
+
const monitor = slowGateMonitor();
|
|
2854
|
+
try {
|
|
2855
|
+
await runHandlerGates(HandlerCtor, interactionGateContext(interaction, this.core), routeId ?? void 0, monitor?.observe);
|
|
2856
|
+
return null;
|
|
3131
2857
|
} catch (caught) {
|
|
3132
|
-
|
|
2858
|
+
return { caught };
|
|
2859
|
+
} finally {
|
|
2860
|
+
monitor?.report(routeId);
|
|
3133
2861
|
}
|
|
3134
2862
|
}
|
|
3135
2863
|
async handleInteraction(interaction) {
|
|
@@ -3174,47 +2902,41 @@ var InteractionDispatcher = class {
|
|
|
3174
2902
|
}
|
|
3175
2903
|
async handleSlashCommand(interaction) {
|
|
3176
2904
|
const route = slashRouteOf(interaction);
|
|
3177
|
-
await this.processInteraction(interaction, () => route, (key) => this.slashMap.get(key));
|
|
2905
|
+
await this.processInteraction(interaction, InteractionRoutes.Slash, () => route, (key) => this.slashMap.get(key));
|
|
3178
2906
|
}
|
|
3179
2907
|
async handleButton(interaction) {
|
|
3180
|
-
await this.handleCustomIdInteraction(interaction, () => this.buttonMap
|
|
2908
|
+
await this.handleCustomIdInteraction(interaction, InteractionRoutes.Button, () => this.buttonMap);
|
|
3181
2909
|
}
|
|
3182
2910
|
async handleModal(interaction) {
|
|
3183
|
-
await this.handleCustomIdInteraction(interaction, () => this.modalMap
|
|
2911
|
+
await this.handleCustomIdInteraction(interaction, InteractionRoutes.Modal, () => this.modalMap);
|
|
3184
2912
|
}
|
|
3185
2913
|
async handleStringSelectMenu(interaction) {
|
|
3186
|
-
await this.handleCustomIdInteraction(interaction, () => this.stringSelectMap
|
|
2914
|
+
await this.handleCustomIdInteraction(interaction, InteractionRoutes.StringMenu, () => this.stringSelectMap);
|
|
3187
2915
|
}
|
|
3188
2916
|
async handleUserSelectMenu(interaction) {
|
|
3189
|
-
await this.handleCustomIdInteraction(interaction, () => this.userSelectMap
|
|
2917
|
+
await this.handleCustomIdInteraction(interaction, InteractionRoutes.UserMenu, () => this.userSelectMap);
|
|
3190
2918
|
}
|
|
3191
2919
|
async handleRoleSelectMenu(interaction) {
|
|
3192
|
-
await this.handleCustomIdInteraction(interaction, () => this.roleSelectMap
|
|
2920
|
+
await this.handleCustomIdInteraction(interaction, InteractionRoutes.RoleMenu, () => this.roleSelectMap);
|
|
3193
2921
|
}
|
|
3194
2922
|
async handleChannelSelectMenu(interaction) {
|
|
3195
|
-
await this.handleCustomIdInteraction(interaction, () => this.channelSelectMap
|
|
2923
|
+
await this.handleCustomIdInteraction(interaction, InteractionRoutes.ChannelMenu, () => this.channelSelectMap);
|
|
3196
2924
|
}
|
|
3197
2925
|
async handleMentionableSelectMenu(interaction) {
|
|
3198
|
-
await this.handleCustomIdInteraction(interaction, () => this.mentionableSelectMap
|
|
2926
|
+
await this.handleCustomIdInteraction(interaction, InteractionRoutes.MentionableMenu, () => this.mentionableSelectMap);
|
|
3199
2927
|
}
|
|
3200
2928
|
async handleMessageContextMenu(interaction) {
|
|
3201
|
-
await this.processInteraction(interaction, () => interaction.commandName, (key) => this.messageContextMenuMap.get(key));
|
|
2929
|
+
await this.processInteraction(interaction, InteractionRoutes.MessageContextMenu, () => interaction.commandName, (key) => this.messageContextMenuMap.get(key));
|
|
3202
2930
|
}
|
|
3203
2931
|
async handleUserContextMenu(interaction) {
|
|
3204
|
-
await this.processInteraction(interaction, () => interaction.commandName, (key) => this.userContextMenuMap.get(key));
|
|
2932
|
+
await this.processInteraction(interaction, InteractionRoutes.UserContextMenu, () => interaction.commandName, (key) => this.userContextMenuMap.get(key));
|
|
3205
2933
|
}
|
|
3206
2934
|
async handleAutocomplete(interaction) {
|
|
3207
2935
|
const route = slashRouteOf(interaction);
|
|
3208
|
-
await this.processInteraction(interaction, () => route, (key) => this.autocompleteMap.get(key), UnhandledAutocomplete);
|
|
2936
|
+
await this.processInteraction(interaction, InteractionRoutes.Autocomplete, () => route, (key) => this.autocompleteMap.get(key), UnhandledAutocomplete);
|
|
3209
2937
|
}
|
|
3210
2938
|
};
|
|
3211
2939
|
|
|
3212
|
-
//#endregion
|
|
3213
|
-
//#region \0@oxc-project+runtime@0.135.0/helpers/esm/decorateMetadata.js
|
|
3214
|
-
function __decorateMetadata(k, v) {
|
|
3215
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
3216
|
-
}
|
|
3217
|
-
|
|
3218
2940
|
//#endregion
|
|
3219
2941
|
//#region \0@oxc-project+runtime@0.135.0/helpers/esm/decorate.js
|
|
3220
2942
|
function __decorate(decorators, target, key, desc) {
|
|
@@ -3226,23 +2948,25 @@ function __decorate(decorators, target, key, desc) {
|
|
|
3226
2948
|
|
|
3227
2949
|
//#endregion
|
|
3228
2950
|
//#region src/bot/Bot.ts
|
|
2951
|
+
const DISPATCH_DRAIN_TIMEOUT_MS = 5e3;
|
|
2952
|
+
const DRAIN_TASK_TIMEOUT_MS = 6e3;
|
|
2953
|
+
const UNBIND_TIMEOUT_MS = 2e3;
|
|
2954
|
+
const LOGOUT_TIMEOUT_MS = 2e3;
|
|
3229
2955
|
/**
|
|
3230
|
-
* Discord
|
|
3231
|
-
*
|
|
3232
|
-
* Don't instantiate this class directly. Use `core.bot` instead.
|
|
2956
|
+
* The Discord client and its controllers. Access it through `core.bot`.
|
|
3233
2957
|
*/
|
|
3234
|
-
var Bot = class
|
|
2958
|
+
var Bot = class {
|
|
3235
2959
|
/** @internal */
|
|
3236
|
-
logger = new Logger("Bot");
|
|
2960
|
+
logger = new Logger("Bot", { channel: "bot" });
|
|
3237
2961
|
isInitialized = false;
|
|
3238
2962
|
_client;
|
|
3239
2963
|
interactions;
|
|
3240
2964
|
events;
|
|
3241
|
-
commands;
|
|
3242
2965
|
emojiInjector;
|
|
2966
|
+
commands;
|
|
3243
2967
|
emojis = Emojis;
|
|
3244
2968
|
mentions = CommandMentions;
|
|
3245
|
-
/** @internal
|
|
2969
|
+
/** @internal */
|
|
3246
2970
|
async onHmr(event) {
|
|
3247
2971
|
if (this.interactions) await this.interactions.onHmr(event);
|
|
3248
2972
|
if (this.events) await this.events.onHmr(event);
|
|
@@ -3250,7 +2974,6 @@ var Bot = class extends Plugin {
|
|
|
3250
2974
|
}
|
|
3251
2975
|
/** @internal */
|
|
3252
2976
|
constructor(core) {
|
|
3253
|
-
super(core);
|
|
3254
2977
|
this._client = new Client(core.config.bot.clientOptions);
|
|
3255
2978
|
if (core.config.bot.interactions.path) this.interactions = new InteractionDispatcher(core);
|
|
3256
2979
|
if (core.config.bot.events.path) this.events = new EventDispatcher(core);
|
|
@@ -3261,12 +2984,24 @@ var Bot = class extends Plugin {
|
|
|
3261
2984
|
});
|
|
3262
2985
|
}
|
|
3263
2986
|
this.emojiInjector = new EmojiInjector(core);
|
|
3264
|
-
|
|
2987
|
+
this.registerShutdownTasks(core);
|
|
3265
2988
|
}
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
2989
|
+
registerShutdownTasks(core) {
|
|
2990
|
+
core.shutdown.addTask(ShutdownPhase.Unbind, "stop-dispatch", () => {
|
|
2991
|
+
this.stopAccepting();
|
|
2992
|
+
return Promise.resolve();
|
|
2993
|
+
}, UNBIND_TIMEOUT_MS);
|
|
2994
|
+
core.shutdown.addTask(ShutdownPhase.Drain, "drain-dispatch", () => this.drain(), DRAIN_TASK_TIMEOUT_MS);
|
|
2995
|
+
core.shutdown.addTask(ShutdownPhase.Logout, "stop-bot", async () => await this.stop(), LOGOUT_TIMEOUT_MS);
|
|
2996
|
+
}
|
|
2997
|
+
stopAccepting() {
|
|
2998
|
+
this.interactions?.stopAccepting();
|
|
2999
|
+
this.events?.stopAccepting();
|
|
3000
|
+
}
|
|
3001
|
+
async drain() {
|
|
3002
|
+
await Promise.allSettled([this.interactions?.drain(DISPATCH_DRAIN_TIMEOUT_MS), this.events?.drain(DISPATCH_DRAIN_TIMEOUT_MS)]);
|
|
3003
|
+
}
|
|
3004
|
+
/** @internal */
|
|
3270
3005
|
async init() {
|
|
3271
3006
|
if (this.isInitialized) return;
|
|
3272
3007
|
this.isInitialized = true;
|
|
@@ -3282,27 +3017,19 @@ var Bot = class extends Plugin {
|
|
|
3282
3017
|
this.interactions?.warnUnhandledContextMenuRoutes(this.commands.contextMenuLeaves());
|
|
3283
3018
|
}
|
|
3284
3019
|
}
|
|
3285
|
-
/**
|
|
3286
|
-
* Stops the bot and cleans up connections
|
|
3287
|
-
* @internal
|
|
3288
|
-
*/
|
|
3020
|
+
/** @internal */
|
|
3289
3021
|
async stop() {
|
|
3290
3022
|
this._client.removeAllListeners();
|
|
3291
3023
|
await this.logout();
|
|
3292
3024
|
}
|
|
3293
|
-
/**
|
|
3294
|
-
* Logs the bot into Discord using the configured token
|
|
3295
|
-
*/
|
|
3296
3025
|
async login(token) {
|
|
3297
|
-
|
|
3026
|
+
const ready = Promise.withResolvers();
|
|
3027
|
+
this._client.once(Events.ClientReady, () => ready.resolve());
|
|
3298
3028
|
this._client.login(token);
|
|
3299
|
-
await
|
|
3029
|
+
await ready.promise;
|
|
3300
3030
|
this.logger.info(`Logged in as ${chalk.bold.magenta(this._client.user?.username)}!`);
|
|
3301
3031
|
return this;
|
|
3302
3032
|
}
|
|
3303
|
-
/**
|
|
3304
|
-
* Logs out and destroys the Discord client connection
|
|
3305
|
-
*/
|
|
3306
3033
|
async logout() {
|
|
3307
3034
|
await this._client.destroy();
|
|
3308
3035
|
this.logger.info(chalk.bold.red("Logged out of Discord!"));
|
|
@@ -3310,248 +3037,19 @@ var Bot = class extends Plugin {
|
|
|
3310
3037
|
get client() {
|
|
3311
3038
|
return this._client;
|
|
3312
3039
|
}
|
|
3313
|
-
emit(event, ...args) {
|
|
3314
|
-
return super.emit(event, ...args);
|
|
3315
|
-
}
|
|
3316
|
-
emitSafe(event, ...args) {
|
|
3317
|
-
return super.emitSafe(event, ...args);
|
|
3318
|
-
}
|
|
3319
|
-
onListenerError(error, event) {
|
|
3320
|
-
this.logger.error(`listener for ${String(event)} threw`, error);
|
|
3321
|
-
}
|
|
3322
|
-
};
|
|
3323
|
-
__decorate([Envapt("DISCORD_BOT_TOKEN", { converter: (raw) => validateDiscordToken(raw) }), __decorateMetadata("design:type", String)], Bot.prototype, "botToken", void 0);
|
|
3324
|
-
|
|
3325
|
-
//#endregion
|
|
3326
|
-
//#region src/subscribers/default/HandledException.ts
|
|
3327
|
-
let HandledException = class HandledException extends WebhookLog {
|
|
3328
|
-
report() {
|
|
3329
|
-
return {
|
|
3330
|
-
username: "Handled Exception",
|
|
3331
|
-
components: [new HandledExceptionContainer(this.data).component],
|
|
3332
|
-
files: [jsonAttachment("source.json", "The raw source the fault came from", this.data.source.raw)]
|
|
3333
|
-
};
|
|
3334
|
-
}
|
|
3335
|
-
};
|
|
3336
|
-
HandledException = __decorate([Subscribe("handledException"), WebhookUrl("HANDLED_EXCEPTION_WEBHOOK_URL")], HandledException);
|
|
3337
|
-
var HandledExceptionContainer = class extends BuilderComponent {
|
|
3338
|
-
constructor(data) {
|
|
3339
|
-
super("container");
|
|
3340
|
-
const { denial, uuid, source } = data;
|
|
3341
|
-
this.instance.addTextDisplayComponents((text) => text.setContent(faultSummary(denial, source))).addSeparatorComponents(new WebhookSeparator().component).addTextDisplayComponents((text) => text.setContent(`### UUID \`${uuid}\`\n\`\`\`${neutralizeFences(causeStack(denial))}\`\`\``)).addSeparatorComponents(new WebhookSeparator().component).addTextDisplayComponents((text) => text.setContent("### Source")).addFileComponents((file) => file.setURL("attachment://source.json"));
|
|
3342
|
-
}
|
|
3343
|
-
};
|
|
3344
|
-
function faultSummary(denial, source) {
|
|
3345
|
-
const head = stripAnsi(`### A handled fault was reported: \`${denial.name}\`\n**Message:** ${denial.message}\n`);
|
|
3346
|
-
if (source.kind === "event") return `${head}**Event:** ${source.eventName}\n**Handler:** ${source.handler}\n**User ID:** \`${source.userId ?? "n/a"}\`\n**Guild ID:** \`${source.guildId ?? "n/a"}\`\n**Channel ID:** \`${source.channelId ?? "n/a"}\``;
|
|
3347
|
-
return `${head}**Kind:** ${source.interactionKind}\n**Command:** ${source.command ?? "n/a"}\n**CustomId:** ${source.customId ?? "n/a"}\n**User ID:** \`${source.userId}\`\n**Guild ID:** \`${source.guildId ?? "n/a"}\`\n**Channel ID:** \`${source.channelId ?? "n/a"}\`\n**Interaction ID:** \`${source.interactionId}\``;
|
|
3348
|
-
}
|
|
3349
|
-
function causeStack(denial) {
|
|
3350
|
-
const { cause } = denial;
|
|
3351
|
-
if (Error.isError(cause)) return stripAnsi(cause.stack ?? cause.message);
|
|
3352
|
-
if (typeof cause === "string") return stripAnsi(cause);
|
|
3353
|
-
if (typeof cause === "bigint" || typeof cause === "symbol" || typeof cause === "function") return cause.toString();
|
|
3354
|
-
if (cause !== void 0) try {
|
|
3355
|
-
return JSON.stringify(cause);
|
|
3356
|
-
} catch {
|
|
3357
|
-
return "[unserializable cause]";
|
|
3358
|
-
}
|
|
3359
|
-
return stripAnsi(denial.stack ?? "No cause recorded.");
|
|
3360
|
-
}
|
|
3361
|
-
|
|
3362
|
-
//#endregion
|
|
3363
|
-
//#region src/subscribers/default/UnknownException.ts
|
|
3364
|
-
let UnknownException = class UnknownException extends WebhookLog {
|
|
3365
|
-
report() {
|
|
3366
|
-
const { metadata } = this.data;
|
|
3367
|
-
return {
|
|
3368
|
-
username: "Unknown Exception",
|
|
3369
|
-
components: [new UnhandledErrorContainer(this.data).component],
|
|
3370
|
-
files: metadata ? [jsonAttachment("metadata.json", "Metadata associated with the error", metadata)] : void 0
|
|
3371
|
-
};
|
|
3372
|
-
}
|
|
3373
|
-
};
|
|
3374
|
-
UnknownException = __decorate([Subscribe("unknownException"), WebhookUrl("UNKNOWN_EXCEPTION_WEBHOOK_URL")], UnknownException);
|
|
3375
|
-
var UnhandledErrorContainer = class extends BuilderComponent {
|
|
3376
|
-
constructor(data) {
|
|
3377
|
-
super("container");
|
|
3378
|
-
const { uuid, error, guild, user, metadata } = data;
|
|
3379
|
-
this.instance.addTextDisplayComponents((text) => text.setContent(`### An unknown exception was thrown\n**Guild ID:** \`${guild?.id ?? "Not used in a guild"}\`\n**Guild Name:** ${guild?.name ?? "Not used in a guild"}\n**User ID:** \`${user?.id ?? "Missing user info"}\`\n**Username:** ${user?.username ?? "Missing user info"}\n`)).addSeparatorComponents(new WebhookSeparator().component).addTextDisplayComponents((text) => text.setContent(`### UUID \`${uuid}\`\n\`\`\`${errorReport(error)}\`\`\``));
|
|
3380
|
-
this.addTimestampsIfAvailable(error);
|
|
3381
|
-
this.addMetadataIfAvailable(metadata);
|
|
3382
|
-
}
|
|
3383
|
-
addTimestampsIfAvailable(error) {
|
|
3384
|
-
if (!(error instanceof DiscordAPIError$1)) return;
|
|
3385
|
-
const now = Date.now();
|
|
3386
|
-
const snowflake = error.url.match(/\/interactions\/(\d+)\//)?.[1];
|
|
3387
|
-
if (!snowflake) return void 0;
|
|
3388
|
-
const interactionTs = timestampFromSnowflake(snowflake);
|
|
3389
|
-
const diff = now - interactionTs;
|
|
3390
|
-
const seconds = Math.floor(diff / 1e3);
|
|
3391
|
-
const millis = diff % 1e3;
|
|
3392
|
-
this.instance.addSeparatorComponents(new WebhookSeparator().component).addTextDisplayComponents((text) => text.setContent(`### Timestamps\n- **\`Interaction sent\` :** ${new Date(interactionTs).toISOString()} (${interactionTs})\n- **\`Error logged \` :** ${new Date(now).toISOString()} (${now})\n- **\`Offset \` :** ${seconds}s ${millis}ms`));
|
|
3393
|
-
}
|
|
3394
|
-
addMetadataIfAvailable(metadata) {
|
|
3395
|
-
if (!metadata) return;
|
|
3396
|
-
this.instance.addSeparatorComponents(new WebhookSeparator().component).addTextDisplayComponents((text) => text.setContent("### Metadata")).addFileComponents((file) => file.setURL("attachment://metadata.json"));
|
|
3397
|
-
}
|
|
3398
|
-
};
|
|
3399
|
-
|
|
3400
|
-
//#endregion
|
|
3401
|
-
//#region src/subscribers/Bus.ts
|
|
3402
|
-
/**
|
|
3403
|
-
* Registers and dispatches subscribers. Subscribers load from the configured directories and run
|
|
3404
|
-
* on a programmatic or framework-event publish. Accessed via `core.bus`. Do not construct it directly.
|
|
3405
|
-
*/
|
|
3406
|
-
var Bus = class extends Plugin {
|
|
3407
|
-
core;
|
|
3408
|
-
logger = new Logger("Subscribers");
|
|
3409
|
-
/** @internal */
|
|
3410
|
-
name = "Subscribers";
|
|
3411
|
-
isInitialized = false;
|
|
3412
|
-
subscribersMap = /* @__PURE__ */ new Map();
|
|
3413
|
-
executedOnceHandlers = /* @__PURE__ */ new Set();
|
|
3414
|
-
webhookProbes = /* @__PURE__ */ new Map();
|
|
3415
|
-
hmrHandler;
|
|
3416
|
-
constructor(core) {
|
|
3417
|
-
super(core);
|
|
3418
|
-
this.core = core;
|
|
3419
|
-
if (this.core.config.subscribers.path) {
|
|
3420
|
-
if (!Envapter.isDevelopment && !Envapter.isTest) return;
|
|
3421
|
-
this.hmrHandler = new HmrModuleHandler({
|
|
3422
|
-
handlersDir: this.core.config.subscribers.path,
|
|
3423
|
-
isHandler: this.isSubscriber.bind(this),
|
|
3424
|
-
registerHandler: this.registerSubscriber.bind(this),
|
|
3425
|
-
unregisterHandler: this.unregisterSubscriber.bind(this),
|
|
3426
|
-
getArtifacts: this.getArtifacts.bind(this),
|
|
3427
|
-
logger: this.logger
|
|
3428
|
-
});
|
|
3429
|
-
}
|
|
3430
|
-
}
|
|
3431
|
-
getArtifacts(ctor) {
|
|
3432
|
-
return [Reflect.getMetadata(SubscribeMetadataKey, ctor).subscriber];
|
|
3433
|
-
}
|
|
3434
|
-
/** @internal */
|
|
3435
|
-
async init() {
|
|
3436
|
-
if (this.isInitialized) return;
|
|
3437
|
-
this.isInitialized = true;
|
|
3438
|
-
this.registerSubscriber(UnknownException);
|
|
3439
|
-
this.registerSubscriber(HandledException);
|
|
3440
|
-
const subscribersDir = this.core.config.subscribers.path;
|
|
3441
|
-
if (subscribersDir) {
|
|
3442
|
-
this.logger.info(chalk.bold(subscribersDir));
|
|
3443
|
-
await this.loadSubscribers(subscribersDir);
|
|
3444
|
-
const totalSubscribers = [...this.subscribersMap.values()].reduce((acc, handlers) => acc + handlers.length, 0);
|
|
3445
|
-
this.logger.utils.list([`${chalk.bold.magenta(totalSubscribers)} subscribers`], chalk.bold.green("Loaded"));
|
|
3446
|
-
}
|
|
3447
|
-
if (!Envapter.isTest) await this.verifyWebhooks();
|
|
3448
|
-
}
|
|
3449
|
-
/** @internal Exposed for tests. */
|
|
3450
|
-
async verifyWebhooks() {
|
|
3451
|
-
const missing = [];
|
|
3452
|
-
await Promise.all([...this.webhookProbes].map(async ([url, envKeys]) => {
|
|
3453
|
-
const result = await WebhookLog.senderFor(url).verify();
|
|
3454
|
-
if (result === "missing") missing.push(...envKeys);
|
|
3455
|
-
else if (result === "unreachable") this.logger.warn(`could not verify webhook at ${paint.sky.bold(envKeys.join(", "))}`);
|
|
3456
|
-
}));
|
|
3457
|
-
if (missing.length > 0) throw new SeedcordError(SeedcordErrorCode.ConfigWebhookNotFound, [missing.join(", ")]);
|
|
3458
|
-
}
|
|
3459
|
-
/** @internal */
|
|
3460
|
-
async onHmr(event) {
|
|
3461
|
-
if (this.hmrHandler) await this.hmrHandler.handle(event);
|
|
3462
|
-
}
|
|
3463
|
-
async loadSubscribers(dir) {
|
|
3464
|
-
await traverseDirectory(dir, (fullPath, relativePath, imported) => {
|
|
3465
|
-
for (const exportName of Object.keys(imported)) {
|
|
3466
|
-
const val = imported[exportName];
|
|
3467
|
-
if (this.isSubscriber(val)) {
|
|
3468
|
-
this.registerSubscriber(val);
|
|
3469
|
-
this.hmrHandler?.trackHandler(fullPath, val);
|
|
3470
|
-
this.logger.utils.registration(val.name, relativePath);
|
|
3471
|
-
}
|
|
3472
|
-
}
|
|
3473
|
-
}, this.logger);
|
|
3474
|
-
}
|
|
3475
|
-
registerSubscriber(handler) {
|
|
3476
|
-
if (handler.prototype instanceof WebhookLog) {
|
|
3477
|
-
const envKey = WebhookLog.envKeyOf(handler);
|
|
3478
|
-
const url = WebhookLog.urlOf(envKey);
|
|
3479
|
-
if (url === null) {
|
|
3480
|
-
this.logger.warn(`${chalk.bold(handler.name)} disabled, ${paint.sky.bold(envKey)} is not set`);
|
|
3481
|
-
return;
|
|
3482
|
-
}
|
|
3483
|
-
const envKeys = this.webhookProbes.get(url) ?? [];
|
|
3484
|
-
if (!envKeys.includes(envKey)) envKeys.push(envKey);
|
|
3485
|
-
this.webhookProbes.set(url, envKeys);
|
|
3486
|
-
}
|
|
3487
|
-
const options = Reflect.getMetadata(SubscribeMetadataKey, handler);
|
|
3488
|
-
let handlers = this.subscribersMap.get(options.subscriber);
|
|
3489
|
-
if (!handlers) {
|
|
3490
|
-
handlers = [];
|
|
3491
|
-
this.subscribersMap.set(options.subscriber, handlers);
|
|
3492
|
-
}
|
|
3493
|
-
handlers.push({
|
|
3494
|
-
ctor: handler,
|
|
3495
|
-
frequency: options.frequency ?? "on"
|
|
3496
|
-
});
|
|
3497
|
-
}
|
|
3498
|
-
unregisterSubscriber(handler, artifacts) {
|
|
3499
|
-
const subscribers = artifacts ?? [...this.subscribersMap.keys()];
|
|
3500
|
-
for (const subscriber of subscribers) {
|
|
3501
|
-
const handlers = this.subscribersMap.get(subscriber);
|
|
3502
|
-
if (!handlers) continue;
|
|
3503
|
-
const index = handlers.findIndex((entry) => entry.ctor === handler);
|
|
3504
|
-
if (index !== -1) handlers.splice(index, 1);
|
|
3505
|
-
}
|
|
3506
|
-
}
|
|
3507
|
-
isSubscriber(obj) {
|
|
3508
|
-
if (typeof obj !== "function") return false;
|
|
3509
|
-
return obj.prototype instanceof Subscriber && Reflect.hasMetadata(SubscribeMetadataKey, obj);
|
|
3510
|
-
}
|
|
3511
|
-
/**
|
|
3512
|
-
* Publishes an event to its subscribers and native listeners.
|
|
3513
|
-
*
|
|
3514
|
-
* Fire-and-forget, so subscriber handlers run asynchronously and this returns before they
|
|
3515
|
-
* complete, and callers must not assume side effects have landed. Errors thrown by a subscriber
|
|
3516
|
-
* are caught and logged, never surfaced here. A `'once'` subscriber is marked as executed when
|
|
3517
|
-
* it starts (even if it throws), so it never runs twice.
|
|
3518
|
-
*
|
|
3519
|
-
* @param event - The subscription key to publish
|
|
3520
|
-
* @param data - Payload passed to each subscriber
|
|
3521
|
-
* @returns Whether the underlying emitter had native listeners for the event
|
|
3522
|
-
*/
|
|
3523
|
-
publish(event, data) {
|
|
3524
|
-
this.processSubscriber(event, data);
|
|
3525
|
-
return super.emit(event, ...[data]);
|
|
3526
|
-
}
|
|
3527
|
-
async processSubscriber(subscriberName, data) {
|
|
3528
|
-
const handlers = this.subscribersMap.get(subscriberName);
|
|
3529
|
-
if (!handlers) return;
|
|
3530
|
-
for (const entry of handlers) {
|
|
3531
|
-
if (entry.frequency === "once" && this.executedOnceHandlers.has(entry.ctor)) continue;
|
|
3532
|
-
try {
|
|
3533
|
-
if (entry.frequency === "once") this.executedOnceHandlers.add(entry.ctor);
|
|
3534
|
-
const Ctor = entry.ctor;
|
|
3535
|
-
await new Ctor(data, this.core).execute();
|
|
3536
|
-
} catch (err) {
|
|
3537
|
-
this.logger.error(`Error in subscriber ${String(subscriberName)} handler ${entry.ctor.name}:`, err);
|
|
3538
|
-
}
|
|
3539
|
-
}
|
|
3540
|
-
}
|
|
3541
3040
|
};
|
|
3041
|
+
__decorate([Envapt("DISCORD_BOT_TOKEN", { converter: (raw) => validateDiscordToken(raw) })], Bot.prototype, "botToken", void 0);
|
|
3542
3042
|
|
|
3543
3043
|
//#endregion
|
|
3544
3044
|
//#region src/version.ts
|
|
3545
3045
|
/** Package version */
|
|
3546
|
-
const version = "0.1.0-next.
|
|
3046
|
+
const version = "0.1.0-next.5";
|
|
3547
3047
|
|
|
3548
3048
|
//#endregion
|
|
3549
3049
|
//#region src/Seedcord.ts
|
|
3550
3050
|
/**
|
|
3551
|
-
*
|
|
3552
|
-
*
|
|
3553
|
-
* Primary entry point for creating Discord bots with Seedcord.
|
|
3554
|
-
* Manages component lifecycle and provides plugin support.
|
|
3051
|
+
* The gateway bot host. Opens a discord.js gateway session, discovers handlers, and runs
|
|
3052
|
+
* coordinated startup and shutdown. Attach plugins with `attach()`.
|
|
3555
3053
|
*/
|
|
3556
3054
|
var Seedcord = class Seedcord extends Pluggable {
|
|
3557
3055
|
config;
|
|
@@ -3561,83 +3059,86 @@ var Seedcord = class Seedcord extends Pluggable {
|
|
|
3561
3059
|
augmentTarget = "@seedcord/gateway";
|
|
3562
3060
|
/** @internal */
|
|
3563
3061
|
version = version;
|
|
3564
|
-
static isInstantiated = false;
|
|
3565
3062
|
healthCheck;
|
|
3566
3063
|
hmrManager;
|
|
3567
|
-
/** @see {@link CoordinatedShutdown} */
|
|
3568
|
-
shutdown;
|
|
3569
|
-
/** @see {@link CoordinatedStartup} */
|
|
3570
|
-
startup;
|
|
3571
3064
|
/** @see {@link Bus} */
|
|
3572
3065
|
bus;
|
|
3066
|
+
subscribers;
|
|
3573
3067
|
/** @see {@link Bot} */
|
|
3574
3068
|
bot;
|
|
3575
3069
|
/** @see {@link IRateLimiter} */
|
|
3576
3070
|
rateLimiter;
|
|
3577
3071
|
/**
|
|
3578
|
-
* Creates a new Seedcord instance
|
|
3579
|
-
*
|
|
3580
3072
|
* @param config - Bot configuration including paths and Discord client options
|
|
3581
3073
|
* @throws A **SeedcordError** When attempting to create multiple instances (singleton)
|
|
3582
3074
|
*/
|
|
3583
3075
|
constructor(config) {
|
|
3584
|
-
|
|
3585
|
-
Seedcord.isInstantiated = true;
|
|
3586
|
-
const shutdown = new CoordinatedShutdown(config.shutdownEnabled);
|
|
3587
|
-
const startup = new CoordinatedStartup();
|
|
3588
|
-
super(shutdown, startup);
|
|
3076
|
+
super(new CoordinatedShutdown(), new CoordinatedStartup());
|
|
3589
3077
|
this.config = config;
|
|
3590
3078
|
installNodeDefaults(config.logger);
|
|
3591
3079
|
setBotColor(config.botColor);
|
|
3592
|
-
this.shutdown = shutdown;
|
|
3593
|
-
this.startup = startup;
|
|
3594
3080
|
this.hmrManager = new HmrManager();
|
|
3595
3081
|
this.hmrManager.init();
|
|
3596
3082
|
this.bus = new Bus(this);
|
|
3083
|
+
this.subscribers = new SubscriberLoader(this.bus, config.subscribers.path);
|
|
3597
3084
|
this.bot = new Bot(this);
|
|
3598
3085
|
this.rateLimiter = config.store ?? new MemoryRateLimiter();
|
|
3599
|
-
this.healthCheck =
|
|
3086
|
+
this.healthCheck = HealthCheck.fromOption(this.shutdown, config.healthCheck);
|
|
3600
3087
|
this.registerStartupTasks();
|
|
3601
3088
|
}
|
|
3602
3089
|
/** The bot's discord username, populated after login. */
|
|
3603
3090
|
get username() {
|
|
3604
3091
|
return this.bot.client.user?.username;
|
|
3605
3092
|
}
|
|
3093
|
+
pluginCapabilities() {
|
|
3094
|
+
return {
|
|
3095
|
+
client: this.bot.client,
|
|
3096
|
+
token: this.bot.botToken,
|
|
3097
|
+
rest: this.bot.client.rest
|
|
3098
|
+
};
|
|
3099
|
+
}
|
|
3606
3100
|
static reset() {
|
|
3607
|
-
|
|
3101
|
+
super.reset();
|
|
3608
3102
|
LoggerChannelRegistry.instance.reset();
|
|
3609
3103
|
}
|
|
3610
3104
|
registerStartupTasks() {
|
|
3611
3105
|
if (Envapter.isDevelopment || Envapter.isTest) this.registerHmrAwareModules();
|
|
3612
|
-
this.startup.addTask(StartupPhase
|
|
3106
|
+
this.startup.addTask(StartupPhase.Configuration, "bus-initialization", async () => {
|
|
3613
3107
|
this.bus.logger.utils.initialization("Subscribers", "start");
|
|
3614
|
-
await this.
|
|
3108
|
+
await this.subscribers.init();
|
|
3615
3109
|
this.bus.logger.utils.initialization("Subscribers", "end");
|
|
3616
3110
|
});
|
|
3617
|
-
this.startup.addTask(StartupPhase
|
|
3111
|
+
this.startup.addTask(StartupPhase.Login, "bot-initialization", async () => {
|
|
3618
3112
|
this.bot.logger.utils.initialization("Bot", "start");
|
|
3619
3113
|
await this.bot.init();
|
|
3620
3114
|
this.bot.logger.utils.initialization("Bot", "end");
|
|
3621
3115
|
});
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3116
|
+
const { healthCheck } = this;
|
|
3117
|
+
if (healthCheck) this.startup.addTask(StartupPhase.Ready, "health-check", async () => {
|
|
3118
|
+
healthCheck.logger.utils.initialization("HealthCheck", "start");
|
|
3119
|
+
await healthCheck.init();
|
|
3120
|
+
healthCheck.logger.utils.initialization("HealthCheck", "end");
|
|
3626
3121
|
});
|
|
3627
3122
|
}
|
|
3628
3123
|
/**
|
|
3629
3124
|
* Starts the bot and runs all initialization tasks
|
|
3630
3125
|
*
|
|
3631
|
-
* @returns This Seedcord instance when
|
|
3126
|
+
* @returns This Seedcord instance when initialized
|
|
3632
3127
|
*/
|
|
3633
3128
|
async start() {
|
|
3634
|
-
|
|
3129
|
+
try {
|
|
3130
|
+
await super.init();
|
|
3131
|
+
} catch (caught) {
|
|
3132
|
+
await this.shutdown.run(1, false);
|
|
3133
|
+
Seedcord.reset();
|
|
3134
|
+
throw caught;
|
|
3135
|
+
}
|
|
3635
3136
|
return this;
|
|
3636
3137
|
}
|
|
3637
3138
|
registerHmrAwareModules() {
|
|
3638
|
-
this.startup.addTask(StartupPhase
|
|
3139
|
+
this.startup.addTask(StartupPhase.Configuration, "hmr-registration", async () => {
|
|
3639
3140
|
this.hmrManager.register(this.bot);
|
|
3640
|
-
this.hmrManager.register(this.
|
|
3141
|
+
this.hmrManager.register(this.subscribers);
|
|
3641
3142
|
for (const plugin of this.plugins) this.hmrManager.register(plugin);
|
|
3642
3143
|
await Promise.resolve();
|
|
3643
3144
|
});
|
|
@@ -3645,5 +3146,5 @@ var Seedcord = class Seedcord extends Pluggable {
|
|
|
3645
3146
|
};
|
|
3646
3147
|
|
|
3647
3148
|
//#endregion
|
|
3648
|
-
export { ArraySource, AutocompleteHandler, ButtonHandler, CommandMentions, ContextMenuHandler, CursorSource, Emojis, EventHandler, EventMiddleware, Gated, IgnoreBots, InteractionHandler, InteractionMiddleware, Middleware, MiddlewareType, ModalHandler, Nsfw, Paginator,
|
|
3149
|
+
export { ArraySource, AutocompleteHandler, ButtonHandler, CommandMentions, ContextMenuHandler, CursorSource, Emojis, EventHandler, EventMiddleware, Gated, IgnoreBots, InteractionHandler, InteractionMiddleware, Middleware, MiddlewareType, ModalHandler, Nsfw, Paginator, Plugin, RegisterEvent, RepliableHandler, Seedcord, SelectMenuHandler, SlashHandler, Subscriber, WebhookLog, checkBotPermissions, checkPermissions, fetchGuildMember, fetchManyGuildMembers, fetchManyUsers, fetchRole, fetchText, fetchUser, getBotRole, getConfirmation, hasPermsToAssign, updateMemberRoles, version };
|
|
3649
3150
|
//# sourceMappingURL=index.mjs.map
|