@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.cjs
CHANGED
|
@@ -38,19 +38,17 @@ let _seedcord_utils_internal = require("@seedcord/utils/internal");
|
|
|
38
38
|
let discord_js = require("discord.js");
|
|
39
39
|
let node_crypto = require("node:crypto");
|
|
40
40
|
node_crypto = __toESM(node_crypto, 1);
|
|
41
|
-
let
|
|
42
|
-
let
|
|
43
|
-
chalk = __toESM(chalk, 1);
|
|
44
|
-
let envapt = require("envapt");
|
|
45
|
-
let discord_api_types_v10 = require("discord-api-types/v10");
|
|
46
|
-
let _seedcord_utils = require("@seedcord/utils");
|
|
47
|
-
let _discordjs_rest = require("@discordjs/rest");
|
|
48
|
-
let _seedcord_core_node = require("@seedcord/core/node");
|
|
41
|
+
let _seedcord_core_plugin = require("@seedcord/core/plugin");
|
|
42
|
+
let _seedcord_core_node_internal = require("@seedcord/core/node/internal");
|
|
49
43
|
let _seedcord_rate_limiter = require("@seedcord/rate-limiter");
|
|
50
44
|
let _seedcord_types_internal = require("@seedcord/types/internal");
|
|
45
|
+
let envapt = require("envapt");
|
|
46
|
+
let chalk = require("chalk");
|
|
47
|
+
chalk = __toESM(chalk, 1);
|
|
51
48
|
let envapt_legacy = require("envapt/legacy");
|
|
52
49
|
let node_path = require("node:path");
|
|
53
50
|
let _seedcord_core_hmr = require("@seedcord/core/hmr");
|
|
51
|
+
let _seedcord_utils = require("@seedcord/utils");
|
|
54
52
|
let _seedcord_utils_node = require("@seedcord/utils/node");
|
|
55
53
|
|
|
56
54
|
//#region src/logger-bootstrap.ts
|
|
@@ -165,7 +163,7 @@ let MiddlewareType = /* @__PURE__ */ function(MiddlewareType) {
|
|
|
165
163
|
* Interaction middleware cannot specify event filters.
|
|
166
164
|
*
|
|
167
165
|
* @param type - Middleware kind from {@link MiddlewareType}
|
|
168
|
-
* @param priority - Ordering value where lower runs earlier. {@default `0`}
|
|
166
|
+
* @param priority - Ordering value where lower runs earlier. {@default `0` }
|
|
169
167
|
* @param options - Additional registration options
|
|
170
168
|
*
|
|
171
169
|
* @decorator
|
|
@@ -353,7 +351,7 @@ const Emojis = emojiStorage;
|
|
|
353
351
|
*/
|
|
354
352
|
var EmojiInjector = class {
|
|
355
353
|
core;
|
|
356
|
-
logger = new _seedcord_logger.Logger("Emojis");
|
|
354
|
+
logger = new _seedcord_logger.Logger("Emojis", { channel: "bot" });
|
|
357
355
|
constructor(core) {
|
|
358
356
|
this.core = core;
|
|
359
357
|
}
|
|
@@ -428,7 +426,7 @@ const MULTI_GUILD = Symbol("seedcord:commandmentions:multi-guild");
|
|
|
428
426
|
*/
|
|
429
427
|
var CommandMentionInjector = class {
|
|
430
428
|
core;
|
|
431
|
-
logger = new _seedcord_logger.Logger("CommandMentions");
|
|
429
|
+
logger = new _seedcord_logger.Logger("CommandMentions", { channel: "bot" });
|
|
432
430
|
constructor(core) {
|
|
433
431
|
this.core = core;
|
|
434
432
|
}
|
|
@@ -586,7 +584,7 @@ function checkBotPermissions(target, scope, inverse = false, errors) {
|
|
|
586
584
|
}
|
|
587
585
|
const me = target.guild.members.me;
|
|
588
586
|
if (!me) {
|
|
589
|
-
new _seedcord_logger.Logger("checkBotPermissions").warn(`Bot member is unavailable in guild ${target.guild.id} while checking permissions in channel ${target.id}`);
|
|
587
|
+
new _seedcord_logger.Logger("checkBotPermissions", { channel: "errors" }).warn(`Bot member is unavailable in guild ${target.guild.id} while checking permissions in channel ${target.id}`);
|
|
590
588
|
const names = scope.map((bit) => _seedcord_core_internal.PermissionNames.get(bit) ?? String(bit));
|
|
591
589
|
throw new ((errors?.missingNotice) ?? _seedcord_core_internal.MissingPermissions)(void 0, "The bot", names);
|
|
592
590
|
}
|
|
@@ -785,8 +783,11 @@ function seedState(interaction) {
|
|
|
785
783
|
*/
|
|
786
784
|
var ReplySender = class extends _seedcord_core_internal.BaseReplySender {
|
|
787
785
|
interaction;
|
|
788
|
-
constructor(interaction, routeId) {
|
|
789
|
-
super(routeId, seedState(interaction)
|
|
786
|
+
constructor(interaction, routeId, bus) {
|
|
787
|
+
super(routeId, seedState(interaction), bus && {
|
|
788
|
+
bus,
|
|
789
|
+
interactionId: interaction.id
|
|
790
|
+
});
|
|
790
791
|
this.interaction = interaction;
|
|
791
792
|
}
|
|
792
793
|
async writeReply(response, opts) {
|
|
@@ -893,43 +894,9 @@ function slashRouteOf(interaction) {
|
|
|
893
894
|
return (0, _seedcord_utils_internal.buildSlashRoute)(interaction.commandName, subcommand, group);
|
|
894
895
|
}
|
|
895
896
|
|
|
896
|
-
//#endregion
|
|
897
|
-
//#region src/miscellaneous/FaultThrottle.ts
|
|
898
|
-
/**
|
|
899
|
-
* Drops duplicate fault reports inside a fixed window so a recurring fault (a database outage on a
|
|
900
|
-
* hot path) reports once per window instead of flooding the webhook. Keyed on a structural string, so
|
|
901
|
-
* the map stays small, and a stale entry is evicted on the next read of its key.
|
|
902
|
-
*
|
|
903
|
-
* @internal
|
|
904
|
-
*/
|
|
905
|
-
var FaultThrottle = class {
|
|
906
|
-
windowMs;
|
|
907
|
-
now;
|
|
908
|
-
lastReportedAt = /* @__PURE__ */ new Map();
|
|
909
|
-
constructor(windowMs = 6e4, now = () => Date.now()) {
|
|
910
|
-
this.windowMs = windowMs;
|
|
911
|
-
this.now = now;
|
|
912
|
-
}
|
|
913
|
-
/** Evicts the entry for this key if it has aged out, so the map never holds stale keys forever. */
|
|
914
|
-
shouldReport(key) {
|
|
915
|
-
const last = this.lastReportedAt.get(key);
|
|
916
|
-
if (last === void 0) return true;
|
|
917
|
-
if (this.now() - last < this.windowMs) return false;
|
|
918
|
-
this.lastReportedAt.delete(key);
|
|
919
|
-
return true;
|
|
920
|
-
}
|
|
921
|
-
markReported(key) {
|
|
922
|
-
this.lastReportedAt.set(key, this.now());
|
|
923
|
-
}
|
|
924
|
-
clear() {
|
|
925
|
-
this.lastReportedAt.clear();
|
|
926
|
-
}
|
|
927
|
-
};
|
|
928
|
-
|
|
929
897
|
//#endregion
|
|
930
898
|
//#region src/miscellaneous/extractErrorResponse.ts
|
|
931
|
-
const logger$2 = new _seedcord_logger.Logger("ErrorsHandling");
|
|
932
|
-
const faultThrottle = new FaultThrottle();
|
|
899
|
+
const logger$2 = new _seedcord_logger.Logger("ErrorsHandling", { channel: "errors" });
|
|
933
900
|
/**
|
|
934
901
|
* Processes an error into a user-facing reply and routes its report.
|
|
935
902
|
*
|
|
@@ -959,29 +926,30 @@ function extractErrorResponse(error, core, origin) {
|
|
|
959
926
|
response: override ? new override(uuid).render(ctx) : new _seedcord_core.Fault().render(ctx)
|
|
960
927
|
};
|
|
961
928
|
}
|
|
962
|
-
function withThrottle(origin, error, publish) {
|
|
929
|
+
function withThrottle(core, origin, error, publish) {
|
|
930
|
+
const throttle = _seedcord_core_internal.FaultThrottle.for(core);
|
|
963
931
|
const key = faultKey(origin, error);
|
|
964
|
-
if (!
|
|
965
|
-
logger$2.
|
|
932
|
+
if (!throttle.shouldReport(key)) {
|
|
933
|
+
logger$2.debug(`throttled duplicate fault ${key}`);
|
|
966
934
|
return;
|
|
967
935
|
}
|
|
968
936
|
publish();
|
|
969
|
-
|
|
937
|
+
throttle.markReported(key);
|
|
970
938
|
}
|
|
971
939
|
function reportFault(denial, core, origin, uuid) {
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
if (origin.interaction) core.bus.
|
|
940
|
+
logger$2.error(`${denial.name}: ${uuid}`, denial);
|
|
941
|
+
withThrottle(core, origin, denial, () => {
|
|
942
|
+
if (origin.interaction) core.bus[_seedcord_core_internal.PublishDefault]("handledException", {
|
|
975
943
|
denial,
|
|
976
944
|
uuid,
|
|
977
945
|
source: buildInteractionSource(origin.interaction)
|
|
978
946
|
});
|
|
979
|
-
else if (origin.event) core.bus.
|
|
947
|
+
else if (origin.event) core.bus[_seedcord_core_internal.PublishDefault]("handledException", {
|
|
980
948
|
denial,
|
|
981
949
|
uuid,
|
|
982
950
|
source: buildEventSource(origin.event, origin)
|
|
983
951
|
});
|
|
984
|
-
else core.bus.
|
|
952
|
+
else core.bus[_seedcord_core_internal.PublishDefault]("unknownException", {
|
|
985
953
|
uuid,
|
|
986
954
|
error: denial,
|
|
987
955
|
...scalarActors(origin),
|
|
@@ -996,10 +964,10 @@ function causeLine(error) {
|
|
|
996
964
|
return `\ncaused by ${cause.name}: ${first ?? cause.message}`;
|
|
997
965
|
}
|
|
998
966
|
function reportRawFault(error, core, origin, uuid) {
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
core.bus.
|
|
967
|
+
if (core.config.errors?.errorStack ?? false) logger$2.error(uuid, error);
|
|
968
|
+
else logger$2.error(`${uuid} | ${error.message}${causeLine(error)}`);
|
|
969
|
+
withThrottle(core, origin, error, () => {
|
|
970
|
+
core.bus[_seedcord_core_internal.PublishDefault]("unknownException", {
|
|
1003
971
|
uuid,
|
|
1004
972
|
error,
|
|
1005
973
|
...scalarActors(origin),
|
|
@@ -1142,112 +1110,6 @@ async function getConfirmation(interaction, prompt, options) {
|
|
|
1142
1110
|
return confirmed;
|
|
1143
1111
|
}
|
|
1144
1112
|
|
|
1145
|
-
//#endregion
|
|
1146
|
-
//#region src/interfaces/Plugin.ts
|
|
1147
|
-
/**
|
|
1148
|
-
* Base class for Seedcord plugins
|
|
1149
|
-
*
|
|
1150
|
-
* Extend this class to create plugins that integrate with the Seedcord lifecycle.
|
|
1151
|
-
* Plugins have access to the core instance and must implement initialization logic.
|
|
1152
|
-
*/
|
|
1153
|
-
var Plugin = class extends _seedcord_event_emitter.TypedEventEmitter {
|
|
1154
|
-
pluggable;
|
|
1155
|
-
name = this.constructor.name;
|
|
1156
|
-
constructor(pluggable) {
|
|
1157
|
-
super();
|
|
1158
|
-
this.pluggable = pluggable;
|
|
1159
|
-
}
|
|
1160
|
-
/**
|
|
1161
|
-
* Reloads plugin state on an HMR update. The base implementation is a no-op, override it in your plugin.
|
|
1162
|
-
* @virtual
|
|
1163
|
-
*/
|
|
1164
|
-
onHmr(_event) {
|
|
1165
|
-
return Promise.resolve();
|
|
1166
|
-
}
|
|
1167
|
-
/**
|
|
1168
|
-
* Registers critical file patterns that should trigger a full restart when changed in Dev HMR.
|
|
1169
|
-
* @param patterns - Glob patterns relative to the project root
|
|
1170
|
-
*/
|
|
1171
|
-
registerCriticalFiles(patterns) {
|
|
1172
|
-
(0, _seedcord_core_internal.getDevChannel)()?.send("seedcord:register-critical-files", { patterns });
|
|
1173
|
-
}
|
|
1174
|
-
/** @internal */
|
|
1175
|
-
removeListener(event, listener) {
|
|
1176
|
-
return super.removeListener(event, listener);
|
|
1177
|
-
}
|
|
1178
|
-
/** @internal */
|
|
1179
|
-
removeAllListeners(event) {
|
|
1180
|
-
return super.removeAllListeners(event);
|
|
1181
|
-
}
|
|
1182
|
-
};
|
|
1183
|
-
/**
|
|
1184
|
-
* Base class for objects that can have plugins attached.
|
|
1185
|
-
*
|
|
1186
|
-
* Provides plugin attachment capabilities and lifecycle management. Plugins are attached during
|
|
1187
|
-
* configuration and initialized during startup. Not constructed directly, the host is a {@link Seedcord}.
|
|
1188
|
-
*/
|
|
1189
|
-
var Pluggable = class Pluggable extends _seedcord_event_emitter.TypedEventEmitter {
|
|
1190
|
-
isInitialized = false;
|
|
1191
|
-
shutdown;
|
|
1192
|
-
startup;
|
|
1193
|
-
plugins = [];
|
|
1194
|
-
static PLUGIN_INIT_TIMEOUT_MS = 15e3;
|
|
1195
|
-
constructor(shutdown, startup) {
|
|
1196
|
-
super();
|
|
1197
|
-
this.shutdown = shutdown;
|
|
1198
|
-
this.startup = startup;
|
|
1199
|
-
}
|
|
1200
|
-
/** @internal */
|
|
1201
|
-
async init() {
|
|
1202
|
-
if (this.isInitialized) return this;
|
|
1203
|
-
await this.startup.run();
|
|
1204
|
-
this.isInitialized = true;
|
|
1205
|
-
return this;
|
|
1206
|
-
}
|
|
1207
|
-
/**
|
|
1208
|
-
* Attaches a plugin to this instance
|
|
1209
|
-
*
|
|
1210
|
-
* Plugins provide external functionality and are initialized during the specified startup phase.
|
|
1211
|
-
* The plugin instance becomes available as a property in `core` wherever it's available.
|
|
1212
|
-
*
|
|
1213
|
-
* Make sure to augment the {@link Core} interface with the plugin type to ensure TypeScript recognizes it and provides intellisense.
|
|
1214
|
-
*
|
|
1215
|
-
* @typeParam Key - The property name for accessing the plugin
|
|
1216
|
-
* @typeParam Ctor - The plugin constructor type
|
|
1217
|
-
* @param key - Property name to access the plugin instance
|
|
1218
|
-
* @param Plugin - Plugin constructor class
|
|
1219
|
-
* @param startupPhase - When during startup to initialize this plugin ({@link StartupPhase})
|
|
1220
|
-
* @param args - Additional arguments to pass to the plugin constructor
|
|
1221
|
-
* @returns This instance with the plugin attached as a typed property
|
|
1222
|
-
* @throws A **SeedcordError** When called after initialization or if key already exists
|
|
1223
|
-
* @example
|
|
1224
|
-
* ```typescript
|
|
1225
|
-
* seedcord.attach('db', Mongo, StartupPhase.Configuration, { uri: 'mongodb://...', name: 'seedcord', dir: ... })
|
|
1226
|
-
* ```
|
|
1227
|
-
*/
|
|
1228
|
-
attach(key, Plugin, startupPhase, ...args) {
|
|
1229
|
-
if (this.isInitialized) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.CorePluginAfterInit);
|
|
1230
|
-
if (key in this) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.CorePluginKeyExists, [key]);
|
|
1231
|
-
const instance = new Plugin(this, ...args);
|
|
1232
|
-
this.plugins.push(instance);
|
|
1233
|
-
const entry = { [key]: instance };
|
|
1234
|
-
this.startup.addTask(startupPhase, `Plugin:${key}`, async () => {
|
|
1235
|
-
instance.logger.utils.initialization(key, "start");
|
|
1236
|
-
await instance.init();
|
|
1237
|
-
instance.logger.utils.initialization(key, "end");
|
|
1238
|
-
}, Pluggable.PLUGIN_INIT_TIMEOUT_MS);
|
|
1239
|
-
return Object.assign(this, entry);
|
|
1240
|
-
}
|
|
1241
|
-
/** @internal */
|
|
1242
|
-
removeListener(event, listener) {
|
|
1243
|
-
return super.removeListener(event, listener);
|
|
1244
|
-
}
|
|
1245
|
-
/** @internal */
|
|
1246
|
-
removeAllListeners(event) {
|
|
1247
|
-
return super.removeAllListeners(event);
|
|
1248
|
-
}
|
|
1249
|
-
};
|
|
1250
|
-
|
|
1251
1113
|
//#endregion
|
|
1252
1114
|
//#region src/handlers/BaseHandler.ts
|
|
1253
1115
|
/**
|
|
@@ -1273,7 +1135,7 @@ var BaseHandler = class extends _seedcord_core.BaseHandler {
|
|
|
1273
1135
|
*/
|
|
1274
1136
|
var InteractionMiddleware = class extends BaseHandler {
|
|
1275
1137
|
constructor(event, core) {
|
|
1276
|
-
super(event, core);
|
|
1138
|
+
super(event, core, void 0, "interactions");
|
|
1277
1139
|
}
|
|
1278
1140
|
};
|
|
1279
1141
|
|
|
@@ -1293,8 +1155,8 @@ var RepliableHandler = class extends _seedcord_core.RepliableHandler {
|
|
|
1293
1155
|
getEvent() {
|
|
1294
1156
|
return this.event;
|
|
1295
1157
|
}
|
|
1296
|
-
buildSender(event,
|
|
1297
|
-
return new ReplySender(event, routeId);
|
|
1158
|
+
buildSender(event, core, routeId) {
|
|
1159
|
+
return new ReplySender(event, routeId, core.bus);
|
|
1298
1160
|
}
|
|
1299
1161
|
};
|
|
1300
1162
|
|
|
@@ -1449,7 +1311,7 @@ var ContextMenuHandler = class extends InteractionHandler {
|
|
|
1449
1311
|
*/
|
|
1450
1312
|
var AutocompleteHandler = class extends BaseHandler {
|
|
1451
1313
|
constructor(event, core, dispatch) {
|
|
1452
|
-
super(event, core, dispatch);
|
|
1314
|
+
super(event, core, dispatch, "interactions");
|
|
1453
1315
|
}
|
|
1454
1316
|
decodedFocused;
|
|
1455
1317
|
/**
|
|
@@ -1500,7 +1362,11 @@ var AutocompleteHandler = class extends BaseHandler {
|
|
|
1500
1362
|
}
|
|
1501
1363
|
/** Send autocomplete suggestions, callback type 8. Prefer {@link match}, which restricts each field's choices to its declared type. */
|
|
1502
1364
|
async respond(choices) {
|
|
1503
|
-
|
|
1365
|
+
const routeId = this.dispatch?.routeId ?? "autocomplete";
|
|
1366
|
+
await (0, _seedcord_core_internal.reportedWrite)({
|
|
1367
|
+
bus: this.core.bus,
|
|
1368
|
+
interactionId: this.event.id
|
|
1369
|
+
}, routeId, "respond", () => this.event.respond(choices));
|
|
1504
1370
|
}
|
|
1505
1371
|
/** The firing command route, for a field whose completion differs per registered command. */
|
|
1506
1372
|
get route() {
|
|
@@ -1693,7 +1559,7 @@ var SelectMenuHandler = class extends ComponentHandler {};
|
|
|
1693
1559
|
var EventHandler = class extends BaseHandler {
|
|
1694
1560
|
firedEvent;
|
|
1695
1561
|
constructor(event, core, eventName) {
|
|
1696
|
-
super(event, core);
|
|
1562
|
+
super(event, core, void 0, "events");
|
|
1697
1563
|
this.firedEvent = eventName;
|
|
1698
1564
|
}
|
|
1699
1565
|
/**
|
|
@@ -1746,7 +1612,7 @@ var EventHandler = class extends BaseHandler {
|
|
|
1746
1612
|
var EventMiddleware = class extends BaseHandler {
|
|
1747
1613
|
firedEvent;
|
|
1748
1614
|
constructor(event, core, eventName) {
|
|
1749
|
-
super(event, core);
|
|
1615
|
+
super(event, core, void 0, "events");
|
|
1750
1616
|
this.firedEvent = eventName;
|
|
1751
1617
|
}
|
|
1752
1618
|
/**
|
|
@@ -1931,10 +1797,15 @@ var Paginator = class {
|
|
|
1931
1797
|
}
|
|
1932
1798
|
};
|
|
1933
1799
|
}
|
|
1934
|
-
/**
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1800
|
+
/**
|
|
1801
|
+
* Render page 0 and send it through the handler's sender, picking reply or followUp from its ack state.
|
|
1802
|
+
*
|
|
1803
|
+
* @param handler - The handler starting the paginator, normally `this`.
|
|
1804
|
+
*/
|
|
1805
|
+
async start(handler) {
|
|
1806
|
+
const interaction = handler.getEvent();
|
|
1807
|
+
const response = await this.page(contextOf(interaction, handler.core), 0);
|
|
1808
|
+
return handler.getSender().send(response, { ephemeral: this.config.ephemeral ?? false });
|
|
1938
1809
|
}
|
|
1939
1810
|
/** Render a page as a {@link ReplyResponse}. To post it elsewhere, add `flags: MessageFlags.IsComponentsV2`. */
|
|
1940
1811
|
async page(ctx, n) {
|
|
@@ -1994,250 +1865,32 @@ var CursorSource = class {
|
|
|
1994
1865
|
};
|
|
1995
1866
|
|
|
1996
1867
|
//#endregion
|
|
1997
|
-
//#region src/subscribers/
|
|
1868
|
+
//#region src/subscribers/index.ts
|
|
1998
1869
|
/**
|
|
1999
|
-
*
|
|
2000
|
-
*
|
|
2001
|
-
* Provides type-safe access to subscribers data and the core framework instance.
|
|
2002
|
-
* Extend this class to create custom subscriber handlers.
|
|
1870
|
+
* Base class for a gateway subscriber, binding `this.core` to the gateway {@link Core}.
|
|
2003
1871
|
*
|
|
2004
|
-
* @typeParam KeyOfSubscribers - The
|
|
1872
|
+
* @typeParam KeyOfSubscribers - The subscription key this handler receives
|
|
2005
1873
|
*/
|
|
2006
|
-
var Subscriber = class {
|
|
2007
|
-
data;
|
|
2008
|
-
core;
|
|
2009
|
-
logger;
|
|
2010
|
-
/**
|
|
2011
|
-
* Instantiates the subscriber with event data and core.
|
|
2012
|
-
*
|
|
2013
|
-
* @param data - The subscriber event data
|
|
2014
|
-
* @param core - The core framework instance
|
|
2015
|
-
*/
|
|
2016
|
-
constructor(data, core) {
|
|
2017
|
-
this.data = data;
|
|
2018
|
-
this.core = core;
|
|
2019
|
-
this.logger = new _seedcord_logger.Logger(this.constructor.name);
|
|
2020
|
-
}
|
|
2021
|
-
};
|
|
2022
|
-
|
|
2023
|
-
//#endregion
|
|
2024
|
-
//#region src/subscribers/decorators/Subscribe.ts
|
|
1874
|
+
var Subscriber = class extends _seedcord_core.Subscriber {};
|
|
2025
1875
|
/**
|
|
2026
|
-
*
|
|
1876
|
+
* Base class for a gateway subscriber that delivers its event to a Discord webhook, binding
|
|
1877
|
+
* `this.core` to the gateway {@link Core}.
|
|
2027
1878
|
*
|
|
2028
|
-
*
|
|
2029
|
-
* registration and execution when the subscriber is emitted.
|
|
2030
|
-
*
|
|
2031
|
-
* @typeParam TSubscriber - The subscriber event name to register for
|
|
2032
|
-
* @param subscriber - The subscriber event name to register for
|
|
2033
|
-
* @param options - Options to configure the subscriber handler registration.
|
|
2034
|
-
* @decorator
|
|
2035
|
-
* @example
|
|
2036
|
-
* ```typescript
|
|
2037
|
-
* \@Subscribe('userJoin')
|
|
2038
|
-
* class WelcomeHandler extends Subscriber<'userJoin'> {
|
|
2039
|
-
* async execute() {
|
|
2040
|
-
* // Handle user join event
|
|
2041
|
-
* }
|
|
2042
|
-
* }
|
|
2043
|
-
* ```
|
|
2044
|
-
* @example
|
|
2045
|
-
* ```ts
|
|
2046
|
-
* \@Subscribe('userJoin', { frequency: 'once' })
|
|
2047
|
-
* // or
|
|
2048
|
-
* \@Subscribe('userJoin')
|
|
2049
|
-
* ```
|
|
1879
|
+
* @typeParam KeyOfSubscribers - The subscription key this reporter receives
|
|
2050
1880
|
*/
|
|
2051
|
-
|
|
2052
|
-
return function(constructor) {
|
|
2053
|
-
const meta = {
|
|
2054
|
-
subscriber,
|
|
2055
|
-
frequency: options?.frequency
|
|
2056
|
-
};
|
|
2057
|
-
Reflect.defineMetadata(_seedcord_core_internal.SubscribeMetadataKey, meta, constructor);
|
|
2058
|
-
};
|
|
2059
|
-
}
|
|
1881
|
+
var WebhookLog = class extends _seedcord_core.WebhookLog {};
|
|
2060
1882
|
|
|
2061
1883
|
//#endregion
|
|
2062
|
-
//#region src/
|
|
1884
|
+
//#region src/plugin.ts
|
|
2063
1885
|
/**
|
|
2064
|
-
*
|
|
1886
|
+
* Base class for a gateway plugin, binding `this.core` to the gateway {@link Core}.
|
|
2065
1887
|
*
|
|
2066
|
-
*
|
|
2067
|
-
*
|
|
1888
|
+
* A plugin that runs on either transport extends the base from `@seedcord/core/plugin`, whose
|
|
1889
|
+
* `this.core` carries the shared members only.
|
|
2068
1890
|
*
|
|
2069
|
-
* @
|
|
2070
|
-
* @decorator
|
|
2071
|
-
* @example
|
|
2072
|
-
* ```ts
|
|
2073
|
-
* \@Subscribe('memberBanned')
|
|
2074
|
-
* \@WebhookUrl('BAN_LOG_WEBHOOK_URL')
|
|
2075
|
-
* class BanLog extends WebhookLog<'memberBanned'> {
|
|
2076
|
-
* report() {
|
|
2077
|
-
* return { components: [new BanCard(this.data).component] };
|
|
2078
|
-
* }
|
|
2079
|
-
* }
|
|
2080
|
-
* ```
|
|
1891
|
+
* @typeParam Opts - The transport, runtime, and capabilities the plugin declares.
|
|
2081
1892
|
*/
|
|
2082
|
-
|
|
2083
|
-
return function(constructor) {
|
|
2084
|
-
Reflect.defineMetadata(_seedcord_core_internal.WebhookUrlMetadataKey, envKey, constructor);
|
|
2085
|
-
};
|
|
2086
|
-
}
|
|
2087
|
-
|
|
2088
|
-
//#endregion
|
|
2089
|
-
//#region src/miscellaneous/flagsFor.ts
|
|
2090
|
-
/** @internal */
|
|
2091
|
-
function flagsFor(ephemeral) {
|
|
2092
|
-
let flags = discord_api_types_v10.MessageFlags.IsComponentsV2;
|
|
2093
|
-
if (ephemeral) flags |= discord_api_types_v10.MessageFlags.Ephemeral;
|
|
2094
|
-
return flags;
|
|
2095
|
-
}
|
|
2096
|
-
|
|
2097
|
-
//#endregion
|
|
2098
|
-
//#region src/subscribers/bases/webhookHelpers.ts
|
|
2099
|
-
const DISCORD_WEBHOOK_REGEX = new RegExp(String.raw`^https?:\/\/(?:canary\.|ptb\.)?discord(?:app)?\.com\/api\/webhooks\/\d+\/[\w$-]+$`);
|
|
2100
|
-
function isDiscordWebhookUrl(value) {
|
|
2101
|
-
return URL.canParse(value) && DISCORD_WEBHOOK_REGEX.test(value);
|
|
2102
|
-
}
|
|
2103
|
-
function jsonAttachment(name, description, data) {
|
|
2104
|
-
const content = (0, _seedcord_utils.filterCirculars)(data);
|
|
2105
|
-
return {
|
|
2106
|
-
name,
|
|
2107
|
-
description,
|
|
2108
|
-
data: Buffer.from(JSON.stringify(content, void 0, 2), "utf8")
|
|
2109
|
-
};
|
|
2110
|
-
}
|
|
2111
|
-
function neutralizeFences(text) {
|
|
2112
|
-
return text.replaceAll("```", "```");
|
|
2113
|
-
}
|
|
2114
|
-
/** An error's stack with its direct cause appended (one level), ANSI-stripped for the webhook payload. */
|
|
2115
|
-
function errorReport(error) {
|
|
2116
|
-
let report = error.stack ?? `${error.name}: ${error.message}`;
|
|
2117
|
-
if (Error.isError(error.cause)) {
|
|
2118
|
-
const cause = error.cause.stack ?? `${error.cause.name}: ${error.cause.message}`;
|
|
2119
|
-
report += `\n\nCaused by:\n${cause}`;
|
|
2120
|
-
}
|
|
2121
|
-
const stripped = neutralizeFences((0, _seedcord_utils.stripAnsi)(report));
|
|
2122
|
-
return stripped.length > 1800 ? `${stripped.slice(0, 1799)}…` : stripped;
|
|
2123
|
-
}
|
|
2124
|
-
var WebhookSeparator = class extends _seedcord_core.BuilderComponent {
|
|
2125
|
-
constructor() {
|
|
2126
|
-
super("separator");
|
|
2127
|
-
this.instance.setSpacing(discord_api_types_v10.SeparatorSpacingSize.Small).setDivider(true);
|
|
2128
|
-
}
|
|
2129
|
-
};
|
|
2130
|
-
|
|
2131
|
-
//#endregion
|
|
2132
|
-
//#region src/subscribers/bases/WebhookSender.ts
|
|
2133
|
-
const HTTP_NOT_FOUND = 404;
|
|
2134
|
-
const HTTP_UNAUTHORIZED = 401;
|
|
2135
|
-
/**
|
|
2136
|
-
* Sends component messages to a Discord webhook through `@discordjs/rest`.
|
|
2137
|
-
*
|
|
2138
|
-
* @throws A `SeedcordError` when the url does not carry a webhook id and token.
|
|
2139
|
-
*/
|
|
2140
|
-
var WebhookSender = class {
|
|
2141
|
-
rest = new _discordjs_rest.REST();
|
|
2142
|
-
route;
|
|
2143
|
-
constructor(url) {
|
|
2144
|
-
const match = /\/webhooks\/(\d+)\/([\w$-]+)$/.exec(url);
|
|
2145
|
-
if (!match?.[1] || !match[2]) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.ConfigWebhookUrlInvalid, ["The url"]);
|
|
2146
|
-
this.route = discord_api_types_v10.Routes.webhook(match[1], match[2]);
|
|
2147
|
-
}
|
|
2148
|
-
/**
|
|
2149
|
-
* Fetches the webhook object, sending no message. Returns `missing` on a 404 or 401 from
|
|
2150
|
-
* Discord and `unreachable` on any other failure.
|
|
2151
|
-
*/
|
|
2152
|
-
async verify() {
|
|
2153
|
-
try {
|
|
2154
|
-
await this.rest.get(this.route, { auth: false });
|
|
2155
|
-
return "ok";
|
|
2156
|
-
} catch (error) {
|
|
2157
|
-
return error instanceof _discordjs_rest.DiscordAPIError && (error.status === HTTP_NOT_FOUND || error.status === HTTP_UNAUTHORIZED) ? "missing" : "unreachable";
|
|
2158
|
-
}
|
|
2159
|
-
}
|
|
2160
|
-
async send(options) {
|
|
2161
|
-
const { flags, username, components, files } = options;
|
|
2162
|
-
await this.rest.post(this.route, {
|
|
2163
|
-
auth: false,
|
|
2164
|
-
query: new URLSearchParams({
|
|
2165
|
-
with_components: "true",
|
|
2166
|
-
wait: "true"
|
|
2167
|
-
}),
|
|
2168
|
-
body: {
|
|
2169
|
-
flags,
|
|
2170
|
-
username,
|
|
2171
|
-
components: components.map((component) => component.toJSON()),
|
|
2172
|
-
...files?.length && { attachments: files.map((file, id) => ({
|
|
2173
|
-
id,
|
|
2174
|
-
filename: file.name,
|
|
2175
|
-
description: file.description
|
|
2176
|
-
})) }
|
|
2177
|
-
},
|
|
2178
|
-
...files?.length && { files: files.map((file) => ({
|
|
2179
|
-
name: file.name,
|
|
2180
|
-
data: file.data
|
|
2181
|
-
})) }
|
|
2182
|
-
});
|
|
2183
|
-
}
|
|
2184
|
-
};
|
|
2185
|
-
|
|
2186
|
-
//#endregion
|
|
2187
|
-
//#region src/subscribers/bases/WebhookLog.ts
|
|
2188
|
-
/**
|
|
2189
|
-
* Base class for subscribers that deliver their event to a Discord webhook.
|
|
2190
|
-
*
|
|
2191
|
-
* Declare the url's environment variable with `@WebhookUrl` and implement {@link report}. Url
|
|
2192
|
-
* resolution, validation, sender reuse, and sending run in the base. When the variable is unset
|
|
2193
|
-
* the reporter is skipped at registration with a boot warning.
|
|
2194
|
-
*
|
|
2195
|
-
* @typeParam KeyOfSubscribers - The subscription key this reporter receives
|
|
2196
|
-
*/
|
|
2197
|
-
var WebhookLog = class WebhookLog extends Subscriber {
|
|
2198
|
-
static senders = /* @__PURE__ */ new Map();
|
|
2199
|
-
async execute() {
|
|
2200
|
-
const url = WebhookLog.urlOf(WebhookLog.envKeyOf(this.constructor));
|
|
2201
|
-
if (url === null) {
|
|
2202
|
-
this.logger.warn(`${chalk.default.bold(this.constructor.name)} has no webhook url set, this reporter is disabled`);
|
|
2203
|
-
return;
|
|
2204
|
-
}
|
|
2205
|
-
const { username, components, files } = await this.report();
|
|
2206
|
-
try {
|
|
2207
|
-
await WebhookLog.senderFor(url).send({
|
|
2208
|
-
flags: flagsFor(false),
|
|
2209
|
-
username: username ?? this.constructor.name,
|
|
2210
|
-
components,
|
|
2211
|
-
files
|
|
2212
|
-
});
|
|
2213
|
-
} catch (error) {
|
|
2214
|
-
this.logger.error("Failed to send webhook report", error);
|
|
2215
|
-
}
|
|
2216
|
-
}
|
|
2217
|
-
/** @internal */
|
|
2218
|
-
static senderFor(url) {
|
|
2219
|
-
let sender = WebhookLog.senders.get(url);
|
|
2220
|
-
if (!sender) {
|
|
2221
|
-
sender = new WebhookSender(url);
|
|
2222
|
-
WebhookLog.senders.set(url, sender);
|
|
2223
|
-
}
|
|
2224
|
-
return sender;
|
|
2225
|
-
}
|
|
2226
|
-
/** @internal */
|
|
2227
|
-
static envKeyOf(ctor) {
|
|
2228
|
-
const envKey = Reflect.getMetadata(_seedcord_core_internal.WebhookUrlMetadataKey, ctor);
|
|
2229
|
-
if (!envKey) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.DecoratorWebhookUrlMissing, [ctor.name]);
|
|
2230
|
-
return envKey;
|
|
2231
|
-
}
|
|
2232
|
-
/** @internal Returns null when the env var is unset, throws when set but malformed. */
|
|
2233
|
-
static urlOf(envKey) {
|
|
2234
|
-
if (!envapt.Envapter.has(envKey)) return null;
|
|
2235
|
-
const raw = envapt.Envapter.getRequired(envKey, envapt.Converters.String).trim();
|
|
2236
|
-
if (raw === "") return null;
|
|
2237
|
-
if (!isDiscordWebhookUrl(raw)) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.ConfigWebhookUrlInvalid, [envKey]);
|
|
2238
|
-
return raw;
|
|
2239
|
-
}
|
|
2240
|
-
};
|
|
1893
|
+
var Plugin = class extends _seedcord_core_plugin.Plugin {};
|
|
2241
1894
|
|
|
2242
1895
|
//#endregion
|
|
2243
1896
|
//#region src/bot/utilities/miscellaneous/contextMenuLeaves.ts
|
|
@@ -2293,8 +1946,7 @@ function slashRouteLeaves(commands) {
|
|
|
2293
1946
|
var CommandRegistry = class {
|
|
2294
1947
|
core;
|
|
2295
1948
|
onDeployed;
|
|
2296
|
-
|
|
2297
|
-
logger = new _seedcord_logger.Logger("Commands");
|
|
1949
|
+
logger = new _seedcord_logger.Logger("Commands", { channel: "commands" });
|
|
2298
1950
|
isInitialised = false;
|
|
2299
1951
|
globalCommands = [];
|
|
2300
1952
|
guildCommands = new discord_js.Collection();
|
|
@@ -2381,7 +2033,7 @@ var CommandRegistry = class {
|
|
|
2381
2033
|
this.registerCommand(exported, rel);
|
|
2382
2034
|
this.hmrHandler?.trackHandler(fullPath, exported);
|
|
2383
2035
|
}
|
|
2384
|
-
}
|
|
2036
|
+
});
|
|
2385
2037
|
}
|
|
2386
2038
|
isCommandClass(obj) {
|
|
2387
2039
|
if (typeof obj !== "function") return false;
|
|
@@ -2543,7 +2195,7 @@ function eventGateContext(eventName, args, core) {
|
|
|
2543
2195
|
|
|
2544
2196
|
//#endregion
|
|
2545
2197
|
//#region src/bot/handleEventFault.ts
|
|
2546
|
-
const logger$1 = new _seedcord_logger.Logger("EventBoundary");
|
|
2198
|
+
const logger$1 = new _seedcord_logger.Logger("EventBoundary", { channel: "errors" });
|
|
2547
2199
|
/**
|
|
2548
2200
|
* The event controller boundary. Reports a throw from an event handler and never replies, since a
|
|
2549
2201
|
* generic event has no reply target. A {@link Silence} or a non-reporting {@link Notice} stops quietly, a
|
|
@@ -2556,15 +2208,15 @@ function handleEventFault(caught, eventName, handlerName, args, core) {
|
|
|
2556
2208
|
if (caught.reason !== void 0 && (core.config.errors?.logSilences ?? true)) logger$1.debug(`Silence: ${caught.reason}`);
|
|
2557
2209
|
return;
|
|
2558
2210
|
}
|
|
2559
|
-
|
|
2211
|
+
const error = (0, _seedcord_core_internal.asError)(caught);
|
|
2560
2212
|
const ignore = new Set(core.config.errors?.ignoreEventApiCodes ?? []);
|
|
2561
|
-
if (
|
|
2562
|
-
logger$1.debug(`swallowed api code ${
|
|
2213
|
+
if (error instanceof discord_js.DiscordAPIError && ignore.has(error.code)) {
|
|
2214
|
+
logger$1.debug(`swallowed api code ${error.code}`);
|
|
2563
2215
|
return;
|
|
2564
2216
|
}
|
|
2565
|
-
if (
|
|
2217
|
+
if (error instanceof _seedcord_core.Notice && !error.report) return;
|
|
2566
2218
|
const actor = deriveEventActor(args);
|
|
2567
|
-
extractErrorResponse(
|
|
2219
|
+
extractErrorResponse(error, core, {
|
|
2568
2220
|
event: {
|
|
2569
2221
|
name: eventName,
|
|
2570
2222
|
handler: handlerName,
|
|
@@ -2579,21 +2231,22 @@ function handleEventFault(caught, eventName, handlerName, args, core) {
|
|
|
2579
2231
|
//#endregion
|
|
2580
2232
|
//#region src/bot/controllers/EventDispatcher.ts
|
|
2581
2233
|
/**
|
|
2582
|
-
* Scans event handler directories,
|
|
2583
|
-
*
|
|
2584
|
-
*
|
|
2234
|
+
* Scans event handler directories, attaches one client listener per registered event, and runs the
|
|
2235
|
+
* handlers for each fire. Interactions route through InteractionDispatcher. Multiple handlers can
|
|
2236
|
+
* point to one event.
|
|
2585
2237
|
*
|
|
2586
2238
|
* @internal
|
|
2587
2239
|
*/
|
|
2588
2240
|
var EventDispatcher = class {
|
|
2589
2241
|
core;
|
|
2590
|
-
logger = new _seedcord_logger.Logger("Events");
|
|
2242
|
+
logger = new _seedcord_logger.Logger("Events", { channel: "events" });
|
|
2591
2243
|
isInitialized = false;
|
|
2592
|
-
name = "Events";
|
|
2593
2244
|
eventMap = /* @__PURE__ */ new Map();
|
|
2594
2245
|
middlewares = [];
|
|
2595
2246
|
executedOnceHandlers = /* @__PURE__ */ new Set();
|
|
2596
2247
|
attachedEvents = /* @__PURE__ */ new Set();
|
|
2248
|
+
inFlight = /* @__PURE__ */ new Set();
|
|
2249
|
+
draining = false;
|
|
2597
2250
|
loading = false;
|
|
2598
2251
|
loadedHandlers = [];
|
|
2599
2252
|
loadedMiddlewares = [];
|
|
@@ -2657,7 +2310,7 @@ var EventDispatcher = class {
|
|
|
2657
2310
|
this.registerHandler(val, relativePath);
|
|
2658
2311
|
this.hmrHandler?.trackHandler(fullPath, val);
|
|
2659
2312
|
}
|
|
2660
|
-
}
|
|
2313
|
+
});
|
|
2661
2314
|
}
|
|
2662
2315
|
async loadMiddlewares(dir) {
|
|
2663
2316
|
await (0, _seedcord_utils_node.traverseDirectory)(dir, (fullPath, relativePath, imported) => {
|
|
@@ -2666,7 +2319,7 @@ var EventDispatcher = class {
|
|
|
2666
2319
|
this.registerMiddleware(val, relativePath);
|
|
2667
2320
|
this.hmrHandler?.trackMiddleware(fullPath, val);
|
|
2668
2321
|
}
|
|
2669
|
-
}
|
|
2322
|
+
});
|
|
2670
2323
|
}
|
|
2671
2324
|
/** @internal For use in dev mode */
|
|
2672
2325
|
async onHmr(event) {
|
|
@@ -2762,15 +2415,26 @@ var EventDispatcher = class {
|
|
|
2762
2415
|
const handlerEntries = this.eventMap.get(eventName);
|
|
2763
2416
|
this.logger.debug(`Attaching ${_seedcord_logger.paint.mint.bold(eventName)} to the client with ${_seedcord_logger.paint.mute(handlerEntries?.length ?? 0)} handler(s)`);
|
|
2764
2417
|
this.core.bot.client.on(eventName, (...args) => {
|
|
2765
|
-
this.
|
|
2766
|
-
(
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2418
|
+
if (this.draining) return;
|
|
2419
|
+
this.core.bus[_seedcord_core_internal.PublishDefault]("anyEvent", {
|
|
2420
|
+
name: eventName,
|
|
2421
|
+
args
|
|
2422
|
+
});
|
|
2423
|
+
const run = this.processEvent(eventName, args).catch((caught) => {
|
|
2424
|
+
const error = (0, _seedcord_core_internal.asError)(caught);
|
|
2425
|
+
this.logger.error(`[${_seedcord_logger.paint.coral.bold("UNHANDLED ERROR AT ROOT")}] ${error.name}`, error.stack);
|
|
2426
|
+
this.core.bus[_seedcord_core_internal.PublishDefault]("unhandledEventError", { error });
|
|
2427
|
+
});
|
|
2428
|
+
this.inFlight.add(run);
|
|
2429
|
+
run.finally(() => this.inFlight.delete(run));
|
|
2772
2430
|
});
|
|
2773
2431
|
}
|
|
2432
|
+
stopAccepting() {
|
|
2433
|
+
this.draining = true;
|
|
2434
|
+
}
|
|
2435
|
+
drain(timeoutMs) {
|
|
2436
|
+
return (0, _seedcord_core_node_internal.settleWithin)(Promise.allSettled(this.inFlight), timeoutMs);
|
|
2437
|
+
}
|
|
2774
2438
|
async processEvent(eventName, args) {
|
|
2775
2439
|
const handlerEntries = this.eventMap.get(eventName);
|
|
2776
2440
|
if (!handlerEntries || handlerEntries.length === 0) return;
|
|
@@ -2835,7 +2499,7 @@ const HARMLESS_API_CODES = new Set([
|
|
|
2835
2499
|
|
|
2836
2500
|
//#endregion
|
|
2837
2501
|
//#region src/bot/handleInteractionFault.ts
|
|
2838
|
-
const logger = new _seedcord_logger.Logger("InteractionBoundary");
|
|
2502
|
+
const logger = new _seedcord_logger.Logger("InteractionBoundary", { channel: "errors" });
|
|
2839
2503
|
/**
|
|
2840
2504
|
* The interaction controller boundary. Sorts a throw from any handler-lifecycle phase into the right
|
|
2841
2505
|
* user reply plus the right bus event. When a handler is built its sender is passed in, so the boundary
|
|
@@ -2844,33 +2508,45 @@ const logger = new _seedcord_logger.Logger("InteractionBoundary");
|
|
|
2844
2508
|
*
|
|
2845
2509
|
* @internal
|
|
2846
2510
|
*/
|
|
2847
|
-
async function handleInteractionFault(caught, interaction, core, sender) {
|
|
2511
|
+
async function handleInteractionFault(caught, interaction, core, routeId, sender) {
|
|
2848
2512
|
if (caught instanceof _seedcord_core.Silence) {
|
|
2849
2513
|
if (caught.reason !== void 0 && (core.config.errors?.logSilences ?? true)) logger.debug(`Silence: ${caught.reason}`);
|
|
2850
2514
|
return;
|
|
2851
2515
|
}
|
|
2852
|
-
|
|
2516
|
+
const error = (0, _seedcord_core_internal.asError)(caught);
|
|
2853
2517
|
const ignore = new Set(core.config.errors?.ignoreApiCodes ?? []);
|
|
2854
|
-
if (
|
|
2855
|
-
logger.debug(`swallowed api code ${
|
|
2518
|
+
if (error instanceof discord_js.DiscordAPIError && ignore.has(error.code)) {
|
|
2519
|
+
logger.debug(`swallowed api code ${error.code}`);
|
|
2856
2520
|
return;
|
|
2857
2521
|
}
|
|
2858
2522
|
if (interaction.isAutocomplete()) {
|
|
2859
|
-
extractErrorResponse(
|
|
2523
|
+
extractErrorResponse(error, core, {
|
|
2860
2524
|
guild: interaction.guild,
|
|
2861
2525
|
user: interaction.user,
|
|
2862
2526
|
metadata: interaction,
|
|
2863
2527
|
route: slashRouteOf(interaction)
|
|
2864
2528
|
});
|
|
2529
|
+
await sendEmptyChoices(interaction, core, routeId);
|
|
2865
2530
|
return;
|
|
2866
2531
|
}
|
|
2867
|
-
const { response } = extractErrorResponse(
|
|
2532
|
+
const { response } = extractErrorResponse(error, core, {
|
|
2868
2533
|
interaction,
|
|
2869
2534
|
guild: interaction.guild,
|
|
2870
2535
|
user: interaction.user,
|
|
2871
2536
|
metadata: interaction
|
|
2872
2537
|
});
|
|
2873
|
-
await sendGuarded(sender ?? new ReplySender(interaction,
|
|
2538
|
+
await sendGuarded(sender ?? new ReplySender(interaction, routeId, core.bus), response, error instanceof _seedcord_core.Notice ? error.ephemeral : true);
|
|
2539
|
+
}
|
|
2540
|
+
async function sendEmptyChoices(interaction, core, routeId) {
|
|
2541
|
+
const telemetry = {
|
|
2542
|
+
bus: core.bus,
|
|
2543
|
+
interactionId: interaction.id
|
|
2544
|
+
};
|
|
2545
|
+
try {
|
|
2546
|
+
await (0, _seedcord_core_internal.reportedWrite)(telemetry, routeId, "respond", () => interaction.respond([]));
|
|
2547
|
+
} catch (error) {
|
|
2548
|
+
logger.debug(`autocomplete empty-choices send failed: ${String(error)}`);
|
|
2549
|
+
}
|
|
2874
2550
|
}
|
|
2875
2551
|
async function sendGuarded(sender, response, ephemeral) {
|
|
2876
2552
|
try {
|
|
@@ -2894,9 +2570,8 @@ async function sendGuarded(sender, response, ephemeral) {
|
|
|
2894
2570
|
*/
|
|
2895
2571
|
var InteractionDispatcher = class {
|
|
2896
2572
|
core;
|
|
2897
|
-
logger = new _seedcord_logger.Logger("Interactions");
|
|
2573
|
+
logger = new _seedcord_logger.Logger("Interactions", { channel: "interactions" });
|
|
2898
2574
|
isInitialized = false;
|
|
2899
|
-
name = "Interactions";
|
|
2900
2575
|
slashMap = /* @__PURE__ */ new Map();
|
|
2901
2576
|
buttonMap = /* @__PURE__ */ new Map();
|
|
2902
2577
|
modalMap = /* @__PURE__ */ new Map();
|
|
@@ -2908,8 +2583,11 @@ var InteractionDispatcher = class {
|
|
|
2908
2583
|
messageContextMenuMap = /* @__PURE__ */ new Map();
|
|
2909
2584
|
userContextMenuMap = /* @__PURE__ */ new Map();
|
|
2910
2585
|
autocompleteMap = /* @__PURE__ */ new Map();
|
|
2586
|
+
handlerFiles = /* @__PURE__ */ new Map();
|
|
2911
2587
|
keysToIgnore = /* @__PURE__ */ new Set();
|
|
2912
2588
|
middlewares = [];
|
|
2589
|
+
inFlight = /* @__PURE__ */ new Set();
|
|
2590
|
+
draining = false;
|
|
2913
2591
|
loading = false;
|
|
2914
2592
|
loadedHandlers = [];
|
|
2915
2593
|
loadedMiddlewares = [];
|
|
@@ -3024,7 +2702,7 @@ var InteractionDispatcher = class {
|
|
|
3024
2702
|
this.registerHandler(val, relativePath);
|
|
3025
2703
|
this.hmrHandler?.trackHandler(fullPath, val);
|
|
3026
2704
|
}
|
|
3027
|
-
}
|
|
2705
|
+
});
|
|
3028
2706
|
}
|
|
3029
2707
|
async loadMiddlewares(dir) {
|
|
3030
2708
|
await (0, _seedcord_utils_node.traverseDirectory)(dir, (fullPath, relativePath, imported) => {
|
|
@@ -3033,7 +2711,7 @@ var InteractionDispatcher = class {
|
|
|
3033
2711
|
this.registerMiddleware(val, relativePath);
|
|
3034
2712
|
this.hmrHandler?.trackMiddleware(fullPath, val);
|
|
3035
2713
|
}
|
|
3036
|
-
}
|
|
2714
|
+
});
|
|
3037
2715
|
}
|
|
3038
2716
|
registerMiddleware(middlewareCtor, relativePath) {
|
|
3039
2717
|
const metadata = Reflect.getMetadata(_seedcord_core_internal.MiddlewareMetadataKey, middlewareCtor);
|
|
@@ -3063,6 +2741,7 @@ var InteractionDispatcher = class {
|
|
|
3063
2741
|
return obj.prototype instanceof InteractionMiddleware && Reflect.hasMetadata(_seedcord_core_internal.MiddlewareMetadataKey, obj);
|
|
3064
2742
|
}
|
|
3065
2743
|
registerHandler(handlerClass, relativePath) {
|
|
2744
|
+
const from = (0, _seedcord_utils.formatFilePath)(relativePath);
|
|
3066
2745
|
const writes = [];
|
|
3067
2746
|
for (const [routeType, map] of this.routeTypes) {
|
|
3068
2747
|
const meta = Reflect.getMetadata(_seedcord_core_internal.InteractionRouteKeys[routeType], handlerClass);
|
|
@@ -3070,16 +2749,16 @@ var InteractionDispatcher = class {
|
|
|
3070
2749
|
for (const route of meta) {
|
|
3071
2750
|
const existing = map.get(route);
|
|
3072
2751
|
if (existing && existing !== handlerClass) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.InteractionDuplicateRoute, [
|
|
3073
|
-
route
|
|
3074
|
-
existing.name
|
|
3075
|
-
handlerClass.name
|
|
2752
|
+
`${routeType}:${route}`,
|
|
2753
|
+
`${existing.name} (${this.handlerFiles.get(existing) ?? "unknown file"})`,
|
|
2754
|
+
`${handlerClass.name} (${from})`
|
|
3076
2755
|
]);
|
|
3077
2756
|
writes.push([map, route]);
|
|
3078
2757
|
}
|
|
3079
2758
|
}
|
|
3080
2759
|
if (writes.length === 0) return;
|
|
3081
2760
|
for (const [map, route] of writes) map.set(route, handlerClass);
|
|
3082
|
-
|
|
2761
|
+
this.handlerFiles.set(handlerClass, from);
|
|
3083
2762
|
if (this.loading) this.loadedHandlers.push({
|
|
3084
2763
|
name: handlerClass.name,
|
|
3085
2764
|
from
|
|
@@ -3091,6 +2770,7 @@ var InteractionDispatcher = class {
|
|
|
3091
2770
|
await this.hmrHandler?.handle(event);
|
|
3092
2771
|
}
|
|
3093
2772
|
unregisterHandler(handlerClass, artifacts) {
|
|
2773
|
+
this.handlerFiles.delete(handlerClass);
|
|
3094
2774
|
if (artifacts) {
|
|
3095
2775
|
for (const { routeType, routes } of artifacts) {
|
|
3096
2776
|
const map = this.routeTypes.find(([type]) => type === routeType)?.[1];
|
|
@@ -3110,44 +2790,92 @@ var InteractionDispatcher = class {
|
|
|
3110
2790
|
}
|
|
3111
2791
|
attachToClient() {
|
|
3112
2792
|
this.core.bot.client.on(discord_js.Events.InteractionCreate, (interaction) => {
|
|
3113
|
-
this.
|
|
3114
|
-
this.
|
|
3115
|
-
|
|
3116
|
-
|
|
2793
|
+
if (this.draining) return;
|
|
2794
|
+
this.core.bus[_seedcord_core_internal.PublishDefault]("anyInteraction", { interaction });
|
|
2795
|
+
const run = this.handleInteraction(interaction).catch((caught) => {
|
|
2796
|
+
const error = (0, _seedcord_core_internal.asError)(caught);
|
|
2797
|
+
this.logger.error(`[${_seedcord_logger.paint.coral.bold("UNHANDLED ERROR AT ROOT")}] ${error.name}`, error.stack);
|
|
2798
|
+
this.core.bus[_seedcord_core_internal.PublishDefault]("unhandledInteractionError", { error });
|
|
3117
2799
|
});
|
|
2800
|
+
this.inFlight.add(run);
|
|
2801
|
+
run.finally(() => this.inFlight.delete(run));
|
|
3118
2802
|
});
|
|
3119
2803
|
}
|
|
3120
|
-
|
|
2804
|
+
stopAccepting() {
|
|
2805
|
+
this.draining = true;
|
|
2806
|
+
}
|
|
2807
|
+
drain(timeoutMs) {
|
|
2808
|
+
return (0, _seedcord_core_node_internal.settleWithin)(Promise.allSettled(this.inFlight), timeoutMs);
|
|
2809
|
+
}
|
|
2810
|
+
async handleCustomIdInteraction(interaction, kind, getMap) {
|
|
3121
2811
|
if ([...this.keysToIgnore].some((matcher) => matcher.owns(interaction.customId))) return;
|
|
3122
2812
|
const prefix = (0, _seedcord_core_internal.prefixOf)(interaction.customId);
|
|
3123
|
-
if (!prefix)
|
|
3124
|
-
await this.processInteraction(interaction, () => prefix, (key) => getMap().get(key));
|
|
2813
|
+
if (!prefix) this.logger.warn(`${_seedcord_logger.paint.sky.bold(kind)} has invalid customId: ${_seedcord_logger.paint.mute(interaction.customId)}`);
|
|
2814
|
+
await this.processInteraction(interaction, kind, () => prefix, (key) => getMap().get(key));
|
|
3125
2815
|
}
|
|
3126
|
-
async processInteraction(interaction, extractKey, getHandler, fallback = UnhandledRepliable) {
|
|
2816
|
+
async processInteraction(interaction, kind, extractKey, getHandler, fallback = UnhandledRepliable) {
|
|
3127
2817
|
const key = extractKey(interaction);
|
|
2818
|
+
const startedAt = performance.now();
|
|
2819
|
+
const queuedMs = (0, _seedcord_core_internal.queuedMsFor)(interaction.id);
|
|
2820
|
+
const matched = getHandler(key);
|
|
2821
|
+
let routeId = `${kind}:${key || "unrouted"}`;
|
|
2822
|
+
let reported = false;
|
|
2823
|
+
const report = (outcome) => {
|
|
2824
|
+
if (reported) return;
|
|
2825
|
+
reported = true;
|
|
2826
|
+
this.core.bus[_seedcord_core_internal.PublishDefault]("interactionDispatched", (0, _seedcord_core_internal.dispatchedPayload)({
|
|
2827
|
+
routeId,
|
|
2828
|
+
interactionId: interaction.id,
|
|
2829
|
+
kind,
|
|
2830
|
+
outcome,
|
|
2831
|
+
startedAt,
|
|
2832
|
+
fallback: !matched,
|
|
2833
|
+
queuedMs
|
|
2834
|
+
}));
|
|
2835
|
+
};
|
|
3128
2836
|
let sender;
|
|
3129
2837
|
try {
|
|
3130
|
-
if (!interaction.isAutocomplete())
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
}
|
|
3136
|
-
this.logger.debug(`Processing ${_seedcord_logger.paint.mint.bold(key)} with ${_seedcord_logger.paint.mute(HandlerCtor.name)}`);
|
|
3137
|
-
const dispatch = new _seedcord_core.DispatchContext((0, _seedcord_core_internal.routeIdOf)(HandlerCtor));
|
|
3138
|
-
const handler = new HandlerCtor(interaction, this.core, dispatch);
|
|
2838
|
+
if (!interaction.isAutocomplete()) await this.runMiddlewares(interaction);
|
|
2839
|
+
const HandlerCtor = matched ?? fallback;
|
|
2840
|
+
const dispatch = new _seedcord_core.DispatchContext((0, _seedcord_core_internal.routeIdOf)(HandlerCtor) ?? routeId);
|
|
2841
|
+
routeId = dispatch.routeId ?? routeId;
|
|
2842
|
+
const handler = this.buildHandler(HandlerCtor, interaction, dispatch, key, !matched);
|
|
3139
2843
|
if (handler instanceof RepliableHandler) sender = handler.getSender();
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
} finally {
|
|
3145
|
-
monitor?.report(dispatch.routeId);
|
|
3146
|
-
}
|
|
2844
|
+
const refusal = interaction.isAutocomplete() ? null : await this.gateRefusal(HandlerCtor, interaction, dispatch.routeId);
|
|
2845
|
+
if (refusal) {
|
|
2846
|
+
await this.answer(refusal.caught, interaction, routeId, sender, report);
|
|
2847
|
+
return;
|
|
3147
2848
|
}
|
|
3148
2849
|
await handler.execute();
|
|
2850
|
+
report("handled");
|
|
2851
|
+
} catch (caught) {
|
|
2852
|
+
await this.answer(caught, interaction, routeId, sender, report);
|
|
2853
|
+
}
|
|
2854
|
+
}
|
|
2855
|
+
async answer(caught, interaction, routeId, sender, report) {
|
|
2856
|
+
try {
|
|
2857
|
+
await handleInteractionFault(caught, interaction, this.core, routeId, sender);
|
|
2858
|
+
} finally {
|
|
2859
|
+
report((0, _seedcord_core_internal.outcomeFor)(caught));
|
|
2860
|
+
}
|
|
2861
|
+
}
|
|
2862
|
+
buildHandler(HandlerCtor, interaction, dispatch, key, isFallback) {
|
|
2863
|
+
if (isFallback) this.logger.warn(`No handler found for key ${_seedcord_logger.paint.sky.bold(key)}. Falling back to ${HandlerCtor.name}.`);
|
|
2864
|
+
this.logger.debug(`Processing ${_seedcord_logger.paint.mint.bold(key)} with ${_seedcord_logger.paint.mute(HandlerCtor.name)}`);
|
|
2865
|
+
return new HandlerCtor(interaction, this.core, dispatch);
|
|
2866
|
+
}
|
|
2867
|
+
async runMiddlewares(interaction) {
|
|
2868
|
+
for (const { ctor } of this.middlewares) await new ctor(interaction, this.core).execute();
|
|
2869
|
+
}
|
|
2870
|
+
async gateRefusal(HandlerCtor, interaction, routeId) {
|
|
2871
|
+
const monitor = (0, _seedcord_core_internal.slowGateMonitor)();
|
|
2872
|
+
try {
|
|
2873
|
+
await (0, _seedcord_core_internal.runHandlerGates)(HandlerCtor, interactionGateContext(interaction, this.core), routeId ?? void 0, monitor?.observe);
|
|
2874
|
+
return null;
|
|
3149
2875
|
} catch (caught) {
|
|
3150
|
-
|
|
2876
|
+
return { caught };
|
|
2877
|
+
} finally {
|
|
2878
|
+
monitor?.report(routeId);
|
|
3151
2879
|
}
|
|
3152
2880
|
}
|
|
3153
2881
|
async handleInteraction(interaction) {
|
|
@@ -3192,47 +2920,41 @@ var InteractionDispatcher = class {
|
|
|
3192
2920
|
}
|
|
3193
2921
|
async handleSlashCommand(interaction) {
|
|
3194
2922
|
const route = slashRouteOf(interaction);
|
|
3195
|
-
await this.processInteraction(interaction, () => route, (key) => this.slashMap.get(key));
|
|
2923
|
+
await this.processInteraction(interaction, _seedcord_core_internal.InteractionRoutes.Slash, () => route, (key) => this.slashMap.get(key));
|
|
3196
2924
|
}
|
|
3197
2925
|
async handleButton(interaction) {
|
|
3198
|
-
await this.handleCustomIdInteraction(interaction, () => this.buttonMap
|
|
2926
|
+
await this.handleCustomIdInteraction(interaction, _seedcord_core_internal.InteractionRoutes.Button, () => this.buttonMap);
|
|
3199
2927
|
}
|
|
3200
2928
|
async handleModal(interaction) {
|
|
3201
|
-
await this.handleCustomIdInteraction(interaction, () => this.modalMap
|
|
2929
|
+
await this.handleCustomIdInteraction(interaction, _seedcord_core_internal.InteractionRoutes.Modal, () => this.modalMap);
|
|
3202
2930
|
}
|
|
3203
2931
|
async handleStringSelectMenu(interaction) {
|
|
3204
|
-
await this.handleCustomIdInteraction(interaction, () => this.stringSelectMap
|
|
2932
|
+
await this.handleCustomIdInteraction(interaction, _seedcord_core_internal.InteractionRoutes.StringMenu, () => this.stringSelectMap);
|
|
3205
2933
|
}
|
|
3206
2934
|
async handleUserSelectMenu(interaction) {
|
|
3207
|
-
await this.handleCustomIdInteraction(interaction, () => this.userSelectMap
|
|
2935
|
+
await this.handleCustomIdInteraction(interaction, _seedcord_core_internal.InteractionRoutes.UserMenu, () => this.userSelectMap);
|
|
3208
2936
|
}
|
|
3209
2937
|
async handleRoleSelectMenu(interaction) {
|
|
3210
|
-
await this.handleCustomIdInteraction(interaction, () => this.roleSelectMap
|
|
2938
|
+
await this.handleCustomIdInteraction(interaction, _seedcord_core_internal.InteractionRoutes.RoleMenu, () => this.roleSelectMap);
|
|
3211
2939
|
}
|
|
3212
2940
|
async handleChannelSelectMenu(interaction) {
|
|
3213
|
-
await this.handleCustomIdInteraction(interaction, () => this.channelSelectMap
|
|
2941
|
+
await this.handleCustomIdInteraction(interaction, _seedcord_core_internal.InteractionRoutes.ChannelMenu, () => this.channelSelectMap);
|
|
3214
2942
|
}
|
|
3215
2943
|
async handleMentionableSelectMenu(interaction) {
|
|
3216
|
-
await this.handleCustomIdInteraction(interaction, () => this.mentionableSelectMap
|
|
2944
|
+
await this.handleCustomIdInteraction(interaction, _seedcord_core_internal.InteractionRoutes.MentionableMenu, () => this.mentionableSelectMap);
|
|
3217
2945
|
}
|
|
3218
2946
|
async handleMessageContextMenu(interaction) {
|
|
3219
|
-
await this.processInteraction(interaction, () => interaction.commandName, (key) => this.messageContextMenuMap.get(key));
|
|
2947
|
+
await this.processInteraction(interaction, _seedcord_core_internal.InteractionRoutes.MessageContextMenu, () => interaction.commandName, (key) => this.messageContextMenuMap.get(key));
|
|
3220
2948
|
}
|
|
3221
2949
|
async handleUserContextMenu(interaction) {
|
|
3222
|
-
await this.processInteraction(interaction, () => interaction.commandName, (key) => this.userContextMenuMap.get(key));
|
|
2950
|
+
await this.processInteraction(interaction, _seedcord_core_internal.InteractionRoutes.UserContextMenu, () => interaction.commandName, (key) => this.userContextMenuMap.get(key));
|
|
3223
2951
|
}
|
|
3224
2952
|
async handleAutocomplete(interaction) {
|
|
3225
2953
|
const route = slashRouteOf(interaction);
|
|
3226
|
-
await this.processInteraction(interaction, () => route, (key) => this.autocompleteMap.get(key), UnhandledAutocomplete);
|
|
2954
|
+
await this.processInteraction(interaction, _seedcord_core_internal.InteractionRoutes.Autocomplete, () => route, (key) => this.autocompleteMap.get(key), UnhandledAutocomplete);
|
|
3227
2955
|
}
|
|
3228
2956
|
};
|
|
3229
2957
|
|
|
3230
|
-
//#endregion
|
|
3231
|
-
//#region \0@oxc-project+runtime@0.135.0/helpers/esm/decorateMetadata.js
|
|
3232
|
-
function __decorateMetadata(k, v) {
|
|
3233
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
3234
|
-
}
|
|
3235
|
-
|
|
3236
2958
|
//#endregion
|
|
3237
2959
|
//#region \0@oxc-project+runtime@0.135.0/helpers/esm/decorate.js
|
|
3238
2960
|
function __decorate(decorators, target, key, desc) {
|
|
@@ -3244,23 +2966,25 @@ function __decorate(decorators, target, key, desc) {
|
|
|
3244
2966
|
|
|
3245
2967
|
//#endregion
|
|
3246
2968
|
//#region src/bot/Bot.ts
|
|
2969
|
+
const DISPATCH_DRAIN_TIMEOUT_MS = 5e3;
|
|
2970
|
+
const DRAIN_TASK_TIMEOUT_MS = 6e3;
|
|
2971
|
+
const UNBIND_TIMEOUT_MS = 2e3;
|
|
2972
|
+
const LOGOUT_TIMEOUT_MS = 2e3;
|
|
3247
2973
|
/**
|
|
3248
|
-
* Discord
|
|
3249
|
-
*
|
|
3250
|
-
* Don't instantiate this class directly. Use `core.bot` instead.
|
|
2974
|
+
* The Discord client and its controllers. Access it through `core.bot`.
|
|
3251
2975
|
*/
|
|
3252
|
-
var Bot = class
|
|
2976
|
+
var Bot = class {
|
|
3253
2977
|
/** @internal */
|
|
3254
|
-
logger = new _seedcord_logger.Logger("Bot");
|
|
2978
|
+
logger = new _seedcord_logger.Logger("Bot", { channel: "bot" });
|
|
3255
2979
|
isInitialized = false;
|
|
3256
2980
|
_client;
|
|
3257
2981
|
interactions;
|
|
3258
2982
|
events;
|
|
3259
|
-
commands;
|
|
3260
2983
|
emojiInjector;
|
|
2984
|
+
commands;
|
|
3261
2985
|
emojis = Emojis;
|
|
3262
2986
|
mentions = CommandMentions;
|
|
3263
|
-
/** @internal
|
|
2987
|
+
/** @internal */
|
|
3264
2988
|
async onHmr(event) {
|
|
3265
2989
|
if (this.interactions) await this.interactions.onHmr(event);
|
|
3266
2990
|
if (this.events) await this.events.onHmr(event);
|
|
@@ -3268,7 +2992,6 @@ var Bot = class extends Plugin {
|
|
|
3268
2992
|
}
|
|
3269
2993
|
/** @internal */
|
|
3270
2994
|
constructor(core) {
|
|
3271
|
-
super(core);
|
|
3272
2995
|
this._client = new discord_js.Client(core.config.bot.clientOptions);
|
|
3273
2996
|
if (core.config.bot.interactions.path) this.interactions = new InteractionDispatcher(core);
|
|
3274
2997
|
if (core.config.bot.events.path) this.events = new EventDispatcher(core);
|
|
@@ -3279,12 +3002,24 @@ var Bot = class extends Plugin {
|
|
|
3279
3002
|
});
|
|
3280
3003
|
}
|
|
3281
3004
|
this.emojiInjector = new EmojiInjector(core);
|
|
3282
|
-
|
|
3005
|
+
this.registerShutdownTasks(core);
|
|
3283
3006
|
}
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3007
|
+
registerShutdownTasks(core) {
|
|
3008
|
+
core.shutdown.addTask(_seedcord_core_node_internal.ShutdownPhase.Unbind, "stop-dispatch", () => {
|
|
3009
|
+
this.stopAccepting();
|
|
3010
|
+
return Promise.resolve();
|
|
3011
|
+
}, UNBIND_TIMEOUT_MS);
|
|
3012
|
+
core.shutdown.addTask(_seedcord_core_node_internal.ShutdownPhase.Drain, "drain-dispatch", () => this.drain(), DRAIN_TASK_TIMEOUT_MS);
|
|
3013
|
+
core.shutdown.addTask(_seedcord_core_node_internal.ShutdownPhase.Logout, "stop-bot", async () => await this.stop(), LOGOUT_TIMEOUT_MS);
|
|
3014
|
+
}
|
|
3015
|
+
stopAccepting() {
|
|
3016
|
+
this.interactions?.stopAccepting();
|
|
3017
|
+
this.events?.stopAccepting();
|
|
3018
|
+
}
|
|
3019
|
+
async drain() {
|
|
3020
|
+
await Promise.allSettled([this.interactions?.drain(DISPATCH_DRAIN_TIMEOUT_MS), this.events?.drain(DISPATCH_DRAIN_TIMEOUT_MS)]);
|
|
3021
|
+
}
|
|
3022
|
+
/** @internal */
|
|
3288
3023
|
async init() {
|
|
3289
3024
|
if (this.isInitialized) return;
|
|
3290
3025
|
this.isInitialized = true;
|
|
@@ -3300,27 +3035,19 @@ var Bot = class extends Plugin {
|
|
|
3300
3035
|
this.interactions?.warnUnhandledContextMenuRoutes(this.commands.contextMenuLeaves());
|
|
3301
3036
|
}
|
|
3302
3037
|
}
|
|
3303
|
-
/**
|
|
3304
|
-
* Stops the bot and cleans up connections
|
|
3305
|
-
* @internal
|
|
3306
|
-
*/
|
|
3038
|
+
/** @internal */
|
|
3307
3039
|
async stop() {
|
|
3308
3040
|
this._client.removeAllListeners();
|
|
3309
3041
|
await this.logout();
|
|
3310
3042
|
}
|
|
3311
|
-
/**
|
|
3312
|
-
* Logs the bot into Discord using the configured token
|
|
3313
|
-
*/
|
|
3314
3043
|
async login(token) {
|
|
3315
|
-
|
|
3044
|
+
const ready = Promise.withResolvers();
|
|
3045
|
+
this._client.once(discord_js.Events.ClientReady, () => ready.resolve());
|
|
3316
3046
|
this._client.login(token);
|
|
3317
|
-
await
|
|
3047
|
+
await ready.promise;
|
|
3318
3048
|
this.logger.info(`Logged in as ${chalk.default.bold.magenta(this._client.user?.username)}!`);
|
|
3319
3049
|
return this;
|
|
3320
3050
|
}
|
|
3321
|
-
/**
|
|
3322
|
-
* Logs out and destroys the Discord client connection
|
|
3323
|
-
*/
|
|
3324
3051
|
async logout() {
|
|
3325
3052
|
await this._client.destroy();
|
|
3326
3053
|
this.logger.info(chalk.default.bold.red("Logged out of Discord!"));
|
|
@@ -3328,250 +3055,21 @@ var Bot = class extends Plugin {
|
|
|
3328
3055
|
get client() {
|
|
3329
3056
|
return this._client;
|
|
3330
3057
|
}
|
|
3331
|
-
emit(event, ...args) {
|
|
3332
|
-
return super.emit(event, ...args);
|
|
3333
|
-
}
|
|
3334
|
-
emitSafe(event, ...args) {
|
|
3335
|
-
return super.emitSafe(event, ...args);
|
|
3336
|
-
}
|
|
3337
|
-
onListenerError(error, event) {
|
|
3338
|
-
this.logger.error(`listener for ${String(event)} threw`, error);
|
|
3339
|
-
}
|
|
3340
|
-
};
|
|
3341
|
-
__decorate([(0, envapt_legacy.Envapt)("DISCORD_BOT_TOKEN", { converter: (raw) => (0, _seedcord_errors_internal.validateDiscordToken)(raw) }), __decorateMetadata("design:type", String)], Bot.prototype, "botToken", void 0);
|
|
3342
|
-
|
|
3343
|
-
//#endregion
|
|
3344
|
-
//#region src/subscribers/default/HandledException.ts
|
|
3345
|
-
let HandledException = class HandledException extends WebhookLog {
|
|
3346
|
-
report() {
|
|
3347
|
-
return {
|
|
3348
|
-
username: "Handled Exception",
|
|
3349
|
-
components: [new HandledExceptionContainer(this.data).component],
|
|
3350
|
-
files: [jsonAttachment("source.json", "The raw source the fault came from", this.data.source.raw)]
|
|
3351
|
-
};
|
|
3352
|
-
}
|
|
3353
|
-
};
|
|
3354
|
-
HandledException = __decorate([Subscribe("handledException"), WebhookUrl("HANDLED_EXCEPTION_WEBHOOK_URL")], HandledException);
|
|
3355
|
-
var HandledExceptionContainer = class extends _seedcord_core.BuilderComponent {
|
|
3356
|
-
constructor(data) {
|
|
3357
|
-
super("container");
|
|
3358
|
-
const { denial, uuid, source } = data;
|
|
3359
|
-
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"));
|
|
3360
|
-
}
|
|
3361
|
-
};
|
|
3362
|
-
function faultSummary(denial, source) {
|
|
3363
|
-
const head = (0, _seedcord_utils.stripAnsi)(`### A handled fault was reported: \`${denial.name}\`\n**Message:** ${denial.message}\n`);
|
|
3364
|
-
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"}\``;
|
|
3365
|
-
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}\``;
|
|
3366
|
-
}
|
|
3367
|
-
function causeStack(denial) {
|
|
3368
|
-
const { cause } = denial;
|
|
3369
|
-
if (Error.isError(cause)) return (0, _seedcord_utils.stripAnsi)(cause.stack ?? cause.message);
|
|
3370
|
-
if (typeof cause === "string") return (0, _seedcord_utils.stripAnsi)(cause);
|
|
3371
|
-
if (typeof cause === "bigint" || typeof cause === "symbol" || typeof cause === "function") return cause.toString();
|
|
3372
|
-
if (cause !== void 0) try {
|
|
3373
|
-
return JSON.stringify(cause);
|
|
3374
|
-
} catch {
|
|
3375
|
-
return "[unserializable cause]";
|
|
3376
|
-
}
|
|
3377
|
-
return (0, _seedcord_utils.stripAnsi)(denial.stack ?? "No cause recorded.");
|
|
3378
|
-
}
|
|
3379
|
-
|
|
3380
|
-
//#endregion
|
|
3381
|
-
//#region src/subscribers/default/UnknownException.ts
|
|
3382
|
-
let UnknownException = class UnknownException extends WebhookLog {
|
|
3383
|
-
report() {
|
|
3384
|
-
const { metadata } = this.data;
|
|
3385
|
-
return {
|
|
3386
|
-
username: "Unknown Exception",
|
|
3387
|
-
components: [new UnhandledErrorContainer(this.data).component],
|
|
3388
|
-
files: metadata ? [jsonAttachment("metadata.json", "Metadata associated with the error", metadata)] : void 0
|
|
3389
|
-
};
|
|
3390
|
-
}
|
|
3391
|
-
};
|
|
3392
|
-
UnknownException = __decorate([Subscribe("unknownException"), WebhookUrl("UNKNOWN_EXCEPTION_WEBHOOK_URL")], UnknownException);
|
|
3393
|
-
var UnhandledErrorContainer = class extends _seedcord_core.BuilderComponent {
|
|
3394
|
-
constructor(data) {
|
|
3395
|
-
super("container");
|
|
3396
|
-
const { uuid, error, guild, user, metadata } = data;
|
|
3397
|
-
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)}\`\`\``));
|
|
3398
|
-
this.addTimestampsIfAvailable(error);
|
|
3399
|
-
this.addMetadataIfAvailable(metadata);
|
|
3400
|
-
}
|
|
3401
|
-
addTimestampsIfAvailable(error) {
|
|
3402
|
-
if (!(error instanceof _discordjs_rest.DiscordAPIError)) return;
|
|
3403
|
-
const now = Date.now();
|
|
3404
|
-
const snowflake = error.url.match(/\/interactions\/(\d+)\//)?.[1];
|
|
3405
|
-
if (!snowflake) return void 0;
|
|
3406
|
-
const interactionTs = (0, _seedcord_utils.timestampFromSnowflake)(snowflake);
|
|
3407
|
-
const diff = now - interactionTs;
|
|
3408
|
-
const seconds = Math.floor(diff / 1e3);
|
|
3409
|
-
const millis = diff % 1e3;
|
|
3410
|
-
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`));
|
|
3411
|
-
}
|
|
3412
|
-
addMetadataIfAvailable(metadata) {
|
|
3413
|
-
if (!metadata) return;
|
|
3414
|
-
this.instance.addSeparatorComponents(new WebhookSeparator().component).addTextDisplayComponents((text) => text.setContent("### Metadata")).addFileComponents((file) => file.setURL("attachment://metadata.json"));
|
|
3415
|
-
}
|
|
3416
|
-
};
|
|
3417
|
-
|
|
3418
|
-
//#endregion
|
|
3419
|
-
//#region src/subscribers/Bus.ts
|
|
3420
|
-
/**
|
|
3421
|
-
* Registers and dispatches subscribers. Subscribers load from the configured directories and run
|
|
3422
|
-
* on a programmatic or framework-event publish. Accessed via `core.bus`. Do not construct it directly.
|
|
3423
|
-
*/
|
|
3424
|
-
var Bus = class extends Plugin {
|
|
3425
|
-
core;
|
|
3426
|
-
logger = new _seedcord_logger.Logger("Subscribers");
|
|
3427
|
-
/** @internal */
|
|
3428
|
-
name = "Subscribers";
|
|
3429
|
-
isInitialized = false;
|
|
3430
|
-
subscribersMap = /* @__PURE__ */ new Map();
|
|
3431
|
-
executedOnceHandlers = /* @__PURE__ */ new Set();
|
|
3432
|
-
webhookProbes = /* @__PURE__ */ new Map();
|
|
3433
|
-
hmrHandler;
|
|
3434
|
-
constructor(core) {
|
|
3435
|
-
super(core);
|
|
3436
|
-
this.core = core;
|
|
3437
|
-
if (this.core.config.subscribers.path) {
|
|
3438
|
-
if (!envapt.Envapter.isDevelopment && !envapt.Envapter.isTest) return;
|
|
3439
|
-
this.hmrHandler = new _seedcord_core_hmr.HmrModuleHandler({
|
|
3440
|
-
handlersDir: this.core.config.subscribers.path,
|
|
3441
|
-
isHandler: this.isSubscriber.bind(this),
|
|
3442
|
-
registerHandler: this.registerSubscriber.bind(this),
|
|
3443
|
-
unregisterHandler: this.unregisterSubscriber.bind(this),
|
|
3444
|
-
getArtifacts: this.getArtifacts.bind(this),
|
|
3445
|
-
logger: this.logger
|
|
3446
|
-
});
|
|
3447
|
-
}
|
|
3448
|
-
}
|
|
3449
|
-
getArtifacts(ctor) {
|
|
3450
|
-
return [Reflect.getMetadata(_seedcord_core_internal.SubscribeMetadataKey, ctor).subscriber];
|
|
3451
|
-
}
|
|
3452
|
-
/** @internal */
|
|
3453
|
-
async init() {
|
|
3454
|
-
if (this.isInitialized) return;
|
|
3455
|
-
this.isInitialized = true;
|
|
3456
|
-
this.registerSubscriber(UnknownException);
|
|
3457
|
-
this.registerSubscriber(HandledException);
|
|
3458
|
-
const subscribersDir = this.core.config.subscribers.path;
|
|
3459
|
-
if (subscribersDir) {
|
|
3460
|
-
this.logger.info(chalk.default.bold(subscribersDir));
|
|
3461
|
-
await this.loadSubscribers(subscribersDir);
|
|
3462
|
-
const totalSubscribers = [...this.subscribersMap.values()].reduce((acc, handlers) => acc + handlers.length, 0);
|
|
3463
|
-
this.logger.utils.list([`${chalk.default.bold.magenta(totalSubscribers)} subscribers`], chalk.default.bold.green("Loaded"));
|
|
3464
|
-
}
|
|
3465
|
-
if (!envapt.Envapter.isTest) await this.verifyWebhooks();
|
|
3466
|
-
}
|
|
3467
|
-
/** @internal Exposed for tests. */
|
|
3468
|
-
async verifyWebhooks() {
|
|
3469
|
-
const missing = [];
|
|
3470
|
-
await Promise.all([...this.webhookProbes].map(async ([url, envKeys]) => {
|
|
3471
|
-
const result = await WebhookLog.senderFor(url).verify();
|
|
3472
|
-
if (result === "missing") missing.push(...envKeys);
|
|
3473
|
-
else if (result === "unreachable") this.logger.warn(`could not verify webhook at ${_seedcord_logger.paint.sky.bold(envKeys.join(", "))}`);
|
|
3474
|
-
}));
|
|
3475
|
-
if (missing.length > 0) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.ConfigWebhookNotFound, [missing.join(", ")]);
|
|
3476
|
-
}
|
|
3477
|
-
/** @internal */
|
|
3478
|
-
async onHmr(event) {
|
|
3479
|
-
if (this.hmrHandler) await this.hmrHandler.handle(event);
|
|
3480
|
-
}
|
|
3481
|
-
async loadSubscribers(dir) {
|
|
3482
|
-
await (0, _seedcord_utils_node.traverseDirectory)(dir, (fullPath, relativePath, imported) => {
|
|
3483
|
-
for (const exportName of Object.keys(imported)) {
|
|
3484
|
-
const val = imported[exportName];
|
|
3485
|
-
if (this.isSubscriber(val)) {
|
|
3486
|
-
this.registerSubscriber(val);
|
|
3487
|
-
this.hmrHandler?.trackHandler(fullPath, val);
|
|
3488
|
-
this.logger.utils.registration(val.name, relativePath);
|
|
3489
|
-
}
|
|
3490
|
-
}
|
|
3491
|
-
}, this.logger);
|
|
3492
|
-
}
|
|
3493
|
-
registerSubscriber(handler) {
|
|
3494
|
-
if (handler.prototype instanceof WebhookLog) {
|
|
3495
|
-
const envKey = WebhookLog.envKeyOf(handler);
|
|
3496
|
-
const url = WebhookLog.urlOf(envKey);
|
|
3497
|
-
if (url === null) {
|
|
3498
|
-
this.logger.warn(`${chalk.default.bold(handler.name)} disabled, ${_seedcord_logger.paint.sky.bold(envKey)} is not set`);
|
|
3499
|
-
return;
|
|
3500
|
-
}
|
|
3501
|
-
const envKeys = this.webhookProbes.get(url) ?? [];
|
|
3502
|
-
if (!envKeys.includes(envKey)) envKeys.push(envKey);
|
|
3503
|
-
this.webhookProbes.set(url, envKeys);
|
|
3504
|
-
}
|
|
3505
|
-
const options = Reflect.getMetadata(_seedcord_core_internal.SubscribeMetadataKey, handler);
|
|
3506
|
-
let handlers = this.subscribersMap.get(options.subscriber);
|
|
3507
|
-
if (!handlers) {
|
|
3508
|
-
handlers = [];
|
|
3509
|
-
this.subscribersMap.set(options.subscriber, handlers);
|
|
3510
|
-
}
|
|
3511
|
-
handlers.push({
|
|
3512
|
-
ctor: handler,
|
|
3513
|
-
frequency: options.frequency ?? "on"
|
|
3514
|
-
});
|
|
3515
|
-
}
|
|
3516
|
-
unregisterSubscriber(handler, artifacts) {
|
|
3517
|
-
const subscribers = artifacts ?? [...this.subscribersMap.keys()];
|
|
3518
|
-
for (const subscriber of subscribers) {
|
|
3519
|
-
const handlers = this.subscribersMap.get(subscriber);
|
|
3520
|
-
if (!handlers) continue;
|
|
3521
|
-
const index = handlers.findIndex((entry) => entry.ctor === handler);
|
|
3522
|
-
if (index !== -1) handlers.splice(index, 1);
|
|
3523
|
-
}
|
|
3524
|
-
}
|
|
3525
|
-
isSubscriber(obj) {
|
|
3526
|
-
if (typeof obj !== "function") return false;
|
|
3527
|
-
return obj.prototype instanceof Subscriber && Reflect.hasMetadata(_seedcord_core_internal.SubscribeMetadataKey, obj);
|
|
3528
|
-
}
|
|
3529
|
-
/**
|
|
3530
|
-
* Publishes an event to its subscribers and native listeners.
|
|
3531
|
-
*
|
|
3532
|
-
* Fire-and-forget, so subscriber handlers run asynchronously and this returns before they
|
|
3533
|
-
* complete, and callers must not assume side effects have landed. Errors thrown by a subscriber
|
|
3534
|
-
* are caught and logged, never surfaced here. A `'once'` subscriber is marked as executed when
|
|
3535
|
-
* it starts (even if it throws), so it never runs twice.
|
|
3536
|
-
*
|
|
3537
|
-
* @param event - The subscription key to publish
|
|
3538
|
-
* @param data - Payload passed to each subscriber
|
|
3539
|
-
* @returns Whether the underlying emitter had native listeners for the event
|
|
3540
|
-
*/
|
|
3541
|
-
publish(event, data) {
|
|
3542
|
-
this.processSubscriber(event, data);
|
|
3543
|
-
return super.emit(event, ...[data]);
|
|
3544
|
-
}
|
|
3545
|
-
async processSubscriber(subscriberName, data) {
|
|
3546
|
-
const handlers = this.subscribersMap.get(subscriberName);
|
|
3547
|
-
if (!handlers) return;
|
|
3548
|
-
for (const entry of handlers) {
|
|
3549
|
-
if (entry.frequency === "once" && this.executedOnceHandlers.has(entry.ctor)) continue;
|
|
3550
|
-
try {
|
|
3551
|
-
if (entry.frequency === "once") this.executedOnceHandlers.add(entry.ctor);
|
|
3552
|
-
const Ctor = entry.ctor;
|
|
3553
|
-
await new Ctor(data, this.core).execute();
|
|
3554
|
-
} catch (err) {
|
|
3555
|
-
this.logger.error(`Error in subscriber ${String(subscriberName)} handler ${entry.ctor.name}:`, err);
|
|
3556
|
-
}
|
|
3557
|
-
}
|
|
3558
|
-
}
|
|
3559
3058
|
};
|
|
3059
|
+
__decorate([(0, envapt_legacy.Envapt)("DISCORD_BOT_TOKEN", { converter: (raw) => (0, _seedcord_errors_internal.validateDiscordToken)(raw) })], Bot.prototype, "botToken", void 0);
|
|
3560
3060
|
|
|
3561
3061
|
//#endregion
|
|
3562
3062
|
//#region src/version.ts
|
|
3563
3063
|
/** Package version */
|
|
3564
|
-
const version = "0.1.0-next.
|
|
3064
|
+
const version = "0.1.0-next.5";
|
|
3565
3065
|
|
|
3566
3066
|
//#endregion
|
|
3567
3067
|
//#region src/Seedcord.ts
|
|
3568
3068
|
/**
|
|
3569
|
-
*
|
|
3570
|
-
*
|
|
3571
|
-
* Primary entry point for creating Discord bots with Seedcord.
|
|
3572
|
-
* Manages component lifecycle and provides plugin support.
|
|
3069
|
+
* The gateway bot host. Opens a discord.js gateway session, discovers handlers, and runs
|
|
3070
|
+
* coordinated startup and shutdown. Attach plugins with `attach()`.
|
|
3573
3071
|
*/
|
|
3574
|
-
var Seedcord = class Seedcord extends Pluggable {
|
|
3072
|
+
var Seedcord = class Seedcord extends _seedcord_core_node_internal.Pluggable {
|
|
3575
3073
|
config;
|
|
3576
3074
|
/** @internal */
|
|
3577
3075
|
[_seedcord_types_internal.SeedcordBrand] = true;
|
|
@@ -3579,83 +3077,86 @@ var Seedcord = class Seedcord extends Pluggable {
|
|
|
3579
3077
|
augmentTarget = "@seedcord/gateway";
|
|
3580
3078
|
/** @internal */
|
|
3581
3079
|
version = version;
|
|
3582
|
-
static isInstantiated = false;
|
|
3583
3080
|
healthCheck;
|
|
3584
3081
|
hmrManager;
|
|
3585
|
-
/** @see {@link CoordinatedShutdown} */
|
|
3586
|
-
shutdown;
|
|
3587
|
-
/** @see {@link CoordinatedStartup} */
|
|
3588
|
-
startup;
|
|
3589
3082
|
/** @see {@link Bus} */
|
|
3590
3083
|
bus;
|
|
3084
|
+
subscribers;
|
|
3591
3085
|
/** @see {@link Bot} */
|
|
3592
3086
|
bot;
|
|
3593
3087
|
/** @see {@link IRateLimiter} */
|
|
3594
3088
|
rateLimiter;
|
|
3595
3089
|
/**
|
|
3596
|
-
* Creates a new Seedcord instance
|
|
3597
|
-
*
|
|
3598
3090
|
* @param config - Bot configuration including paths and Discord client options
|
|
3599
3091
|
* @throws A **SeedcordError** When attempting to create multiple instances (singleton)
|
|
3600
3092
|
*/
|
|
3601
3093
|
constructor(config) {
|
|
3602
|
-
|
|
3603
|
-
Seedcord.isInstantiated = true;
|
|
3604
|
-
const shutdown = new _seedcord_core_node.CoordinatedShutdown(config.shutdownEnabled);
|
|
3605
|
-
const startup = new _seedcord_core_node.CoordinatedStartup();
|
|
3606
|
-
super(shutdown, startup);
|
|
3094
|
+
super(new _seedcord_core_node_internal.CoordinatedShutdown(), new _seedcord_core_node_internal.CoordinatedStartup());
|
|
3607
3095
|
this.config = config;
|
|
3608
3096
|
(0, _seedcord_logger_node.installNodeDefaults)(config.logger);
|
|
3609
3097
|
(0, _seedcord_core_internal.setBotColor)(config.botColor);
|
|
3610
|
-
this.shutdown = shutdown;
|
|
3611
|
-
this.startup = startup;
|
|
3612
3098
|
this.hmrManager = new _seedcord_core_internal.HmrManager();
|
|
3613
3099
|
this.hmrManager.init();
|
|
3614
|
-
this.bus = new Bus(this);
|
|
3100
|
+
this.bus = new _seedcord_core.Bus(this);
|
|
3101
|
+
this.subscribers = new _seedcord_core_node_internal.SubscriberLoader(this.bus, config.subscribers.path);
|
|
3615
3102
|
this.bot = new Bot(this);
|
|
3616
3103
|
this.rateLimiter = config.store ?? new _seedcord_rate_limiter.MemoryRateLimiter();
|
|
3617
|
-
this.healthCheck =
|
|
3104
|
+
this.healthCheck = _seedcord_core_node_internal.HealthCheck.fromOption(this.shutdown, config.healthCheck);
|
|
3618
3105
|
this.registerStartupTasks();
|
|
3619
3106
|
}
|
|
3620
3107
|
/** The bot's discord username, populated after login. */
|
|
3621
3108
|
get username() {
|
|
3622
3109
|
return this.bot.client.user?.username;
|
|
3623
3110
|
}
|
|
3111
|
+
pluginCapabilities() {
|
|
3112
|
+
return {
|
|
3113
|
+
client: this.bot.client,
|
|
3114
|
+
token: this.bot.botToken,
|
|
3115
|
+
rest: this.bot.client.rest
|
|
3116
|
+
};
|
|
3117
|
+
}
|
|
3624
3118
|
static reset() {
|
|
3625
|
-
|
|
3119
|
+
super.reset();
|
|
3626
3120
|
_seedcord_logger.LoggerChannelRegistry.instance.reset();
|
|
3627
3121
|
}
|
|
3628
3122
|
registerStartupTasks() {
|
|
3629
3123
|
if (envapt.Envapter.isDevelopment || envapt.Envapter.isTest) this.registerHmrAwareModules();
|
|
3630
|
-
this.startup.addTask(
|
|
3124
|
+
this.startup.addTask(_seedcord_core_node_internal.StartupPhase.Configuration, "bus-initialization", async () => {
|
|
3631
3125
|
this.bus.logger.utils.initialization("Subscribers", "start");
|
|
3632
|
-
await this.
|
|
3126
|
+
await this.subscribers.init();
|
|
3633
3127
|
this.bus.logger.utils.initialization("Subscribers", "end");
|
|
3634
3128
|
});
|
|
3635
|
-
this.startup.addTask(
|
|
3129
|
+
this.startup.addTask(_seedcord_core_node_internal.StartupPhase.Login, "bot-initialization", async () => {
|
|
3636
3130
|
this.bot.logger.utils.initialization("Bot", "start");
|
|
3637
3131
|
await this.bot.init();
|
|
3638
3132
|
this.bot.logger.utils.initialization("Bot", "end");
|
|
3639
3133
|
});
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3134
|
+
const { healthCheck } = this;
|
|
3135
|
+
if (healthCheck) this.startup.addTask(_seedcord_core_node_internal.StartupPhase.Ready, "health-check", async () => {
|
|
3136
|
+
healthCheck.logger.utils.initialization("HealthCheck", "start");
|
|
3137
|
+
await healthCheck.init();
|
|
3138
|
+
healthCheck.logger.utils.initialization("HealthCheck", "end");
|
|
3644
3139
|
});
|
|
3645
3140
|
}
|
|
3646
3141
|
/**
|
|
3647
3142
|
* Starts the bot and runs all initialization tasks
|
|
3648
3143
|
*
|
|
3649
|
-
* @returns This Seedcord instance when
|
|
3144
|
+
* @returns This Seedcord instance when initialized
|
|
3650
3145
|
*/
|
|
3651
3146
|
async start() {
|
|
3652
|
-
|
|
3147
|
+
try {
|
|
3148
|
+
await super.init();
|
|
3149
|
+
} catch (caught) {
|
|
3150
|
+
await this.shutdown.run(1, false);
|
|
3151
|
+
Seedcord.reset();
|
|
3152
|
+
throw caught;
|
|
3153
|
+
}
|
|
3653
3154
|
return this;
|
|
3654
3155
|
}
|
|
3655
3156
|
registerHmrAwareModules() {
|
|
3656
|
-
this.startup.addTask(
|
|
3157
|
+
this.startup.addTask(_seedcord_core_node_internal.StartupPhase.Configuration, "hmr-registration", async () => {
|
|
3657
3158
|
this.hmrManager.register(this.bot);
|
|
3658
|
-
this.hmrManager.register(this.
|
|
3159
|
+
this.hmrManager.register(this.subscribers);
|
|
3659
3160
|
for (const plugin of this.plugins) this.hmrManager.register(plugin);
|
|
3660
3161
|
await Promise.resolve();
|
|
3661
3162
|
});
|
|
@@ -3681,29 +3182,14 @@ exports.MiddlewareType = MiddlewareType;
|
|
|
3681
3182
|
exports.ModalHandler = ModalHandler;
|
|
3682
3183
|
exports.Nsfw = Nsfw;
|
|
3683
3184
|
exports.Paginator = Paginator;
|
|
3684
|
-
exports.Pluggable = Pluggable;
|
|
3685
3185
|
exports.Plugin = Plugin;
|
|
3686
3186
|
exports.RegisterEvent = RegisterEvent;
|
|
3687
3187
|
exports.RepliableHandler = RepliableHandler;
|
|
3688
3188
|
exports.Seedcord = Seedcord;
|
|
3689
3189
|
exports.SelectMenuHandler = SelectMenuHandler;
|
|
3690
|
-
Object.defineProperty(exports, 'ShutdownPhase', {
|
|
3691
|
-
enumerable: true,
|
|
3692
|
-
get: function () {
|
|
3693
|
-
return _seedcord_core_node.ShutdownPhase;
|
|
3694
|
-
}
|
|
3695
|
-
});
|
|
3696
3190
|
exports.SlashHandler = SlashHandler;
|
|
3697
|
-
Object.defineProperty(exports, 'StartupPhase', {
|
|
3698
|
-
enumerable: true,
|
|
3699
|
-
get: function () {
|
|
3700
|
-
return _seedcord_core_node.StartupPhase;
|
|
3701
|
-
}
|
|
3702
|
-
});
|
|
3703
|
-
exports.Subscribe = Subscribe;
|
|
3704
3191
|
exports.Subscriber = Subscriber;
|
|
3705
3192
|
exports.WebhookLog = WebhookLog;
|
|
3706
|
-
exports.WebhookUrl = WebhookUrl;
|
|
3707
3193
|
exports.checkBotPermissions = checkBotPermissions;
|
|
3708
3194
|
exports.checkPermissions = checkPermissions;
|
|
3709
3195
|
exports.fetchGuildMember = fetchGuildMember;
|
|
@@ -3731,6 +3217,7 @@ Object.keys(_seedcord_errors).forEach(function (k) {
|
|
|
3731
3217
|
});
|
|
3732
3218
|
});
|
|
3733
3219
|
|
|
3220
|
+
var _seedcord_event_emitter = require("@seedcord/event-emitter");
|
|
3734
3221
|
Object.keys(_seedcord_event_emitter).forEach(function (k) {
|
|
3735
3222
|
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
3736
3223
|
enumerable: true,
|