@seedcord/gateway 0.1.0-next.4 → 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 +209 -635
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +82 -353
- package/dist/index.mjs +211 -637
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -8
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
|
-
chalk = __toESM(chalk, 1);
|
|
43
|
-
let envapt = require("envapt");
|
|
44
|
-
let discord_api_types_v10 = require("discord-api-types/v10");
|
|
45
|
-
let _seedcord_utils = require("@seedcord/utils");
|
|
46
|
-
let _discordjs_rest = require("@discordjs/rest");
|
|
41
|
+
let _seedcord_core_plugin = require("@seedcord/core/plugin");
|
|
47
42
|
let _seedcord_core_node_internal = require("@seedcord/core/node/internal");
|
|
48
43
|
let _seedcord_rate_limiter = require("@seedcord/rate-limiter");
|
|
49
44
|
let _seedcord_types_internal = require("@seedcord/types/internal");
|
|
50
|
-
let
|
|
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),
|
|
@@ -1167,7 +1135,7 @@ var BaseHandler = class extends _seedcord_core.BaseHandler {
|
|
|
1167
1135
|
*/
|
|
1168
1136
|
var InteractionMiddleware = class extends BaseHandler {
|
|
1169
1137
|
constructor(event, core) {
|
|
1170
|
-
super(event, core);
|
|
1138
|
+
super(event, core, void 0, "interactions");
|
|
1171
1139
|
}
|
|
1172
1140
|
};
|
|
1173
1141
|
|
|
@@ -1187,8 +1155,8 @@ var RepliableHandler = class extends _seedcord_core.RepliableHandler {
|
|
|
1187
1155
|
getEvent() {
|
|
1188
1156
|
return this.event;
|
|
1189
1157
|
}
|
|
1190
|
-
buildSender(event,
|
|
1191
|
-
return new ReplySender(event, routeId);
|
|
1158
|
+
buildSender(event, core, routeId) {
|
|
1159
|
+
return new ReplySender(event, routeId, core.bus);
|
|
1192
1160
|
}
|
|
1193
1161
|
};
|
|
1194
1162
|
|
|
@@ -1343,7 +1311,7 @@ var ContextMenuHandler = class extends InteractionHandler {
|
|
|
1343
1311
|
*/
|
|
1344
1312
|
var AutocompleteHandler = class extends BaseHandler {
|
|
1345
1313
|
constructor(event, core, dispatch) {
|
|
1346
|
-
super(event, core, dispatch);
|
|
1314
|
+
super(event, core, dispatch, "interactions");
|
|
1347
1315
|
}
|
|
1348
1316
|
decodedFocused;
|
|
1349
1317
|
/**
|
|
@@ -1394,7 +1362,11 @@ var AutocompleteHandler = class extends BaseHandler {
|
|
|
1394
1362
|
}
|
|
1395
1363
|
/** Send autocomplete suggestions, callback type 8. Prefer {@link match}, which restricts each field's choices to its declared type. */
|
|
1396
1364
|
async respond(choices) {
|
|
1397
|
-
|
|
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));
|
|
1398
1370
|
}
|
|
1399
1371
|
/** The firing command route, for a field whose completion differs per registered command. */
|
|
1400
1372
|
get route() {
|
|
@@ -1587,7 +1559,7 @@ var SelectMenuHandler = class extends ComponentHandler {};
|
|
|
1587
1559
|
var EventHandler = class extends BaseHandler {
|
|
1588
1560
|
firedEvent;
|
|
1589
1561
|
constructor(event, core, eventName) {
|
|
1590
|
-
super(event, core);
|
|
1562
|
+
super(event, core, void 0, "events");
|
|
1591
1563
|
this.firedEvent = eventName;
|
|
1592
1564
|
}
|
|
1593
1565
|
/**
|
|
@@ -1640,7 +1612,7 @@ var EventHandler = class extends BaseHandler {
|
|
|
1640
1612
|
var EventMiddleware = class extends BaseHandler {
|
|
1641
1613
|
firedEvent;
|
|
1642
1614
|
constructor(event, core, eventName) {
|
|
1643
|
-
super(event, core);
|
|
1615
|
+
super(event, core, void 0, "events");
|
|
1644
1616
|
this.firedEvent = eventName;
|
|
1645
1617
|
}
|
|
1646
1618
|
/**
|
|
@@ -1825,10 +1797,15 @@ var Paginator = class {
|
|
|
1825
1797
|
}
|
|
1826
1798
|
};
|
|
1827
1799
|
}
|
|
1828
|
-
/**
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
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 });
|
|
1832
1809
|
}
|
|
1833
1810
|
/** Render a page as a {@link ReplyResponse}. To post it elsewhere, add `flags: MessageFlags.IsComponentsV2`. */
|
|
1834
1811
|
async page(ctx, n) {
|
|
@@ -1888,250 +1865,32 @@ var CursorSource = class {
|
|
|
1888
1865
|
};
|
|
1889
1866
|
|
|
1890
1867
|
//#endregion
|
|
1891
|
-
//#region src/subscribers/
|
|
1892
|
-
/**
|
|
1893
|
-
* Abstract base class for handling application subscribers.
|
|
1894
|
-
*
|
|
1895
|
-
* Provides type-safe access to subscribers data and the core framework instance.
|
|
1896
|
-
* Extend this class to create custom subscriber handlers.
|
|
1897
|
-
*
|
|
1898
|
-
* @typeParam KeyOfSubscribers - The specific subscriber type this handler processes
|
|
1899
|
-
*/
|
|
1900
|
-
var Subscriber = class {
|
|
1901
|
-
data;
|
|
1902
|
-
core;
|
|
1903
|
-
logger;
|
|
1904
|
-
/**
|
|
1905
|
-
* Instantiates the subscriber with event data and core.
|
|
1906
|
-
*
|
|
1907
|
-
* @param data - The subscriber event data
|
|
1908
|
-
* @param core - The core framework instance
|
|
1909
|
-
*/
|
|
1910
|
-
constructor(data, core) {
|
|
1911
|
-
this.data = data;
|
|
1912
|
-
this.core = core;
|
|
1913
|
-
this.logger = new _seedcord_logger.Logger(this.constructor.name);
|
|
1914
|
-
}
|
|
1915
|
-
};
|
|
1916
|
-
|
|
1917
|
-
//#endregion
|
|
1918
|
-
//#region src/subscribers/decorators/Subscribe.ts
|
|
1919
|
-
/**
|
|
1920
|
-
* Registers a subscriber handler class with a specific subscriber event.
|
|
1921
|
-
*
|
|
1922
|
-
* Associates the decorated class with a subscriber event type for automatic
|
|
1923
|
-
* registration and execution when the subscriber is emitted.
|
|
1924
|
-
*
|
|
1925
|
-
* @typeParam TSubscriber - The subscriber event name to register for
|
|
1926
|
-
* @param subscriber - The subscriber event name to register for
|
|
1927
|
-
* @param options - Options to configure the subscriber handler registration.
|
|
1928
|
-
* @decorator
|
|
1929
|
-
* @example
|
|
1930
|
-
* ```typescript
|
|
1931
|
-
* \@Subscribe('userJoin')
|
|
1932
|
-
* class WelcomeHandler extends Subscriber<'userJoin'> {
|
|
1933
|
-
* async execute() {
|
|
1934
|
-
* // Handle user join event
|
|
1935
|
-
* }
|
|
1936
|
-
* }
|
|
1937
|
-
* ```
|
|
1938
|
-
* @example
|
|
1939
|
-
* ```ts
|
|
1940
|
-
* \@Subscribe('userJoin', { frequency: 'once' })
|
|
1941
|
-
* // or
|
|
1942
|
-
* \@Subscribe('userJoin')
|
|
1943
|
-
* ```
|
|
1944
|
-
*/
|
|
1945
|
-
function Subscribe(subscriber, options) {
|
|
1946
|
-
return function(constructor) {
|
|
1947
|
-
const meta = {
|
|
1948
|
-
subscriber,
|
|
1949
|
-
frequency: options?.frequency
|
|
1950
|
-
};
|
|
1951
|
-
Reflect.defineMetadata(_seedcord_core_internal.SubscribeMetadataKey, meta, constructor);
|
|
1952
|
-
};
|
|
1953
|
-
}
|
|
1954
|
-
|
|
1955
|
-
//#endregion
|
|
1956
|
-
//#region src/subscribers/decorators/WebhookUrl.ts
|
|
1868
|
+
//#region src/subscribers/index.ts
|
|
1957
1869
|
/**
|
|
1958
|
-
*
|
|
1870
|
+
* Base class for a gateway subscriber, binding `this.core` to the gateway {@link Core}.
|
|
1959
1871
|
*
|
|
1960
|
-
*
|
|
1961
|
-
* at boot.
|
|
1962
|
-
*
|
|
1963
|
-
* @param envKey - The environment variable name holding the webhook url
|
|
1964
|
-
* @decorator
|
|
1965
|
-
* @example
|
|
1966
|
-
* ```ts
|
|
1967
|
-
* \@Subscribe('memberBanned')
|
|
1968
|
-
* \@WebhookUrl('BAN_LOG_WEBHOOK_URL')
|
|
1969
|
-
* class BanLog extends WebhookLog<'memberBanned'> {
|
|
1970
|
-
* report() {
|
|
1971
|
-
* return { components: [new BanCard(this.data).component] };
|
|
1972
|
-
* }
|
|
1973
|
-
* }
|
|
1974
|
-
* ```
|
|
1872
|
+
* @typeParam KeyOfSubscribers - The subscription key this handler receives
|
|
1975
1873
|
*/
|
|
1976
|
-
|
|
1977
|
-
return function(constructor) {
|
|
1978
|
-
Reflect.defineMetadata(_seedcord_core_internal.WebhookUrlMetadataKey, envKey, constructor);
|
|
1979
|
-
};
|
|
1980
|
-
}
|
|
1981
|
-
|
|
1982
|
-
//#endregion
|
|
1983
|
-
//#region src/miscellaneous/flagsFor.ts
|
|
1984
|
-
/** @internal */
|
|
1985
|
-
function flagsFor(ephemeral) {
|
|
1986
|
-
let flags = discord_api_types_v10.MessageFlags.IsComponentsV2;
|
|
1987
|
-
if (ephemeral) flags |= discord_api_types_v10.MessageFlags.Ephemeral;
|
|
1988
|
-
return flags;
|
|
1989
|
-
}
|
|
1990
|
-
|
|
1991
|
-
//#endregion
|
|
1992
|
-
//#region src/subscribers/bases/webhookHelpers.ts
|
|
1993
|
-
const DISCORD_WEBHOOK_REGEX = new RegExp(String.raw`^https?:\/\/(?:canary\.|ptb\.)?discord(?:app)?\.com\/api\/webhooks\/\d+\/[\w$-]+$`);
|
|
1994
|
-
function isDiscordWebhookUrl(value) {
|
|
1995
|
-
return URL.canParse(value) && DISCORD_WEBHOOK_REGEX.test(value);
|
|
1996
|
-
}
|
|
1997
|
-
function jsonAttachment(name, description, data) {
|
|
1998
|
-
const content = (0, _seedcord_utils.filterCirculars)(data);
|
|
1999
|
-
return {
|
|
2000
|
-
name,
|
|
2001
|
-
description,
|
|
2002
|
-
data: Buffer.from(JSON.stringify(content, void 0, 2), "utf8")
|
|
2003
|
-
};
|
|
2004
|
-
}
|
|
2005
|
-
function neutralizeFences(text) {
|
|
2006
|
-
return text.replaceAll("```", "```");
|
|
2007
|
-
}
|
|
2008
|
-
/** An error's stack with its direct cause appended (one level), ANSI-stripped for the webhook payload. */
|
|
2009
|
-
function errorReport(error) {
|
|
2010
|
-
let report = error.stack ?? `${error.name}: ${error.message}`;
|
|
2011
|
-
if (Error.isError(error.cause)) {
|
|
2012
|
-
const cause = error.cause.stack ?? `${error.cause.name}: ${error.cause.message}`;
|
|
2013
|
-
report += `\n\nCaused by:\n${cause}`;
|
|
2014
|
-
}
|
|
2015
|
-
const stripped = neutralizeFences((0, _seedcord_utils.stripAnsi)(report));
|
|
2016
|
-
return stripped.length > 1800 ? `${stripped.slice(0, 1799)}…` : stripped;
|
|
2017
|
-
}
|
|
2018
|
-
var WebhookSeparator = class extends _seedcord_core.BuilderComponent {
|
|
2019
|
-
constructor() {
|
|
2020
|
-
super("separator");
|
|
2021
|
-
this.instance.setSpacing(discord_api_types_v10.SeparatorSpacingSize.Small).setDivider(true);
|
|
2022
|
-
}
|
|
2023
|
-
};
|
|
2024
|
-
|
|
2025
|
-
//#endregion
|
|
2026
|
-
//#region src/subscribers/bases/WebhookSender.ts
|
|
2027
|
-
const HTTP_NOT_FOUND = 404;
|
|
2028
|
-
const HTTP_UNAUTHORIZED = 401;
|
|
1874
|
+
var Subscriber = class extends _seedcord_core.Subscriber {};
|
|
2029
1875
|
/**
|
|
2030
|
-
*
|
|
1876
|
+
* Base class for a gateway subscriber that delivers its event to a Discord webhook, binding
|
|
1877
|
+
* `this.core` to the gateway {@link Core}.
|
|
2031
1878
|
*
|
|
2032
|
-
* @
|
|
1879
|
+
* @typeParam KeyOfSubscribers - The subscription key this reporter receives
|
|
2033
1880
|
*/
|
|
2034
|
-
var
|
|
2035
|
-
rest = new _discordjs_rest.REST();
|
|
2036
|
-
route;
|
|
2037
|
-
constructor(url) {
|
|
2038
|
-
const match = /\/webhooks\/(\d+)\/([\w$-]+)$/.exec(url);
|
|
2039
|
-
if (!match?.[1] || !match[2]) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.ConfigWebhookUrlInvalid, ["The url"]);
|
|
2040
|
-
this.route = discord_api_types_v10.Routes.webhook(match[1], match[2]);
|
|
2041
|
-
}
|
|
2042
|
-
/**
|
|
2043
|
-
* Fetches the webhook object, sending no message. Returns `missing` on a 404 or 401 from
|
|
2044
|
-
* Discord and `unreachable` on any other failure.
|
|
2045
|
-
*/
|
|
2046
|
-
async verify() {
|
|
2047
|
-
try {
|
|
2048
|
-
await this.rest.get(this.route, { auth: false });
|
|
2049
|
-
return "ok";
|
|
2050
|
-
} catch (error) {
|
|
2051
|
-
return error instanceof _discordjs_rest.DiscordAPIError && (error.status === HTTP_NOT_FOUND || error.status === HTTP_UNAUTHORIZED) ? "missing" : "unreachable";
|
|
2052
|
-
}
|
|
2053
|
-
}
|
|
2054
|
-
async send(options) {
|
|
2055
|
-
const { flags, username, components, files } = options;
|
|
2056
|
-
await this.rest.post(this.route, {
|
|
2057
|
-
auth: false,
|
|
2058
|
-
query: new URLSearchParams({
|
|
2059
|
-
with_components: "true",
|
|
2060
|
-
wait: "true"
|
|
2061
|
-
}),
|
|
2062
|
-
body: {
|
|
2063
|
-
flags,
|
|
2064
|
-
username,
|
|
2065
|
-
components: components.map((component) => component.toJSON()),
|
|
2066
|
-
...files?.length && { attachments: files.map((file, id) => ({
|
|
2067
|
-
id,
|
|
2068
|
-
filename: file.name,
|
|
2069
|
-
description: file.description
|
|
2070
|
-
})) }
|
|
2071
|
-
},
|
|
2072
|
-
...files?.length && { files: files.map((file) => ({
|
|
2073
|
-
name: file.name,
|
|
2074
|
-
data: file.data
|
|
2075
|
-
})) }
|
|
2076
|
-
});
|
|
2077
|
-
}
|
|
2078
|
-
};
|
|
1881
|
+
var WebhookLog = class extends _seedcord_core.WebhookLog {};
|
|
2079
1882
|
|
|
2080
1883
|
//#endregion
|
|
2081
|
-
//#region src/
|
|
1884
|
+
//#region src/plugin.ts
|
|
2082
1885
|
/**
|
|
2083
|
-
* Base class for
|
|
1886
|
+
* Base class for a gateway plugin, binding `this.core` to the gateway {@link Core}.
|
|
2084
1887
|
*
|
|
2085
|
-
*
|
|
2086
|
-
*
|
|
2087
|
-
* the reporter is skipped at registration with a boot warning.
|
|
1888
|
+
* A plugin that runs on either transport extends the base from `@seedcord/core/plugin`, whose
|
|
1889
|
+
* `this.core` carries the shared members only.
|
|
2088
1890
|
*
|
|
2089
|
-
* @typeParam
|
|
1891
|
+
* @typeParam Opts - The transport, runtime, and capabilities the plugin declares.
|
|
2090
1892
|
*/
|
|
2091
|
-
var
|
|
2092
|
-
static senders = /* @__PURE__ */ new Map();
|
|
2093
|
-
async execute() {
|
|
2094
|
-
const url = WebhookLog.urlOf(WebhookLog.envKeyOf(this.constructor));
|
|
2095
|
-
if (url === null) {
|
|
2096
|
-
this.logger.warn(`${chalk.default.bold(this.constructor.name)} has no webhook url set, this reporter is disabled`);
|
|
2097
|
-
return;
|
|
2098
|
-
}
|
|
2099
|
-
const { username, components, files } = await this.report();
|
|
2100
|
-
try {
|
|
2101
|
-
await WebhookLog.senderFor(url).send({
|
|
2102
|
-
flags: flagsFor(false),
|
|
2103
|
-
username: username ?? this.constructor.name,
|
|
2104
|
-
components,
|
|
2105
|
-
files
|
|
2106
|
-
});
|
|
2107
|
-
} catch (error) {
|
|
2108
|
-
this.logger.error("Failed to send webhook report", error);
|
|
2109
|
-
}
|
|
2110
|
-
}
|
|
2111
|
-
/** @internal */
|
|
2112
|
-
static senderFor(url) {
|
|
2113
|
-
let sender = WebhookLog.senders.get(url);
|
|
2114
|
-
if (!sender) {
|
|
2115
|
-
sender = new WebhookSender(url);
|
|
2116
|
-
WebhookLog.senders.set(url, sender);
|
|
2117
|
-
}
|
|
2118
|
-
return sender;
|
|
2119
|
-
}
|
|
2120
|
-
/** @internal */
|
|
2121
|
-
static envKeyOf(ctor) {
|
|
2122
|
-
const envKey = Reflect.getMetadata(_seedcord_core_internal.WebhookUrlMetadataKey, ctor);
|
|
2123
|
-
if (!envKey) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.DecoratorWebhookUrlMissing, [ctor.name]);
|
|
2124
|
-
return envKey;
|
|
2125
|
-
}
|
|
2126
|
-
/** @internal Returns null when the env var is unset, throws when set but malformed. */
|
|
2127
|
-
static urlOf(envKey) {
|
|
2128
|
-
if (!envapt.Envapter.has(envKey)) return null;
|
|
2129
|
-
const raw = envapt.Envapter.getRequired(envKey, envapt.Converters.String).trim();
|
|
2130
|
-
if (raw === "") return null;
|
|
2131
|
-
if (!isDiscordWebhookUrl(raw)) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.ConfigWebhookUrlInvalid, [envKey]);
|
|
2132
|
-
return raw;
|
|
2133
|
-
}
|
|
2134
|
-
};
|
|
1893
|
+
var Plugin = class extends _seedcord_core_plugin.Plugin {};
|
|
2135
1894
|
|
|
2136
1895
|
//#endregion
|
|
2137
1896
|
//#region src/bot/utilities/miscellaneous/contextMenuLeaves.ts
|
|
@@ -2187,7 +1946,7 @@ function slashRouteLeaves(commands) {
|
|
|
2187
1946
|
var CommandRegistry = class {
|
|
2188
1947
|
core;
|
|
2189
1948
|
onDeployed;
|
|
2190
|
-
logger = new _seedcord_logger.Logger("Commands");
|
|
1949
|
+
logger = new _seedcord_logger.Logger("Commands", { channel: "commands" });
|
|
2191
1950
|
isInitialised = false;
|
|
2192
1951
|
globalCommands = [];
|
|
2193
1952
|
guildCommands = new discord_js.Collection();
|
|
@@ -2274,7 +2033,7 @@ var CommandRegistry = class {
|
|
|
2274
2033
|
this.registerCommand(exported, rel);
|
|
2275
2034
|
this.hmrHandler?.trackHandler(fullPath, exported);
|
|
2276
2035
|
}
|
|
2277
|
-
}
|
|
2036
|
+
});
|
|
2278
2037
|
}
|
|
2279
2038
|
isCommandClass(obj) {
|
|
2280
2039
|
if (typeof obj !== "function") return false;
|
|
@@ -2436,7 +2195,7 @@ function eventGateContext(eventName, args, core) {
|
|
|
2436
2195
|
|
|
2437
2196
|
//#endregion
|
|
2438
2197
|
//#region src/bot/handleEventFault.ts
|
|
2439
|
-
const logger$1 = new _seedcord_logger.Logger("EventBoundary");
|
|
2198
|
+
const logger$1 = new _seedcord_logger.Logger("EventBoundary", { channel: "errors" });
|
|
2440
2199
|
/**
|
|
2441
2200
|
* The event controller boundary. Reports a throw from an event handler and never replies, since a
|
|
2442
2201
|
* generic event has no reply target. A {@link Silence} or a non-reporting {@link Notice} stops quietly, a
|
|
@@ -2449,15 +2208,15 @@ function handleEventFault(caught, eventName, handlerName, args, core) {
|
|
|
2449
2208
|
if (caught.reason !== void 0 && (core.config.errors?.logSilences ?? true)) logger$1.debug(`Silence: ${caught.reason}`);
|
|
2450
2209
|
return;
|
|
2451
2210
|
}
|
|
2452
|
-
|
|
2211
|
+
const error = (0, _seedcord_core_internal.asError)(caught);
|
|
2453
2212
|
const ignore = new Set(core.config.errors?.ignoreEventApiCodes ?? []);
|
|
2454
|
-
if (
|
|
2455
|
-
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}`);
|
|
2456
2215
|
return;
|
|
2457
2216
|
}
|
|
2458
|
-
if (
|
|
2217
|
+
if (error instanceof _seedcord_core.Notice && !error.report) return;
|
|
2459
2218
|
const actor = deriveEventActor(args);
|
|
2460
|
-
extractErrorResponse(
|
|
2219
|
+
extractErrorResponse(error, core, {
|
|
2461
2220
|
event: {
|
|
2462
2221
|
name: eventName,
|
|
2463
2222
|
handler: handlerName,
|
|
@@ -2472,15 +2231,15 @@ function handleEventFault(caught, eventName, handlerName, args, core) {
|
|
|
2472
2231
|
//#endregion
|
|
2473
2232
|
//#region src/bot/controllers/EventDispatcher.ts
|
|
2474
2233
|
/**
|
|
2475
|
-
* Scans event handler directories,
|
|
2476
|
-
*
|
|
2477
|
-
*
|
|
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.
|
|
2478
2237
|
*
|
|
2479
2238
|
* @internal
|
|
2480
2239
|
*/
|
|
2481
2240
|
var EventDispatcher = class {
|
|
2482
2241
|
core;
|
|
2483
|
-
logger = new _seedcord_logger.Logger("Events");
|
|
2242
|
+
logger = new _seedcord_logger.Logger("Events", { channel: "events" });
|
|
2484
2243
|
isInitialized = false;
|
|
2485
2244
|
eventMap = /* @__PURE__ */ new Map();
|
|
2486
2245
|
middlewares = [];
|
|
@@ -2551,7 +2310,7 @@ var EventDispatcher = class {
|
|
|
2551
2310
|
this.registerHandler(val, relativePath);
|
|
2552
2311
|
this.hmrHandler?.trackHandler(fullPath, val);
|
|
2553
2312
|
}
|
|
2554
|
-
}
|
|
2313
|
+
});
|
|
2555
2314
|
}
|
|
2556
2315
|
async loadMiddlewares(dir) {
|
|
2557
2316
|
await (0, _seedcord_utils_node.traverseDirectory)(dir, (fullPath, relativePath, imported) => {
|
|
@@ -2560,7 +2319,7 @@ var EventDispatcher = class {
|
|
|
2560
2319
|
this.registerMiddleware(val, relativePath);
|
|
2561
2320
|
this.hmrHandler?.trackMiddleware(fullPath, val);
|
|
2562
2321
|
}
|
|
2563
|
-
}
|
|
2322
|
+
});
|
|
2564
2323
|
}
|
|
2565
2324
|
/** @internal For use in dev mode */
|
|
2566
2325
|
async onHmr(event) {
|
|
@@ -2657,10 +2416,14 @@ var EventDispatcher = class {
|
|
|
2657
2416
|
this.logger.debug(`Attaching ${_seedcord_logger.paint.mint.bold(eventName)} to the client with ${_seedcord_logger.paint.mute(handlerEntries?.length ?? 0)} handler(s)`);
|
|
2658
2417
|
this.core.bot.client.on(eventName, (...args) => {
|
|
2659
2418
|
if (this.draining) return;
|
|
2660
|
-
this.core.
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
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 });
|
|
2664
2427
|
});
|
|
2665
2428
|
this.inFlight.add(run);
|
|
2666
2429
|
run.finally(() => this.inFlight.delete(run));
|
|
@@ -2736,7 +2499,7 @@ const HARMLESS_API_CODES = new Set([
|
|
|
2736
2499
|
|
|
2737
2500
|
//#endregion
|
|
2738
2501
|
//#region src/bot/handleInteractionFault.ts
|
|
2739
|
-
const logger = new _seedcord_logger.Logger("InteractionBoundary");
|
|
2502
|
+
const logger = new _seedcord_logger.Logger("InteractionBoundary", { channel: "errors" });
|
|
2740
2503
|
/**
|
|
2741
2504
|
* The interaction controller boundary. Sorts a throw from any handler-lifecycle phase into the right
|
|
2742
2505
|
* user reply plus the right bus event. When a handler is built its sender is passed in, so the boundary
|
|
@@ -2745,33 +2508,45 @@ const logger = new _seedcord_logger.Logger("InteractionBoundary");
|
|
|
2745
2508
|
*
|
|
2746
2509
|
* @internal
|
|
2747
2510
|
*/
|
|
2748
|
-
async function handleInteractionFault(caught, interaction, core, sender) {
|
|
2511
|
+
async function handleInteractionFault(caught, interaction, core, routeId, sender) {
|
|
2749
2512
|
if (caught instanceof _seedcord_core.Silence) {
|
|
2750
2513
|
if (caught.reason !== void 0 && (core.config.errors?.logSilences ?? true)) logger.debug(`Silence: ${caught.reason}`);
|
|
2751
2514
|
return;
|
|
2752
2515
|
}
|
|
2753
|
-
|
|
2516
|
+
const error = (0, _seedcord_core_internal.asError)(caught);
|
|
2754
2517
|
const ignore = new Set(core.config.errors?.ignoreApiCodes ?? []);
|
|
2755
|
-
if (
|
|
2756
|
-
logger.debug(`swallowed api code ${
|
|
2518
|
+
if (error instanceof discord_js.DiscordAPIError && ignore.has(error.code)) {
|
|
2519
|
+
logger.debug(`swallowed api code ${error.code}`);
|
|
2757
2520
|
return;
|
|
2758
2521
|
}
|
|
2759
2522
|
if (interaction.isAutocomplete()) {
|
|
2760
|
-
extractErrorResponse(
|
|
2523
|
+
extractErrorResponse(error, core, {
|
|
2761
2524
|
guild: interaction.guild,
|
|
2762
2525
|
user: interaction.user,
|
|
2763
2526
|
metadata: interaction,
|
|
2764
2527
|
route: slashRouteOf(interaction)
|
|
2765
2528
|
});
|
|
2529
|
+
await sendEmptyChoices(interaction, core, routeId);
|
|
2766
2530
|
return;
|
|
2767
2531
|
}
|
|
2768
|
-
const { response } = extractErrorResponse(
|
|
2532
|
+
const { response } = extractErrorResponse(error, core, {
|
|
2769
2533
|
interaction,
|
|
2770
2534
|
guild: interaction.guild,
|
|
2771
2535
|
user: interaction.user,
|
|
2772
2536
|
metadata: interaction
|
|
2773
2537
|
});
|
|
2774
|
-
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
|
+
}
|
|
2775
2550
|
}
|
|
2776
2551
|
async function sendGuarded(sender, response, ephemeral) {
|
|
2777
2552
|
try {
|
|
@@ -2795,7 +2570,7 @@ async function sendGuarded(sender, response, ephemeral) {
|
|
|
2795
2570
|
*/
|
|
2796
2571
|
var InteractionDispatcher = class {
|
|
2797
2572
|
core;
|
|
2798
|
-
logger = new _seedcord_logger.Logger("Interactions");
|
|
2573
|
+
logger = new _seedcord_logger.Logger("Interactions", { channel: "interactions" });
|
|
2799
2574
|
isInitialized = false;
|
|
2800
2575
|
slashMap = /* @__PURE__ */ new Map();
|
|
2801
2576
|
buttonMap = /* @__PURE__ */ new Map();
|
|
@@ -2808,6 +2583,7 @@ var InteractionDispatcher = class {
|
|
|
2808
2583
|
messageContextMenuMap = /* @__PURE__ */ new Map();
|
|
2809
2584
|
userContextMenuMap = /* @__PURE__ */ new Map();
|
|
2810
2585
|
autocompleteMap = /* @__PURE__ */ new Map();
|
|
2586
|
+
handlerFiles = /* @__PURE__ */ new Map();
|
|
2811
2587
|
keysToIgnore = /* @__PURE__ */ new Set();
|
|
2812
2588
|
middlewares = [];
|
|
2813
2589
|
inFlight = /* @__PURE__ */ new Set();
|
|
@@ -2926,7 +2702,7 @@ var InteractionDispatcher = class {
|
|
|
2926
2702
|
this.registerHandler(val, relativePath);
|
|
2927
2703
|
this.hmrHandler?.trackHandler(fullPath, val);
|
|
2928
2704
|
}
|
|
2929
|
-
}
|
|
2705
|
+
});
|
|
2930
2706
|
}
|
|
2931
2707
|
async loadMiddlewares(dir) {
|
|
2932
2708
|
await (0, _seedcord_utils_node.traverseDirectory)(dir, (fullPath, relativePath, imported) => {
|
|
@@ -2935,7 +2711,7 @@ var InteractionDispatcher = class {
|
|
|
2935
2711
|
this.registerMiddleware(val, relativePath);
|
|
2936
2712
|
this.hmrHandler?.trackMiddleware(fullPath, val);
|
|
2937
2713
|
}
|
|
2938
|
-
}
|
|
2714
|
+
});
|
|
2939
2715
|
}
|
|
2940
2716
|
registerMiddleware(middlewareCtor, relativePath) {
|
|
2941
2717
|
const metadata = Reflect.getMetadata(_seedcord_core_internal.MiddlewareMetadataKey, middlewareCtor);
|
|
@@ -2965,6 +2741,7 @@ var InteractionDispatcher = class {
|
|
|
2965
2741
|
return obj.prototype instanceof InteractionMiddleware && Reflect.hasMetadata(_seedcord_core_internal.MiddlewareMetadataKey, obj);
|
|
2966
2742
|
}
|
|
2967
2743
|
registerHandler(handlerClass, relativePath) {
|
|
2744
|
+
const from = (0, _seedcord_utils.formatFilePath)(relativePath);
|
|
2968
2745
|
const writes = [];
|
|
2969
2746
|
for (const [routeType, map] of this.routeTypes) {
|
|
2970
2747
|
const meta = Reflect.getMetadata(_seedcord_core_internal.InteractionRouteKeys[routeType], handlerClass);
|
|
@@ -2972,16 +2749,16 @@ var InteractionDispatcher = class {
|
|
|
2972
2749
|
for (const route of meta) {
|
|
2973
2750
|
const existing = map.get(route);
|
|
2974
2751
|
if (existing && existing !== handlerClass) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.InteractionDuplicateRoute, [
|
|
2975
|
-
route
|
|
2976
|
-
existing.name
|
|
2977
|
-
handlerClass.name
|
|
2752
|
+
`${routeType}:${route}`,
|
|
2753
|
+
`${existing.name} (${this.handlerFiles.get(existing) ?? "unknown file"})`,
|
|
2754
|
+
`${handlerClass.name} (${from})`
|
|
2978
2755
|
]);
|
|
2979
2756
|
writes.push([map, route]);
|
|
2980
2757
|
}
|
|
2981
2758
|
}
|
|
2982
2759
|
if (writes.length === 0) return;
|
|
2983
2760
|
for (const [map, route] of writes) map.set(route, handlerClass);
|
|
2984
|
-
|
|
2761
|
+
this.handlerFiles.set(handlerClass, from);
|
|
2985
2762
|
if (this.loading) this.loadedHandlers.push({
|
|
2986
2763
|
name: handlerClass.name,
|
|
2987
2764
|
from
|
|
@@ -2993,6 +2770,7 @@ var InteractionDispatcher = class {
|
|
|
2993
2770
|
await this.hmrHandler?.handle(event);
|
|
2994
2771
|
}
|
|
2995
2772
|
unregisterHandler(handlerClass, artifacts) {
|
|
2773
|
+
this.handlerFiles.delete(handlerClass);
|
|
2996
2774
|
if (artifacts) {
|
|
2997
2775
|
for (const { routeType, routes } of artifacts) {
|
|
2998
2776
|
const map = this.routeTypes.find(([type]) => type === routeType)?.[1];
|
|
@@ -3013,10 +2791,11 @@ var InteractionDispatcher = class {
|
|
|
3013
2791
|
attachToClient() {
|
|
3014
2792
|
this.core.bot.client.on(discord_js.Events.InteractionCreate, (interaction) => {
|
|
3015
2793
|
if (this.draining) return;
|
|
3016
|
-
this.core.
|
|
3017
|
-
const run = this.handleInteraction(interaction).catch((
|
|
3018
|
-
|
|
3019
|
-
this.
|
|
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 });
|
|
3020
2799
|
});
|
|
3021
2800
|
this.inFlight.add(run);
|
|
3022
2801
|
run.finally(() => this.inFlight.delete(run));
|
|
@@ -3028,37 +2807,75 @@ var InteractionDispatcher = class {
|
|
|
3028
2807
|
drain(timeoutMs) {
|
|
3029
2808
|
return (0, _seedcord_core_node_internal.settleWithin)(Promise.allSettled(this.inFlight), timeoutMs);
|
|
3030
2809
|
}
|
|
3031
|
-
async handleCustomIdInteraction(interaction,
|
|
2810
|
+
async handleCustomIdInteraction(interaction, kind, getMap) {
|
|
3032
2811
|
if ([...this.keysToIgnore].some((matcher) => matcher.owns(interaction.customId))) return;
|
|
3033
2812
|
const prefix = (0, _seedcord_core_internal.prefixOf)(interaction.customId);
|
|
3034
|
-
if (!prefix)
|
|
3035
|
-
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));
|
|
3036
2815
|
}
|
|
3037
|
-
async processInteraction(interaction, extractKey, getHandler, fallback = UnhandledRepliable) {
|
|
2816
|
+
async processInteraction(interaction, kind, extractKey, getHandler, fallback = UnhandledRepliable) {
|
|
3038
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
|
+
};
|
|
3039
2836
|
let sender;
|
|
3040
2837
|
try {
|
|
3041
|
-
if (!interaction.isAutocomplete())
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
}
|
|
3047
|
-
this.logger.debug(`Processing ${_seedcord_logger.paint.mint.bold(key)} with ${_seedcord_logger.paint.mute(HandlerCtor.name)}`);
|
|
3048
|
-
const dispatch = new _seedcord_core.DispatchContext((0, _seedcord_core_internal.routeIdOf)(HandlerCtor));
|
|
3049
|
-
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);
|
|
3050
2843
|
if (handler instanceof RepliableHandler) sender = handler.getSender();
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
} finally {
|
|
3056
|
-
monitor?.report(dispatch.routeId);
|
|
3057
|
-
}
|
|
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;
|
|
3058
2848
|
}
|
|
3059
2849
|
await handler.execute();
|
|
2850
|
+
report("handled");
|
|
3060
2851
|
} catch (caught) {
|
|
3061
|
-
await
|
|
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;
|
|
2875
|
+
} catch (caught) {
|
|
2876
|
+
return { caught };
|
|
2877
|
+
} finally {
|
|
2878
|
+
monitor?.report(routeId);
|
|
3062
2879
|
}
|
|
3063
2880
|
}
|
|
3064
2881
|
async handleInteraction(interaction) {
|
|
@@ -3103,47 +2920,41 @@ var InteractionDispatcher = class {
|
|
|
3103
2920
|
}
|
|
3104
2921
|
async handleSlashCommand(interaction) {
|
|
3105
2922
|
const route = slashRouteOf(interaction);
|
|
3106
|
-
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));
|
|
3107
2924
|
}
|
|
3108
2925
|
async handleButton(interaction) {
|
|
3109
|
-
await this.handleCustomIdInteraction(interaction, () => this.buttonMap
|
|
2926
|
+
await this.handleCustomIdInteraction(interaction, _seedcord_core_internal.InteractionRoutes.Button, () => this.buttonMap);
|
|
3110
2927
|
}
|
|
3111
2928
|
async handleModal(interaction) {
|
|
3112
|
-
await this.handleCustomIdInteraction(interaction, () => this.modalMap
|
|
2929
|
+
await this.handleCustomIdInteraction(interaction, _seedcord_core_internal.InteractionRoutes.Modal, () => this.modalMap);
|
|
3113
2930
|
}
|
|
3114
2931
|
async handleStringSelectMenu(interaction) {
|
|
3115
|
-
await this.handleCustomIdInteraction(interaction, () => this.stringSelectMap
|
|
2932
|
+
await this.handleCustomIdInteraction(interaction, _seedcord_core_internal.InteractionRoutes.StringMenu, () => this.stringSelectMap);
|
|
3116
2933
|
}
|
|
3117
2934
|
async handleUserSelectMenu(interaction) {
|
|
3118
|
-
await this.handleCustomIdInteraction(interaction, () => this.userSelectMap
|
|
2935
|
+
await this.handleCustomIdInteraction(interaction, _seedcord_core_internal.InteractionRoutes.UserMenu, () => this.userSelectMap);
|
|
3119
2936
|
}
|
|
3120
2937
|
async handleRoleSelectMenu(interaction) {
|
|
3121
|
-
await this.handleCustomIdInteraction(interaction, () => this.roleSelectMap
|
|
2938
|
+
await this.handleCustomIdInteraction(interaction, _seedcord_core_internal.InteractionRoutes.RoleMenu, () => this.roleSelectMap);
|
|
3122
2939
|
}
|
|
3123
2940
|
async handleChannelSelectMenu(interaction) {
|
|
3124
|
-
await this.handleCustomIdInteraction(interaction, () => this.channelSelectMap
|
|
2941
|
+
await this.handleCustomIdInteraction(interaction, _seedcord_core_internal.InteractionRoutes.ChannelMenu, () => this.channelSelectMap);
|
|
3125
2942
|
}
|
|
3126
2943
|
async handleMentionableSelectMenu(interaction) {
|
|
3127
|
-
await this.handleCustomIdInteraction(interaction, () => this.mentionableSelectMap
|
|
2944
|
+
await this.handleCustomIdInteraction(interaction, _seedcord_core_internal.InteractionRoutes.MentionableMenu, () => this.mentionableSelectMap);
|
|
3128
2945
|
}
|
|
3129
2946
|
async handleMessageContextMenu(interaction) {
|
|
3130
|
-
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));
|
|
3131
2948
|
}
|
|
3132
2949
|
async handleUserContextMenu(interaction) {
|
|
3133
|
-
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));
|
|
3134
2951
|
}
|
|
3135
2952
|
async handleAutocomplete(interaction) {
|
|
3136
2953
|
const route = slashRouteOf(interaction);
|
|
3137
|
-
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);
|
|
3138
2955
|
}
|
|
3139
2956
|
};
|
|
3140
2957
|
|
|
3141
|
-
//#endregion
|
|
3142
|
-
//#region \0@oxc-project+runtime@0.135.0/helpers/esm/decorateMetadata.js
|
|
3143
|
-
function __decorateMetadata(k, v) {
|
|
3144
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
3145
|
-
}
|
|
3146
|
-
|
|
3147
2958
|
//#endregion
|
|
3148
2959
|
//#region \0@oxc-project+runtime@0.135.0/helpers/esm/decorate.js
|
|
3149
2960
|
function __decorate(decorators, target, key, desc) {
|
|
@@ -3162,9 +2973,9 @@ const LOGOUT_TIMEOUT_MS = 2e3;
|
|
|
3162
2973
|
/**
|
|
3163
2974
|
* The Discord client and its controllers. Access it through `core.bot`.
|
|
3164
2975
|
*/
|
|
3165
|
-
var Bot = class
|
|
2976
|
+
var Bot = class {
|
|
3166
2977
|
/** @internal */
|
|
3167
|
-
logger = new _seedcord_logger.Logger("Bot");
|
|
2978
|
+
logger = new _seedcord_logger.Logger("Bot", { channel: "bot" });
|
|
3168
2979
|
isInitialized = false;
|
|
3169
2980
|
_client;
|
|
3170
2981
|
interactions;
|
|
@@ -3181,7 +2992,6 @@ var Bot = class extends _seedcord_event_emitter.TypedEventEmitter {
|
|
|
3181
2992
|
}
|
|
3182
2993
|
/** @internal */
|
|
3183
2994
|
constructor(core) {
|
|
3184
|
-
super();
|
|
3185
2995
|
this._client = new discord_js.Client(core.config.bot.clientOptions);
|
|
3186
2996
|
if (core.config.bot.interactions.path) this.interactions = new InteractionDispatcher(core);
|
|
3187
2997
|
if (core.config.bot.events.path) this.events = new EventDispatcher(core);
|
|
@@ -3231,9 +3041,10 @@ var Bot = class extends _seedcord_event_emitter.TypedEventEmitter {
|
|
|
3231
3041
|
await this.logout();
|
|
3232
3042
|
}
|
|
3233
3043
|
async login(token) {
|
|
3234
|
-
|
|
3044
|
+
const ready = Promise.withResolvers();
|
|
3045
|
+
this._client.once(discord_js.Events.ClientReady, () => ready.resolve());
|
|
3235
3046
|
this._client.login(token);
|
|
3236
|
-
await
|
|
3047
|
+
await ready.promise;
|
|
3237
3048
|
this.logger.info(`Logged in as ${chalk.default.bold.magenta(this._client.user?.username)}!`);
|
|
3238
3049
|
return this;
|
|
3239
3050
|
}
|
|
@@ -3244,244 +3055,13 @@ var Bot = class extends _seedcord_event_emitter.TypedEventEmitter {
|
|
|
3244
3055
|
get client() {
|
|
3245
3056
|
return this._client;
|
|
3246
3057
|
}
|
|
3247
|
-
/**
|
|
3248
|
-
* The overloads enforce the key/args correlation at compile time only. At runtime this forwards
|
|
3249
|
-
* straight to the underlying TypedEventEmitter.
|
|
3250
|
-
*
|
|
3251
|
-
* @internal
|
|
3252
|
-
*/
|
|
3253
|
-
emit(event, ...args) {
|
|
3254
|
-
return super.emit(event, ...args);
|
|
3255
|
-
}
|
|
3256
|
-
emitSafe(event, ...args) {
|
|
3257
|
-
return super.emitSafe(event, ...args);
|
|
3258
|
-
}
|
|
3259
|
-
onListenerError(error, event) {
|
|
3260
|
-
this.logger.error(`listener for ${String(event)} threw`, error);
|
|
3261
|
-
}
|
|
3262
|
-
};
|
|
3263
|
-
__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);
|
|
3264
|
-
|
|
3265
|
-
//#endregion
|
|
3266
|
-
//#region src/subscribers/default/HandledException.ts
|
|
3267
|
-
let HandledException = class HandledException extends WebhookLog {
|
|
3268
|
-
report() {
|
|
3269
|
-
return {
|
|
3270
|
-
username: "Handled Exception",
|
|
3271
|
-
components: [new HandledExceptionContainer(this.data).component],
|
|
3272
|
-
files: [jsonAttachment("source.json", "The raw source the fault came from", this.data.source.raw)]
|
|
3273
|
-
};
|
|
3274
|
-
}
|
|
3275
|
-
};
|
|
3276
|
-
HandledException = __decorate([Subscribe("handledException"), WebhookUrl("HANDLED_EXCEPTION_WEBHOOK_URL")], HandledException);
|
|
3277
|
-
var HandledExceptionContainer = class extends _seedcord_core.BuilderComponent {
|
|
3278
|
-
constructor(data) {
|
|
3279
|
-
super("container");
|
|
3280
|
-
const { denial, uuid, source } = data;
|
|
3281
|
-
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"));
|
|
3282
|
-
}
|
|
3283
|
-
};
|
|
3284
|
-
function faultSummary(denial, source) {
|
|
3285
|
-
const head = (0, _seedcord_utils.stripAnsi)(`### A handled fault was reported: \`${denial.name}\`\n**Message:** ${denial.message}\n`);
|
|
3286
|
-
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"}\``;
|
|
3287
|
-
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}\``;
|
|
3288
|
-
}
|
|
3289
|
-
function causeStack(denial) {
|
|
3290
|
-
const { cause } = denial;
|
|
3291
|
-
if (Error.isError(cause)) return (0, _seedcord_utils.stripAnsi)(cause.stack ?? cause.message);
|
|
3292
|
-
if (typeof cause === "string") return (0, _seedcord_utils.stripAnsi)(cause);
|
|
3293
|
-
if (typeof cause === "bigint" || typeof cause === "symbol" || typeof cause === "function") return cause.toString();
|
|
3294
|
-
if (cause !== void 0) try {
|
|
3295
|
-
return JSON.stringify(cause);
|
|
3296
|
-
} catch {
|
|
3297
|
-
return "[unserializable cause]";
|
|
3298
|
-
}
|
|
3299
|
-
return (0, _seedcord_utils.stripAnsi)(denial.stack ?? "No cause recorded.");
|
|
3300
|
-
}
|
|
3301
|
-
|
|
3302
|
-
//#endregion
|
|
3303
|
-
//#region src/subscribers/default/UnknownException.ts
|
|
3304
|
-
let UnknownException = class UnknownException extends WebhookLog {
|
|
3305
|
-
report() {
|
|
3306
|
-
const { metadata } = this.data;
|
|
3307
|
-
return {
|
|
3308
|
-
username: "Unknown Exception",
|
|
3309
|
-
components: [new UnhandledErrorContainer(this.data).component],
|
|
3310
|
-
files: metadata ? [jsonAttachment("metadata.json", "Metadata associated with the error", metadata)] : void 0
|
|
3311
|
-
};
|
|
3312
|
-
}
|
|
3313
|
-
};
|
|
3314
|
-
UnknownException = __decorate([Subscribe("unknownException"), WebhookUrl("UNKNOWN_EXCEPTION_WEBHOOK_URL")], UnknownException);
|
|
3315
|
-
var UnhandledErrorContainer = class extends _seedcord_core.BuilderComponent {
|
|
3316
|
-
constructor(data) {
|
|
3317
|
-
super("container");
|
|
3318
|
-
const { uuid, error, guild, user, metadata } = data;
|
|
3319
|
-
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)}\`\`\``));
|
|
3320
|
-
this.addTimestampsIfAvailable(error);
|
|
3321
|
-
this.addMetadataIfAvailable(metadata);
|
|
3322
|
-
}
|
|
3323
|
-
addTimestampsIfAvailable(error) {
|
|
3324
|
-
if (!(error instanceof _discordjs_rest.DiscordAPIError)) return;
|
|
3325
|
-
const now = Date.now();
|
|
3326
|
-
const snowflake = error.url.match(/\/interactions\/(\d+)\//)?.[1];
|
|
3327
|
-
if (!snowflake) return void 0;
|
|
3328
|
-
const interactionTs = (0, _seedcord_utils.timestampFromSnowflake)(snowflake);
|
|
3329
|
-
const diff = now - interactionTs;
|
|
3330
|
-
const seconds = Math.floor(diff / 1e3);
|
|
3331
|
-
const millis = diff % 1e3;
|
|
3332
|
-
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`));
|
|
3333
|
-
}
|
|
3334
|
-
addMetadataIfAvailable(metadata) {
|
|
3335
|
-
if (!metadata) return;
|
|
3336
|
-
this.instance.addSeparatorComponents(new WebhookSeparator().component).addTextDisplayComponents((text) => text.setContent("### Metadata")).addFileComponents((file) => file.setURL("attachment://metadata.json"));
|
|
3337
|
-
}
|
|
3338
|
-
};
|
|
3339
|
-
|
|
3340
|
-
//#endregion
|
|
3341
|
-
//#region src/subscribers/Bus.ts
|
|
3342
|
-
/**
|
|
3343
|
-
* Registers and dispatches subscribers. Subscribers load from the configured directories and run
|
|
3344
|
-
* on a programmatic or framework-event publish. Accessed via `core.bus`. Do not construct it directly.
|
|
3345
|
-
*/
|
|
3346
|
-
var Bus = class extends _seedcord_event_emitter.TypedEventEmitter {
|
|
3347
|
-
core;
|
|
3348
|
-
logger = new _seedcord_logger.Logger("Subscribers");
|
|
3349
|
-
isInitialized = false;
|
|
3350
|
-
subscribersMap = /* @__PURE__ */ new Map();
|
|
3351
|
-
executedOnceHandlers = /* @__PURE__ */ new Set();
|
|
3352
|
-
webhookProbes = /* @__PURE__ */ new Map();
|
|
3353
|
-
hmrHandler;
|
|
3354
|
-
constructor(core) {
|
|
3355
|
-
super();
|
|
3356
|
-
this.core = core;
|
|
3357
|
-
if (this.core.config.subscribers.path) {
|
|
3358
|
-
if (!envapt.Envapter.isDevelopment && !envapt.Envapter.isTest) return;
|
|
3359
|
-
this.hmrHandler = new _seedcord_core_hmr.HmrModuleHandler({
|
|
3360
|
-
handlersDir: this.core.config.subscribers.path,
|
|
3361
|
-
isHandler: this.isSubscriber.bind(this),
|
|
3362
|
-
registerHandler: this.registerSubscriber.bind(this),
|
|
3363
|
-
unregisterHandler: this.unregisterSubscriber.bind(this),
|
|
3364
|
-
getArtifacts: this.getArtifacts.bind(this),
|
|
3365
|
-
logger: this.logger
|
|
3366
|
-
});
|
|
3367
|
-
}
|
|
3368
|
-
}
|
|
3369
|
-
getArtifacts(ctor) {
|
|
3370
|
-
return [Reflect.getMetadata(_seedcord_core_internal.SubscribeMetadataKey, ctor).subscriber];
|
|
3371
|
-
}
|
|
3372
|
-
/** @internal */
|
|
3373
|
-
async init() {
|
|
3374
|
-
if (this.isInitialized) return;
|
|
3375
|
-
this.isInitialized = true;
|
|
3376
|
-
this.registerSubscriber(UnknownException);
|
|
3377
|
-
this.registerSubscriber(HandledException);
|
|
3378
|
-
const subscribersDir = this.core.config.subscribers.path;
|
|
3379
|
-
if (subscribersDir) {
|
|
3380
|
-
this.logger.info(chalk.default.bold(subscribersDir));
|
|
3381
|
-
await this.loadSubscribers(subscribersDir);
|
|
3382
|
-
const totalSubscribers = [...this.subscribersMap.values()].reduce((acc, handlers) => acc + handlers.length, 0);
|
|
3383
|
-
this.logger.utils.list([`${chalk.default.bold.magenta(totalSubscribers)} subscribers`], chalk.default.bold.green("Loaded"));
|
|
3384
|
-
}
|
|
3385
|
-
if (!envapt.Envapter.isTest) await this.verifyWebhooks();
|
|
3386
|
-
}
|
|
3387
|
-
/** @internal Exposed for tests. */
|
|
3388
|
-
async verifyWebhooks() {
|
|
3389
|
-
const missing = [];
|
|
3390
|
-
await Promise.all([...this.webhookProbes].map(async ([url, envKeys]) => {
|
|
3391
|
-
const result = await WebhookLog.senderFor(url).verify();
|
|
3392
|
-
if (result === "missing") missing.push(...envKeys);
|
|
3393
|
-
else if (result === "unreachable") this.logger.warn(`could not verify webhook at ${_seedcord_logger.paint.sky.bold(envKeys.join(", "))}`);
|
|
3394
|
-
}));
|
|
3395
|
-
if (missing.length > 0) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.ConfigWebhookNotFound, [missing.join(", ")]);
|
|
3396
|
-
}
|
|
3397
|
-
/** @internal */
|
|
3398
|
-
async onHmr(event) {
|
|
3399
|
-
if (this.hmrHandler) await this.hmrHandler.handle(event);
|
|
3400
|
-
}
|
|
3401
|
-
async loadSubscribers(dir) {
|
|
3402
|
-
await (0, _seedcord_utils_node.traverseDirectory)(dir, (fullPath, relativePath, imported) => {
|
|
3403
|
-
for (const exportName of Object.keys(imported)) {
|
|
3404
|
-
const val = imported[exportName];
|
|
3405
|
-
if (this.isSubscriber(val)) {
|
|
3406
|
-
this.registerSubscriber(val);
|
|
3407
|
-
this.hmrHandler?.trackHandler(fullPath, val);
|
|
3408
|
-
this.logger.utils.registration(val.name, relativePath);
|
|
3409
|
-
}
|
|
3410
|
-
}
|
|
3411
|
-
}, this.logger);
|
|
3412
|
-
}
|
|
3413
|
-
registerSubscriber(handler) {
|
|
3414
|
-
if (handler.prototype instanceof WebhookLog) {
|
|
3415
|
-
const envKey = WebhookLog.envKeyOf(handler);
|
|
3416
|
-
const url = WebhookLog.urlOf(envKey);
|
|
3417
|
-
if (url === null) {
|
|
3418
|
-
this.logger.warn(`${chalk.default.bold(handler.name)} disabled, ${_seedcord_logger.paint.sky.bold(envKey)} is not set`);
|
|
3419
|
-
return;
|
|
3420
|
-
}
|
|
3421
|
-
const envKeys = this.webhookProbes.get(url) ?? [];
|
|
3422
|
-
if (!envKeys.includes(envKey)) envKeys.push(envKey);
|
|
3423
|
-
this.webhookProbes.set(url, envKeys);
|
|
3424
|
-
}
|
|
3425
|
-
const options = Reflect.getMetadata(_seedcord_core_internal.SubscribeMetadataKey, handler);
|
|
3426
|
-
let handlers = this.subscribersMap.get(options.subscriber);
|
|
3427
|
-
if (!handlers) {
|
|
3428
|
-
handlers = [];
|
|
3429
|
-
this.subscribersMap.set(options.subscriber, handlers);
|
|
3430
|
-
}
|
|
3431
|
-
handlers.push({
|
|
3432
|
-
ctor: handler,
|
|
3433
|
-
frequency: options.frequency ?? "on"
|
|
3434
|
-
});
|
|
3435
|
-
}
|
|
3436
|
-
unregisterSubscriber(handler, artifacts) {
|
|
3437
|
-
const subscribers = artifacts ?? [...this.subscribersMap.keys()];
|
|
3438
|
-
for (const subscriber of subscribers) {
|
|
3439
|
-
const handlers = this.subscribersMap.get(subscriber);
|
|
3440
|
-
if (!handlers) continue;
|
|
3441
|
-
const index = handlers.findIndex((entry) => entry.ctor === handler);
|
|
3442
|
-
if (index !== -1) handlers.splice(index, 1);
|
|
3443
|
-
}
|
|
3444
|
-
}
|
|
3445
|
-
isSubscriber(obj) {
|
|
3446
|
-
if (typeof obj !== "function") return false;
|
|
3447
|
-
return obj.prototype instanceof Subscriber && Reflect.hasMetadata(_seedcord_core_internal.SubscribeMetadataKey, obj);
|
|
3448
|
-
}
|
|
3449
|
-
/**
|
|
3450
|
-
* Publishes an event to its subscribers and native listeners.
|
|
3451
|
-
*
|
|
3452
|
-
* Fire-and-forget, so subscriber handlers run asynchronously and this returns before they
|
|
3453
|
-
* complete, and callers must not assume side effects have landed. Errors thrown by a subscriber
|
|
3454
|
-
* are caught and logged, never surfaced here. A `'once'` subscriber is marked as executed when
|
|
3455
|
-
* it starts (even if it throws), so it never runs twice.
|
|
3456
|
-
*
|
|
3457
|
-
* @param event - The subscription key to publish
|
|
3458
|
-
* @param data - Payload passed to each subscriber
|
|
3459
|
-
* @returns Whether the underlying emitter had native listeners for the event
|
|
3460
|
-
*/
|
|
3461
|
-
publish(event, data) {
|
|
3462
|
-
this.processSubscriber(event, data);
|
|
3463
|
-
return super.emit(event, ...[data]);
|
|
3464
|
-
}
|
|
3465
|
-
async processSubscriber(subscriberName, data) {
|
|
3466
|
-
const handlers = this.subscribersMap.get(subscriberName);
|
|
3467
|
-
if (!handlers) return;
|
|
3468
|
-
for (const entry of handlers) {
|
|
3469
|
-
if (entry.frequency === "once" && this.executedOnceHandlers.has(entry.ctor)) continue;
|
|
3470
|
-
try {
|
|
3471
|
-
if (entry.frequency === "once") this.executedOnceHandlers.add(entry.ctor);
|
|
3472
|
-
const Ctor = entry.ctor;
|
|
3473
|
-
await new Ctor(data, this.core).execute();
|
|
3474
|
-
} catch (err) {
|
|
3475
|
-
this.logger.error(`Error in subscriber ${String(subscriberName)} handler ${entry.ctor.name}:`, err);
|
|
3476
|
-
}
|
|
3477
|
-
}
|
|
3478
|
-
}
|
|
3479
3058
|
};
|
|
3059
|
+
__decorate([(0, envapt_legacy.Envapt)("DISCORD_BOT_TOKEN", { converter: (raw) => (0, _seedcord_errors_internal.validateDiscordToken)(raw) })], Bot.prototype, "botToken", void 0);
|
|
3480
3060
|
|
|
3481
3061
|
//#endregion
|
|
3482
3062
|
//#region src/version.ts
|
|
3483
3063
|
/** Package version */
|
|
3484
|
-
const version = "0.1.0-next.
|
|
3064
|
+
const version = "0.1.0-next.5";
|
|
3485
3065
|
|
|
3486
3066
|
//#endregion
|
|
3487
3067
|
//#region src/Seedcord.ts
|
|
@@ -3499,12 +3079,9 @@ var Seedcord = class Seedcord extends _seedcord_core_node_internal.Pluggable {
|
|
|
3499
3079
|
version = version;
|
|
3500
3080
|
healthCheck;
|
|
3501
3081
|
hmrManager;
|
|
3502
|
-
/** @see {@link CoordinatedShutdown} */
|
|
3503
|
-
shutdown;
|
|
3504
|
-
/** @see {@link CoordinatedStartup} */
|
|
3505
|
-
startup;
|
|
3506
3082
|
/** @see {@link Bus} */
|
|
3507
3083
|
bus;
|
|
3084
|
+
subscribers;
|
|
3508
3085
|
/** @see {@link Bot} */
|
|
3509
3086
|
bot;
|
|
3510
3087
|
/** @see {@link IRateLimiter} */
|
|
@@ -3514,17 +3091,14 @@ var Seedcord = class Seedcord extends _seedcord_core_node_internal.Pluggable {
|
|
|
3514
3091
|
* @throws A **SeedcordError** When attempting to create multiple instances (singleton)
|
|
3515
3092
|
*/
|
|
3516
3093
|
constructor(config) {
|
|
3517
|
-
|
|
3518
|
-
const startup = new _seedcord_core_node_internal.CoordinatedStartup();
|
|
3519
|
-
super(shutdown, startup);
|
|
3094
|
+
super(new _seedcord_core_node_internal.CoordinatedShutdown(), new _seedcord_core_node_internal.CoordinatedStartup());
|
|
3520
3095
|
this.config = config;
|
|
3521
3096
|
(0, _seedcord_logger_node.installNodeDefaults)(config.logger);
|
|
3522
3097
|
(0, _seedcord_core_internal.setBotColor)(config.botColor);
|
|
3523
|
-
this.shutdown = shutdown;
|
|
3524
|
-
this.startup = startup;
|
|
3525
3098
|
this.hmrManager = new _seedcord_core_internal.HmrManager();
|
|
3526
3099
|
this.hmrManager.init();
|
|
3527
|
-
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);
|
|
3528
3102
|
this.bot = new Bot(this);
|
|
3529
3103
|
this.rateLimiter = config.store ?? new _seedcord_rate_limiter.MemoryRateLimiter();
|
|
3530
3104
|
this.healthCheck = _seedcord_core_node_internal.HealthCheck.fromOption(this.shutdown, config.healthCheck);
|
|
@@ -3547,18 +3121,18 @@ var Seedcord = class Seedcord extends _seedcord_core_node_internal.Pluggable {
|
|
|
3547
3121
|
}
|
|
3548
3122
|
registerStartupTasks() {
|
|
3549
3123
|
if (envapt.Envapter.isDevelopment || envapt.Envapter.isTest) this.registerHmrAwareModules();
|
|
3550
|
-
this.startup.addTask(_seedcord_core_node_internal.StartupPhase.Configuration, "
|
|
3124
|
+
this.startup.addTask(_seedcord_core_node_internal.StartupPhase.Configuration, "bus-initialization", async () => {
|
|
3551
3125
|
this.bus.logger.utils.initialization("Subscribers", "start");
|
|
3552
|
-
await this.
|
|
3126
|
+
await this.subscribers.init();
|
|
3553
3127
|
this.bus.logger.utils.initialization("Subscribers", "end");
|
|
3554
3128
|
});
|
|
3555
|
-
this.startup.addTask(_seedcord_core_node_internal.StartupPhase.Login, "
|
|
3129
|
+
this.startup.addTask(_seedcord_core_node_internal.StartupPhase.Login, "bot-initialization", async () => {
|
|
3556
3130
|
this.bot.logger.utils.initialization("Bot", "start");
|
|
3557
3131
|
await this.bot.init();
|
|
3558
3132
|
this.bot.logger.utils.initialization("Bot", "end");
|
|
3559
3133
|
});
|
|
3560
3134
|
const { healthCheck } = this;
|
|
3561
|
-
if (healthCheck) this.startup.addTask(_seedcord_core_node_internal.StartupPhase.Ready, "
|
|
3135
|
+
if (healthCheck) this.startup.addTask(_seedcord_core_node_internal.StartupPhase.Ready, "health-check", async () => {
|
|
3562
3136
|
healthCheck.logger.utils.initialization("HealthCheck", "start");
|
|
3563
3137
|
await healthCheck.init();
|
|
3564
3138
|
healthCheck.logger.utils.initialization("HealthCheck", "end");
|
|
@@ -3580,9 +3154,9 @@ var Seedcord = class Seedcord extends _seedcord_core_node_internal.Pluggable {
|
|
|
3580
3154
|
return this;
|
|
3581
3155
|
}
|
|
3582
3156
|
registerHmrAwareModules() {
|
|
3583
|
-
this.startup.addTask(_seedcord_core_node_internal.StartupPhase.Configuration, "
|
|
3157
|
+
this.startup.addTask(_seedcord_core_node_internal.StartupPhase.Configuration, "hmr-registration", async () => {
|
|
3584
3158
|
this.hmrManager.register(this.bot);
|
|
3585
|
-
this.hmrManager.register(this.
|
|
3159
|
+
this.hmrManager.register(this.subscribers);
|
|
3586
3160
|
for (const plugin of this.plugins) this.hmrManager.register(plugin);
|
|
3587
3161
|
await Promise.resolve();
|
|
3588
3162
|
});
|
|
@@ -3608,15 +3182,14 @@ exports.MiddlewareType = MiddlewareType;
|
|
|
3608
3182
|
exports.ModalHandler = ModalHandler;
|
|
3609
3183
|
exports.Nsfw = Nsfw;
|
|
3610
3184
|
exports.Paginator = Paginator;
|
|
3185
|
+
exports.Plugin = Plugin;
|
|
3611
3186
|
exports.RegisterEvent = RegisterEvent;
|
|
3612
3187
|
exports.RepliableHandler = RepliableHandler;
|
|
3613
3188
|
exports.Seedcord = Seedcord;
|
|
3614
3189
|
exports.SelectMenuHandler = SelectMenuHandler;
|
|
3615
3190
|
exports.SlashHandler = SlashHandler;
|
|
3616
|
-
exports.Subscribe = Subscribe;
|
|
3617
3191
|
exports.Subscriber = Subscriber;
|
|
3618
3192
|
exports.WebhookLog = WebhookLog;
|
|
3619
|
-
exports.WebhookUrl = WebhookUrl;
|
|
3620
3193
|
exports.checkBotPermissions = checkBotPermissions;
|
|
3621
3194
|
exports.checkPermissions = checkPermissions;
|
|
3622
3195
|
exports.fetchGuildMember = fetchGuildMember;
|
|
@@ -3644,6 +3217,7 @@ Object.keys(_seedcord_errors).forEach(function (k) {
|
|
|
3644
3217
|
});
|
|
3645
3218
|
});
|
|
3646
3219
|
|
|
3220
|
+
var _seedcord_event_emitter = require("@seedcord/event-emitter");
|
|
3647
3221
|
Object.keys(_seedcord_event_emitter).forEach(function (k) {
|
|
3648
3222
|
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
3649
3223
|
enumerable: true,
|