@seedcord/http 0.1.0-next.1 → 0.1.0-next.3
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/{RouteManifest-BHdAeyLG.d.mts → RouteManifest-DE-HCMwd.d.mts} +27 -22
- package/dist/{edge.index-D-60VfMn.cjs → edge.index-Bd6sbr37.cjs} +580 -93
- package/dist/edge.index-Bd6sbr37.cjs.map +1 -0
- package/dist/{edge.index-BAgNb6TV.mjs → edge.index-Cv4plNWY.mjs} +523 -96
- package/dist/edge.index-Cv4plNWY.mjs.map +1 -0
- package/dist/{edge.index-CTOyjsO4.d.mts → edge.index-atnWgXP-.d.mts} +150 -47
- package/dist/edge.index.cjs +7 -1
- package/dist/edge.index.d.mts +4 -3
- package/dist/edge.index.mjs +2 -2
- package/dist/index.cjs +121 -40
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +42 -16
- package/dist/index.mjs +116 -42
- package/dist/index.mjs.map +1 -1
- package/dist/manifest.index.cjs +22 -5
- package/dist/manifest.index.cjs.map +1 -1
- package/dist/manifest.index.d.mts +3 -3
- package/dist/manifest.index.mjs +22 -5
- package/dist/manifest.index.mjs.map +1 -1
- package/package.json +8 -7
- package/dist/edge.index-BAgNb6TV.mjs.map +0 -1
- package/dist/edge.index-D-60VfMn.cjs.map +0 -1
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
//#region \0rolldown/runtime.js
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __exportAll = (all, no_symbols) => {
|
|
7
9
|
let target = {};
|
|
@@ -31,6 +33,10 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
31
33
|
return to;
|
|
32
34
|
};
|
|
33
35
|
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
36
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
37
|
+
value: mod,
|
|
38
|
+
enumerable: true
|
|
39
|
+
}) : target, mod));
|
|
34
40
|
|
|
35
41
|
//#endregion
|
|
36
42
|
let _seedcord_errors_internal = require("@seedcord/errors/internal");
|
|
@@ -42,8 +48,8 @@ let _seedcord_logger = require("@seedcord/logger");
|
|
|
42
48
|
let _seedcord_rate_limiter = require("@seedcord/rate-limiter");
|
|
43
49
|
let discord_api_types_v10 = require("discord-api-types/v10");
|
|
44
50
|
let _discordjs_core_http_only = require("@discordjs/core/http-only");
|
|
45
|
-
let _seedcord_errors = require("@seedcord/errors");
|
|
46
51
|
let _seedcord_utils_internal = require("@seedcord/utils/internal");
|
|
52
|
+
let _seedcord_errors = require("@seedcord/errors");
|
|
47
53
|
|
|
48
54
|
//#region src/reply/ReplySender.ts
|
|
49
55
|
function wireAllowedMentions(mentions) {
|
|
@@ -77,8 +83,11 @@ function callbackData(reply, flags) {
|
|
|
77
83
|
var ReplySender = class extends _seedcord_core_internal.BaseReplySender {
|
|
78
84
|
ref;
|
|
79
85
|
rest;
|
|
80
|
-
constructor(ref, rest, routeId) {
|
|
81
|
-
super(routeId
|
|
86
|
+
constructor(ref, rest, routeId, bus) {
|
|
87
|
+
super(routeId, "unacked", bus && {
|
|
88
|
+
bus,
|
|
89
|
+
interactionId: ref.id
|
|
90
|
+
});
|
|
82
91
|
this.ref = ref;
|
|
83
92
|
this.rest = rest;
|
|
84
93
|
}
|
|
@@ -174,12 +183,16 @@ function apiFor(rest) {
|
|
|
174
183
|
* @typeParam Event - The repliable interaction type this handler processes
|
|
175
184
|
*/
|
|
176
185
|
var RepliableHandler = class extends _seedcord_core.RepliableHandler {
|
|
186
|
+
/** @internal */
|
|
187
|
+
getEvent() {
|
|
188
|
+
return this.event;
|
|
189
|
+
}
|
|
177
190
|
buildSender(event, core, routeId) {
|
|
178
191
|
return new ReplySender({
|
|
179
192
|
application_id: event.application_id,
|
|
180
193
|
id: event.id,
|
|
181
194
|
token: event.token
|
|
182
|
-
}, core.rest, routeId);
|
|
195
|
+
}, core.rest, routeId, core.bus);
|
|
183
196
|
}
|
|
184
197
|
/**
|
|
185
198
|
* Typed Discord REST API (`@discordjs/core/http-only`) over `core.rest`, one instance per core.
|
|
@@ -212,31 +225,132 @@ function interactionGateContext(payload, core, routeId) {
|
|
|
212
225
|
};
|
|
213
226
|
}
|
|
214
227
|
|
|
228
|
+
//#endregion
|
|
229
|
+
//#region src/dispatch/slashRouteOf.ts
|
|
230
|
+
/**
|
|
231
|
+
* Computes the dispatch route for a chat-input or autocomplete payload, the raw-payload port of the
|
|
232
|
+
* gateway's `slashRouteOf`. Both assemble keys through `buildSlashRoute`, so a dispatched interaction
|
|
233
|
+
* and a generated registry key always match.
|
|
234
|
+
*/
|
|
235
|
+
function slashRouteOf(data) {
|
|
236
|
+
const first = data.options?.[0];
|
|
237
|
+
if (first?.type === discord_api_types_v10.ApplicationCommandOptionType.SubcommandGroup) return (0, _seedcord_utils_internal.buildSlashRoute)(data.name, first.options[0]?.name, first.name);
|
|
238
|
+
if (first?.type === discord_api_types_v10.ApplicationCommandOptionType.Subcommand) return (0, _seedcord_utils_internal.buildSlashRoute)(data.name, first.name);
|
|
239
|
+
return data.name;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
//#endregion
|
|
243
|
+
//#region src/dispatch/reportFault.ts
|
|
244
|
+
let faultLogger;
|
|
245
|
+
function logger$1() {
|
|
246
|
+
faultLogger ??= new _seedcord_logger.Logger("Faults", { channel: "errors" });
|
|
247
|
+
return faultLogger;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Publishes a fault on the bus, the raw-payload equivalent of the reporting half of gateway's
|
|
251
|
+
* `extractErrorResponse`. A reported {@link Notice} publishes `handledException`, everything else
|
|
252
|
+
* publishes `unknownException`.
|
|
253
|
+
*
|
|
254
|
+
* @internal
|
|
255
|
+
*/
|
|
256
|
+
function reportFault(error, uuid, payload, core) {
|
|
257
|
+
const throttle = _seedcord_core_internal.FaultThrottle.for(core);
|
|
258
|
+
const key = `${routeKeyOf(payload)}:${nameOf(error)}`;
|
|
259
|
+
if (!throttle.shouldReport(key)) {
|
|
260
|
+
logger$1().debug(`throttled duplicate fault ${_seedcord_logger.paint.mute(key)}`);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
const source = interactionSource(payload);
|
|
264
|
+
if (error instanceof _seedcord_core.Notice && source) core.bus[_seedcord_core_internal.PublishDefault]("handledException", {
|
|
265
|
+
denial: error,
|
|
266
|
+
uuid,
|
|
267
|
+
source
|
|
268
|
+
});
|
|
269
|
+
else core.bus[_seedcord_core_internal.PublishDefault]("unknownException", {
|
|
270
|
+
uuid,
|
|
271
|
+
error,
|
|
272
|
+
...actors(payload),
|
|
273
|
+
metadata: payload
|
|
274
|
+
});
|
|
275
|
+
throttle.markReported(key);
|
|
276
|
+
}
|
|
277
|
+
function routeKeyOf(payload) {
|
|
278
|
+
if (payload.type === discord_api_types_v10.InteractionType.ApplicationCommandAutocomplete) return `autocomplete:${slashRouteOf(payload.data)}`;
|
|
279
|
+
if (payload.type === discord_api_types_v10.InteractionType.ApplicationCommand) return payload.data.type === discord_api_types_v10.ApplicationCommandType.ChatInput ? slashRouteOf(payload.data) : payload.data.name;
|
|
280
|
+
return (0, _seedcord_core_internal.prefixOf)(payload.data.custom_id) || payload.data.custom_id;
|
|
281
|
+
}
|
|
282
|
+
function nameOf(error) {
|
|
283
|
+
if (error instanceof _seedcord_core.Notice) return error.name;
|
|
284
|
+
if (error instanceof _discordjs_rest.DiscordAPIError) return String(error.code);
|
|
285
|
+
return error.constructor.name;
|
|
286
|
+
}
|
|
287
|
+
function actors(payload) {
|
|
288
|
+
const user = payload.member?.user ?? payload.user;
|
|
289
|
+
return {
|
|
290
|
+
guild: void 0,
|
|
291
|
+
user: user ? {
|
|
292
|
+
id: user.id,
|
|
293
|
+
username: user.username
|
|
294
|
+
} : void 0
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
function interactionSource(payload) {
|
|
298
|
+
const user = payload.member?.user ?? payload.user;
|
|
299
|
+
if (!user) return null;
|
|
300
|
+
const common = {
|
|
301
|
+
kind: "interaction",
|
|
302
|
+
userId: user.id,
|
|
303
|
+
guildId: payload.guild_id ?? null,
|
|
304
|
+
channelId: payload.channel?.id ?? null,
|
|
305
|
+
interactionId: payload.id,
|
|
306
|
+
raw: payload
|
|
307
|
+
};
|
|
308
|
+
if (payload.type === discord_api_types_v10.InteractionType.ApplicationCommand) {
|
|
309
|
+
const isSlash = payload.data.type === discord_api_types_v10.ApplicationCommandType.ChatInput;
|
|
310
|
+
return {
|
|
311
|
+
...common,
|
|
312
|
+
interactionKind: isSlash ? "slash" : "context-menu",
|
|
313
|
+
command: routeKeyOf(payload),
|
|
314
|
+
customId: null
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
if (payload.type === discord_api_types_v10.InteractionType.ModalSubmit) return {
|
|
318
|
+
...common,
|
|
319
|
+
interactionKind: "modal",
|
|
320
|
+
command: null,
|
|
321
|
+
customId: payload.data.custom_id
|
|
322
|
+
};
|
|
323
|
+
if (payload.type === discord_api_types_v10.InteractionType.MessageComponent) {
|
|
324
|
+
const isButton = payload.data.component_type === discord_api_types_v10.ComponentType.Button;
|
|
325
|
+
return {
|
|
326
|
+
...common,
|
|
327
|
+
interactionKind: isButton ? "button" : "select",
|
|
328
|
+
command: null,
|
|
329
|
+
customId: payload.data.custom_id
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
|
|
215
335
|
//#endregion
|
|
216
336
|
//#region src/dispatch/dispatchInteraction.ts
|
|
217
337
|
let dispatchLogger;
|
|
218
338
|
function logger() {
|
|
219
|
-
dispatchLogger ??= new _seedcord_logger.Logger("Dispatcher");
|
|
339
|
+
dispatchLogger ??= new _seedcord_logger.Logger("Dispatcher", { channel: "interactions" });
|
|
220
340
|
return dispatchLogger;
|
|
221
341
|
}
|
|
222
342
|
function createCore(config, token) {
|
|
223
|
-
|
|
343
|
+
const draft = {
|
|
224
344
|
config,
|
|
225
345
|
rateLimiter: config.store ?? new _seedcord_rate_limiter.MemoryRateLimiter(),
|
|
226
|
-
rest: new _discordjs_rest.REST().setToken(token)
|
|
227
|
-
version: "0.1.0-next.1",
|
|
228
|
-
username: void 0,
|
|
229
|
-
augmentTarget: "@seedcord/http"
|
|
346
|
+
rest: new _discordjs_rest.REST().setToken(token)
|
|
230
347
|
};
|
|
348
|
+
draft.bus = new _seedcord_core.Bus(draft);
|
|
349
|
+
return draft;
|
|
231
350
|
}
|
|
232
351
|
function isHandlerCtor(value) {
|
|
233
352
|
return typeof value === "function" && value.prototype instanceof _seedcord_core.BaseHandler;
|
|
234
353
|
}
|
|
235
|
-
function handlerCtorOf(moduleExports) {
|
|
236
|
-
if (typeof moduleExports !== "object" || moduleExports === null) return null;
|
|
237
|
-
for (const value of Object.values(moduleExports)) if (isHandlerCtor(value)) return value;
|
|
238
|
-
return null;
|
|
239
|
-
}
|
|
240
354
|
const HARMLESS_API_CODES = new Set([
|
|
241
355
|
discord_api_types_v10.RESTJSONErrorCodes.UnknownInteraction,
|
|
242
356
|
discord_api_types_v10.RESTJSONErrorCodes.InteractionHasAlreadyBeenAcknowledged,
|
|
@@ -254,10 +368,13 @@ async function sendGuarded(routeId, send) {
|
|
|
254
368
|
}
|
|
255
369
|
}
|
|
256
370
|
async function respondEmptyChoices(scope) {
|
|
257
|
-
await
|
|
371
|
+
await (0, _seedcord_core_internal.reportedWrite)({
|
|
372
|
+
bus: scope.core.bus,
|
|
373
|
+
interactionId: scope.payload.id
|
|
374
|
+
}, scope.routeId, "respond", () => scope.core.rest.post(discord_api_types_v10.Routes.interactionCallback(scope.payload.id, scope.payload.token), { body: {
|
|
258
375
|
type: discord_api_types_v10.InteractionResponseType.ApplicationCommandAutocompleteResult,
|
|
259
376
|
data: { choices: [] }
|
|
260
|
-
} });
|
|
377
|
+
} }));
|
|
261
378
|
}
|
|
262
379
|
function renderContext(core, uuid) {
|
|
263
380
|
const developerUsername = core.config.notifications?.developerUsername;
|
|
@@ -267,7 +384,10 @@ function renderContext(core, uuid) {
|
|
|
267
384
|
};
|
|
268
385
|
}
|
|
269
386
|
async function handleNotice(notice, uuid, scope) {
|
|
270
|
-
if (notice.report)
|
|
387
|
+
if (notice.report) {
|
|
388
|
+
logger().error(`${notice.name}: ${_seedcord_logger.paint.mute(uuid)}`, notice);
|
|
389
|
+
reportFault(notice, uuid, scope.payload, scope.core);
|
|
390
|
+
}
|
|
271
391
|
const { sender } = scope;
|
|
272
392
|
if (!sender) {
|
|
273
393
|
await sendGuarded(scope.routeId, () => respondEmptyChoices(scope));
|
|
@@ -280,6 +400,7 @@ async function handleRawFault(error, uuid, scope) {
|
|
|
280
400
|
const { core, sender } = scope;
|
|
281
401
|
if (core.config.errors?.errorStack ?? false) logger().error(_seedcord_logger.paint.mute(uuid), error);
|
|
282
402
|
else logger().error(`${_seedcord_logger.paint.mute(uuid)} | ${error.message}`);
|
|
403
|
+
reportFault(error, uuid, scope.payload, core);
|
|
283
404
|
if (!sender) {
|
|
284
405
|
await sendGuarded(scope.routeId, () => respondEmptyChoices(scope));
|
|
285
406
|
return;
|
|
@@ -290,7 +411,7 @@ async function handleRawFault(error, uuid, scope) {
|
|
|
290
411
|
}
|
|
291
412
|
async function handleFault(caught, scope) {
|
|
292
413
|
if (caught instanceof _seedcord_core.Silence) {
|
|
293
|
-
if (caught.reason !== void 0) logger().debug(`Silence: ${caught.reason}`);
|
|
414
|
+
if (caught.reason !== void 0 && (scope.core.config.errors?.logSilences ?? true)) logger().debug(`Silence: ${caught.reason}`);
|
|
294
415
|
return;
|
|
295
416
|
}
|
|
296
417
|
const uuid = crypto.randomUUID();
|
|
@@ -298,7 +419,7 @@ async function handleFault(caught, scope) {
|
|
|
298
419
|
await handleNotice(caught, uuid, scope);
|
|
299
420
|
return;
|
|
300
421
|
}
|
|
301
|
-
const error =
|
|
422
|
+
const error = (0, _seedcord_core_internal.asError)(caught);
|
|
302
423
|
const ignore = new Set(scope.core.config.errors?.ignoreApiCodes ?? []);
|
|
303
424
|
if (error instanceof _discordjs_rest.DiscordAPIError && ignore.has(error.code)) {
|
|
304
425
|
logger().debug(`swallowed api code ${_seedcord_logger.paint.amber(String(error.code))}`);
|
|
@@ -306,79 +427,149 @@ async function handleFault(caught, scope) {
|
|
|
306
427
|
}
|
|
307
428
|
await handleRawFault(error, uuid, scope);
|
|
308
429
|
}
|
|
430
|
+
function unhandledRouteId(match) {
|
|
431
|
+
if (match.routeId) return match.routeId;
|
|
432
|
+
const key = match.attemptedKey ?? "";
|
|
433
|
+
return `${match.kind}:${key.length > 0 ? key : "unrouted"}`;
|
|
434
|
+
}
|
|
309
435
|
function freshScope(match, payload, core) {
|
|
310
436
|
const ref = {
|
|
311
437
|
application_id: payload.application_id,
|
|
312
438
|
id: payload.id,
|
|
313
439
|
token: payload.token
|
|
314
440
|
};
|
|
315
|
-
const routeId = match
|
|
441
|
+
const routeId = unhandledRouteId(match);
|
|
316
442
|
return {
|
|
317
443
|
core,
|
|
318
444
|
payload,
|
|
319
445
|
routeId,
|
|
320
|
-
sender: match.kind === "autocomplete" ? null : new ReplySender(ref, core.rest, routeId)
|
|
446
|
+
sender: match.kind === "autocomplete" ? null : new ReplySender(ref, core.rest, routeId, core.bus)
|
|
321
447
|
};
|
|
322
448
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
449
|
+
function dispatchReporter(match, payload, core) {
|
|
450
|
+
const startedAt = performance.now();
|
|
451
|
+
const queuedMs = (0, _seedcord_core_internal.queuedMsFor)(payload.id);
|
|
452
|
+
return (outcome) => {
|
|
453
|
+
core.bus[_seedcord_core_internal.PublishDefault]("interactionDispatched", (0, _seedcord_core_internal.dispatchedPayload)({
|
|
454
|
+
routeId: unhandledRouteId(match),
|
|
455
|
+
interactionId: payload.id,
|
|
456
|
+
kind: match.kind,
|
|
457
|
+
outcome,
|
|
458
|
+
fallback: match.routeId === null,
|
|
459
|
+
startedAt,
|
|
460
|
+
queuedMs
|
|
461
|
+
}));
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
async function answer(caught, scope, report, outcome = (0, _seedcord_core_internal.outcomeFor)(caught)) {
|
|
331
465
|
try {
|
|
332
|
-
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
await handleFault(caught, freshScope(match, payload, core));
|
|
336
|
-
return null;
|
|
466
|
+
await handleFault(caught, scope);
|
|
467
|
+
} finally {
|
|
468
|
+
report(outcome);
|
|
337
469
|
}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
470
|
+
}
|
|
471
|
+
async function loadHandlerCtor(match, payload, core, report) {
|
|
472
|
+
const routeId = unhandledRouteId(match);
|
|
473
|
+
let exported;
|
|
474
|
+
try {
|
|
475
|
+
exported = await match.load();
|
|
476
|
+
} catch (caught) {
|
|
477
|
+
logger().error(`Route ${_seedcord_logger.paint.sky.bold(routeId)} failed to load its handler.`, caught);
|
|
478
|
+
await answer(caught, freshScope(match, payload, core), report, "failed");
|
|
343
479
|
return null;
|
|
344
480
|
}
|
|
345
|
-
|
|
481
|
+
if (isHandlerCtor(exported)) return exported;
|
|
482
|
+
logger().error(`Route ${_seedcord_logger.paint.sky.bold(routeId)} loaded an export that is not a handler class.`);
|
|
483
|
+
await answer(/* @__PURE__ */ new Error(`route ${routeId} loaded an export that is not a handler class`), freshScope(match, payload, core), report, "failed");
|
|
484
|
+
return null;
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* Runs the pre-ack phase for a matched interaction. Loads the route's handler class, constructs it, and
|
|
488
|
+
* runs its gates. The sender posts any refusal. Returns the post-ack execute continuation, or null when a
|
|
489
|
+
* gate refuses or nothing can run.
|
|
490
|
+
*/
|
|
491
|
+
async function dispatchInteraction(args) {
|
|
492
|
+
const { match, payload, core } = args;
|
|
493
|
+
const report = dispatchReporter(match, payload, core);
|
|
494
|
+
const ctor = await loadHandlerCtor(match, payload, core, report);
|
|
495
|
+
if (!ctor) return null;
|
|
496
|
+
const dispatch = new _seedcord_core.DispatchContext(unhandledRouteId(match));
|
|
346
497
|
let handler;
|
|
347
498
|
try {
|
|
348
499
|
handler = new ctor(payload, core, dispatch);
|
|
349
500
|
} catch (caught) {
|
|
350
|
-
await
|
|
501
|
+
await answer(caught, freshScope(match, payload, core), report);
|
|
351
502
|
return null;
|
|
352
503
|
}
|
|
353
504
|
const scope = {
|
|
354
505
|
core,
|
|
355
506
|
payload,
|
|
356
|
-
routeId: match
|
|
507
|
+
routeId: unhandledRouteId(match),
|
|
357
508
|
sender: handler instanceof RepliableHandler ? handler.getSender() : null
|
|
358
509
|
};
|
|
359
|
-
|
|
510
|
+
const refusal = await gateRefusal(ctor, match, payload, core);
|
|
511
|
+
if (refusal) {
|
|
512
|
+
await answer(refusal.caught, scope, report);
|
|
513
|
+
return null;
|
|
514
|
+
}
|
|
360
515
|
return async () => {
|
|
361
516
|
try {
|
|
362
517
|
await handler.execute();
|
|
518
|
+
report("handled");
|
|
363
519
|
} catch (caught) {
|
|
364
|
-
await
|
|
520
|
+
await answer(caught, scope, report);
|
|
365
521
|
}
|
|
366
522
|
};
|
|
367
523
|
}
|
|
368
|
-
async function
|
|
369
|
-
if (match.kind === "autocomplete" || payload.type === discord_api_types_v10.InteractionType.ApplicationCommandAutocomplete) return
|
|
524
|
+
async function gateRefusal(ctor, match, payload, core) {
|
|
525
|
+
if (match.kind === "autocomplete" || payload.type === discord_api_types_v10.InteractionType.ApplicationCommandAutocomplete) return null;
|
|
370
526
|
const monitor = (0, _seedcord_core_internal.slowGateMonitor)();
|
|
371
527
|
try {
|
|
372
528
|
await (0, _seedcord_core_internal.runHandlerGates)(ctor, interactionGateContext(payload, core, match.routeId), match.routeId ?? void 0, monitor?.observe);
|
|
373
|
-
return
|
|
529
|
+
return null;
|
|
374
530
|
} catch (caught) {
|
|
375
|
-
|
|
376
|
-
return false;
|
|
531
|
+
return { caught };
|
|
377
532
|
} finally {
|
|
378
533
|
monitor?.report(match.routeId);
|
|
379
534
|
}
|
|
380
535
|
}
|
|
381
536
|
|
|
537
|
+
//#endregion
|
|
538
|
+
//#region src/dispatch/registerSubscribers.ts
|
|
539
|
+
/**
|
|
540
|
+
* Registers a manifest's subscribers on the bus without importing them. Each row's module loads on
|
|
541
|
+
* the first publish of one of its keys, which keeps an isolate that never publishes free of the cost.
|
|
542
|
+
*
|
|
543
|
+
* @internal
|
|
544
|
+
*/
|
|
545
|
+
function registerSubscribers(bus, manifest) {
|
|
546
|
+
for (const row of manifest.subscriberRoutes) bus.register({
|
|
547
|
+
keys: row.keys,
|
|
548
|
+
frequency: row.frequency,
|
|
549
|
+
resolve: async () => {
|
|
550
|
+
const keys = row.keys.join(", ");
|
|
551
|
+
let module;
|
|
552
|
+
try {
|
|
553
|
+
module = await row.load();
|
|
554
|
+
} catch (caught) {
|
|
555
|
+
throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.RouteModuleLoadFailed, [keys, row.from], { cause: caught });
|
|
556
|
+
}
|
|
557
|
+
if (!Object.hasOwn(module, row.exportName)) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.InteractionRouteExportMissing, [
|
|
558
|
+
keys,
|
|
559
|
+
row.exportName,
|
|
560
|
+
row.from
|
|
561
|
+
]);
|
|
562
|
+
const exported = module[row.exportName];
|
|
563
|
+
if (typeof exported !== "function" || !(exported.prototype instanceof _seedcord_core.Subscriber)) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.SubscriberRouteNotASubscriber, [
|
|
564
|
+
keys,
|
|
565
|
+
row.exportName,
|
|
566
|
+
row.from
|
|
567
|
+
]);
|
|
568
|
+
return exported;
|
|
569
|
+
}
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
|
|
382
573
|
//#endregion
|
|
383
574
|
//#region src/inputs/HttpSlashOptions.ts
|
|
384
575
|
var HttpSlashOptions = class {
|
|
@@ -455,20 +646,6 @@ function numeric(value) {
|
|
|
455
646
|
return Number.isNaN(parsed) ? null : parsed;
|
|
456
647
|
}
|
|
457
648
|
|
|
458
|
-
//#endregion
|
|
459
|
-
//#region src/dispatch/slashRouteOf.ts
|
|
460
|
-
/**
|
|
461
|
-
* Computes the dispatch route for a chat-input or autocomplete payload, the raw-payload port of the
|
|
462
|
-
* gateway's `slashRouteOf`. Both assemble keys through `buildSlashRoute`, so a dispatched interaction
|
|
463
|
-
* and a generated registry key always match.
|
|
464
|
-
*/
|
|
465
|
-
function slashRouteOf(data) {
|
|
466
|
-
const first = data.options?.[0];
|
|
467
|
-
if (first?.type === discord_api_types_v10.ApplicationCommandOptionType.SubcommandGroup) return (0, _seedcord_utils_internal.buildSlashRoute)(data.name, first.options[0]?.name, first.name);
|
|
468
|
-
if (first?.type === discord_api_types_v10.ApplicationCommandOptionType.Subcommand) return (0, _seedcord_utils_internal.buildSlashRoute)(data.name, first.name);
|
|
469
|
-
return data.name;
|
|
470
|
-
}
|
|
471
|
-
|
|
472
649
|
//#endregion
|
|
473
650
|
//#region src/handlers/interaction/AutocompleteHandler.ts
|
|
474
651
|
/**
|
|
@@ -482,7 +659,7 @@ function slashRouteOf(data) {
|
|
|
482
659
|
*/
|
|
483
660
|
var AutocompleteHandler = class extends _seedcord_core.BaseHandler {
|
|
484
661
|
constructor(event, core, dispatch) {
|
|
485
|
-
super(event, core, dispatch);
|
|
662
|
+
super(event, core, dispatch, "interactions");
|
|
486
663
|
}
|
|
487
664
|
/**
|
|
488
665
|
* Typed Discord REST API (`@discordjs/core/http-only`) over `core.rest`, one instance per core.
|
|
@@ -525,10 +702,14 @@ var AutocompleteHandler = class extends _seedcord_core.BaseHandler {
|
|
|
525
702
|
}
|
|
526
703
|
/** Send autocomplete suggestions, callback type 8. Prefer {@link match}, which restricts each field's choices to its declared type. */
|
|
527
704
|
async respond(choices) {
|
|
528
|
-
|
|
705
|
+
const routeId = this.dispatch?.routeId ?? "autocomplete";
|
|
706
|
+
await (0, _seedcord_core_internal.reportedWrite)({
|
|
707
|
+
bus: this.core.bus,
|
|
708
|
+
interactionId: this.event.id
|
|
709
|
+
}, routeId, "respond", () => this.core.rest.post(discord_api_types_v10.Routes.interactionCallback(this.event.id, this.event.token), { body: {
|
|
529
710
|
type: discord_api_types_v10.InteractionResponseType.ApplicationCommandAutocompleteResult,
|
|
530
711
|
data: { choices }
|
|
531
|
-
} });
|
|
712
|
+
} }));
|
|
532
713
|
}
|
|
533
714
|
/** The firing command route, for a field whose completion differs per registered command. */
|
|
534
715
|
get route() {
|
|
@@ -626,14 +807,30 @@ function componentMapKey(type) {
|
|
|
626
807
|
default: return null;
|
|
627
808
|
}
|
|
628
809
|
}
|
|
629
|
-
function
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
810
|
+
function namedExport(routeId, row) {
|
|
811
|
+
return async () => {
|
|
812
|
+
let moduleExports;
|
|
813
|
+
try {
|
|
814
|
+
moduleExports = await row.load();
|
|
815
|
+
} catch (caught) {
|
|
816
|
+
throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.RouteModuleLoadFailed, [routeId, row.from], { cause: caught });
|
|
817
|
+
}
|
|
818
|
+
if (!Object.hasOwn(moduleExports, row.exportName)) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.InteractionRouteExportMissing, [
|
|
819
|
+
routeId,
|
|
820
|
+
row.exportName,
|
|
821
|
+
row.from
|
|
822
|
+
]);
|
|
823
|
+
return moduleExports[row.exportName];
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
function describeRow(row) {
|
|
827
|
+
return `${row.exportName} (${row.from})`;
|
|
635
828
|
}
|
|
636
|
-
/**
|
|
829
|
+
/**
|
|
830
|
+
* Builds the per-kind lookup maps.
|
|
831
|
+
*
|
|
832
|
+
* @throws A **SeedcordError** when two rows resolve to the same route.
|
|
833
|
+
*/
|
|
637
834
|
function buildRouteMaps(manifest) {
|
|
638
835
|
const maps = {
|
|
639
836
|
slash: /* @__PURE__ */ new Map(),
|
|
@@ -650,19 +847,36 @@ function buildRouteMaps(manifest) {
|
|
|
650
847
|
modal: /* @__PURE__ */ new Map()
|
|
651
848
|
}
|
|
652
849
|
};
|
|
653
|
-
|
|
850
|
+
const owners = /* @__PURE__ */ new Map();
|
|
851
|
+
function set(map, kind, key, row) {
|
|
852
|
+
const routeId = `${kind}:${key}`;
|
|
853
|
+
const owner = owners.get(routeId);
|
|
854
|
+
if (owner) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.InteractionDuplicateRoute, [
|
|
855
|
+
routeId,
|
|
856
|
+
describeRow(owner),
|
|
857
|
+
describeRow(row)
|
|
858
|
+
]);
|
|
859
|
+
owners.set(routeId, row);
|
|
860
|
+
map.set(key, {
|
|
861
|
+
kind,
|
|
862
|
+
routeId,
|
|
863
|
+
load: namedExport(routeId, row)
|
|
864
|
+
});
|
|
865
|
+
}
|
|
866
|
+
for (const row of manifest.commandRoutes) {
|
|
654
867
|
const kind = commandKind(row.type);
|
|
655
|
-
if (kind) set(maps[kind], kind, row.name, row
|
|
868
|
+
if (kind) set(maps[kind], kind, row.name, row);
|
|
656
869
|
}
|
|
657
|
-
for (const row of manifest.
|
|
658
|
-
for (const row of manifest.
|
|
870
|
+
for (const row of manifest.autocompleteRoutes) set(maps.autocomplete, "autocomplete", row.name, row);
|
|
871
|
+
for (const row of manifest.componentRoutes) set(maps.components[row.kind], COMPONENT_KIND[row.kind], row.prefix, row);
|
|
659
872
|
return maps;
|
|
660
873
|
}
|
|
661
|
-
function unhandled(kind) {
|
|
874
|
+
function unhandled(kind, attemptedKey) {
|
|
662
875
|
return {
|
|
663
876
|
kind,
|
|
664
877
|
routeId: null,
|
|
665
|
-
|
|
878
|
+
attemptedKey,
|
|
879
|
+
load: () => Promise.resolve(kind === "autocomplete" ? UnhandledAutocomplete : UnhandledRepliable)
|
|
666
880
|
};
|
|
667
881
|
}
|
|
668
882
|
/**
|
|
@@ -675,17 +889,27 @@ function unhandled(kind) {
|
|
|
675
889
|
function resolve(maps, interaction) {
|
|
676
890
|
switch (interaction.type) {
|
|
677
891
|
case discord_api_types_v10.InteractionType.ApplicationCommand: {
|
|
678
|
-
if (interaction.data.type === discord_api_types_v10.ApplicationCommandType.ChatInput)
|
|
892
|
+
if (interaction.data.type === discord_api_types_v10.ApplicationCommandType.ChatInput) {
|
|
893
|
+
const route = slashRouteOf(interaction.data);
|
|
894
|
+
return maps.slash.get(route) ?? unhandled("slash", route);
|
|
895
|
+
}
|
|
679
896
|
const kind = commandKind(interaction.data.type);
|
|
680
|
-
return kind ? maps[kind].get(interaction.data.name) ?? unhandled(kind) : null;
|
|
897
|
+
return kind ? maps[kind].get(interaction.data.name) ?? unhandled(kind, interaction.data.name) : null;
|
|
898
|
+
}
|
|
899
|
+
case discord_api_types_v10.InteractionType.ApplicationCommandAutocomplete: {
|
|
900
|
+
const route = slashRouteOf(interaction.data);
|
|
901
|
+
return maps.autocomplete.get(route) ?? unhandled("autocomplete", route);
|
|
681
902
|
}
|
|
682
|
-
case discord_api_types_v10.InteractionType.ApplicationCommandAutocomplete: return maps.autocomplete.get(slashRouteOf(interaction.data)) ?? unhandled("autocomplete");
|
|
683
903
|
case discord_api_types_v10.InteractionType.MessageComponent: {
|
|
684
904
|
const key = componentMapKey(interaction.data.component_type);
|
|
685
905
|
if (!key) return null;
|
|
686
|
-
|
|
906
|
+
const prefix = (0, _seedcord_core_internal.prefixOf)(interaction.data.custom_id);
|
|
907
|
+
return maps.components[key].get(prefix) ?? unhandled(COMPONENT_KIND[key], prefix);
|
|
908
|
+
}
|
|
909
|
+
case discord_api_types_v10.InteractionType.ModalSubmit: {
|
|
910
|
+
const prefix = (0, _seedcord_core_internal.prefixOf)(interaction.data.custom_id);
|
|
911
|
+
return maps.components.modal.get(prefix) ?? unhandled("modal", prefix);
|
|
687
912
|
}
|
|
688
|
-
case discord_api_types_v10.InteractionType.ModalSubmit: return maps.components.modal.get((0, _seedcord_core_internal.prefixOf)(interaction.data.custom_id)) ?? unhandled("modal");
|
|
689
913
|
default: return null;
|
|
690
914
|
}
|
|
691
915
|
}
|
|
@@ -766,7 +990,7 @@ function buildEngine(core, maps) {
|
|
|
766
990
|
const verifier = new Ed25519Verifier(envapt.Envapter.getRequired("DISCORD_PUBLIC_KEY", envapt.Converters.String));
|
|
767
991
|
const replays = new ReplayGuard();
|
|
768
992
|
const inFlight = /* @__PURE__ */ new Set();
|
|
769
|
-
const logger = new _seedcord_logger.Logger("Engine");
|
|
993
|
+
const logger = new _seedcord_logger.Logger("Engine", { channel: "bot" });
|
|
770
994
|
async function dispatchMatched(match, payload, ctx) {
|
|
771
995
|
const start = await dispatchInteraction({
|
|
772
996
|
match,
|
|
@@ -774,13 +998,18 @@ function buildEngine(core, maps) {
|
|
|
774
998
|
core
|
|
775
999
|
});
|
|
776
1000
|
if (!start) return;
|
|
777
|
-
const work = start();
|
|
1001
|
+
const work = start().catch(rootFault);
|
|
778
1002
|
if (ctx) ctx.waitUntil(work);
|
|
779
1003
|
else {
|
|
780
1004
|
inFlight.add(work);
|
|
781
1005
|
work.finally(() => inFlight.delete(work));
|
|
782
1006
|
}
|
|
783
1007
|
}
|
|
1008
|
+
function rootFault(caught) {
|
|
1009
|
+
const error = (0, _seedcord_core_internal.asError)(caught);
|
|
1010
|
+
logger.error("dispatch failed past the boundary", error);
|
|
1011
|
+
core.bus[_seedcord_core_internal.PublishDefault]("unhandledInteractionError", { error });
|
|
1012
|
+
}
|
|
784
1013
|
const handle = async (request, ctx) => {
|
|
785
1014
|
if (request.method !== "POST") return new Response(null, {
|
|
786
1015
|
status: METHOD_NOT_ALLOWED,
|
|
@@ -801,10 +1030,11 @@ function buildEngine(core, maps) {
|
|
|
801
1030
|
if (!hasInteractionType(payload)) return new Response(null, { status: BAD_REQUEST });
|
|
802
1031
|
if (payload.type === PING) return Response.json({ type: discord_api_types_v10.InteractionResponseType.Pong });
|
|
803
1032
|
try {
|
|
1033
|
+
core.bus[_seedcord_core_internal.PublishDefault]("anyInteraction", { interaction: payload });
|
|
804
1034
|
const match = resolve(maps, payload);
|
|
805
1035
|
if (match) await dispatchMatched(match, payload, ctx);
|
|
806
1036
|
} catch (caught) {
|
|
807
|
-
|
|
1037
|
+
rootFault(caught);
|
|
808
1038
|
}
|
|
809
1039
|
return new Response(null, { status: ACCEPTED });
|
|
810
1040
|
};
|
|
@@ -830,9 +1060,120 @@ function buildEngine(core, maps) {
|
|
|
830
1060
|
* `SeedcordError` when either is missing or malformed.
|
|
831
1061
|
*/
|
|
832
1062
|
function createSeedcord(config, manifest) {
|
|
833
|
-
|
|
1063
|
+
const core = createCore(config, (0, _seedcord_errors_internal.validateDiscordToken)(envapt.Envapter.get("DISCORD_BOT_TOKEN")));
|
|
1064
|
+
core.bus.registerDefaults();
|
|
1065
|
+
registerSubscribers(core.bus, manifest);
|
|
1066
|
+
return buildEngine(core, buildRouteMaps(manifest)).handle;
|
|
834
1067
|
}
|
|
835
1068
|
|
|
1069
|
+
//#endregion
|
|
1070
|
+
//#region src/applicationId.ts
|
|
1071
|
+
/**
|
|
1072
|
+
* Reads the bot's own application id over REST. A gateway host takes it off the logged-in client, and
|
|
1073
|
+
* an http host has no session to read it from.
|
|
1074
|
+
*
|
|
1075
|
+
* @internal
|
|
1076
|
+
*/
|
|
1077
|
+
async function fetchApplicationId(rest) {
|
|
1078
|
+
try {
|
|
1079
|
+
return (await rest.get(discord_api_types_v10.Routes.currentApplication())).id;
|
|
1080
|
+
} catch (error) {
|
|
1081
|
+
throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.CoreApplicationUnavailable, { cause: error });
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
//#endregion
|
|
1086
|
+
//#region src/emojis/EmojiInjector.ts
|
|
1087
|
+
var Emoji = class {
|
|
1088
|
+
name;
|
|
1089
|
+
id;
|
|
1090
|
+
animated;
|
|
1091
|
+
constructor(name, id, animated = false) {
|
|
1092
|
+
this.name = name;
|
|
1093
|
+
this.id = id;
|
|
1094
|
+
this.animated = animated;
|
|
1095
|
+
}
|
|
1096
|
+
toString() {
|
|
1097
|
+
return `<${this.animated ? "a" : ""}:${this.name}:${this.id}>`;
|
|
1098
|
+
}
|
|
1099
|
+
};
|
|
1100
|
+
const emojiStorage = (0, _seedcord_core_internal.accessorStore)();
|
|
1101
|
+
function reasonOf(error) {
|
|
1102
|
+
return Error.isError(error) ? error.message : String(error);
|
|
1103
|
+
}
|
|
1104
|
+
function byName(list) {
|
|
1105
|
+
const map = /* @__PURE__ */ new Map();
|
|
1106
|
+
for (const entry of list) if (entry.name) map.set(entry.name, entry);
|
|
1107
|
+
return map;
|
|
1108
|
+
}
|
|
1109
|
+
/**
|
|
1110
|
+
* The bot's resolved emojis, keyed by {@link EmojiMap}. Filled by {@link EmojiInjector} during startup.
|
|
1111
|
+
* A read before that throws.
|
|
1112
|
+
*/
|
|
1113
|
+
const Emojis = (0, _seedcord_core_internal.guardedAccessor)("Emojis", emojiStorage);
|
|
1114
|
+
/**
|
|
1115
|
+
* Resolves the configured emojis over REST at startup.
|
|
1116
|
+
*
|
|
1117
|
+
* @internal
|
|
1118
|
+
*/
|
|
1119
|
+
var EmojiInjector = class {
|
|
1120
|
+
core;
|
|
1121
|
+
applicationId;
|
|
1122
|
+
logger = new _seedcord_logger.Logger("Emojis", { channel: "bot" });
|
|
1123
|
+
constructor(core, applicationId = () => fetchApplicationId(core.rest)) {
|
|
1124
|
+
this.core = core;
|
|
1125
|
+
this.applicationId = applicationId;
|
|
1126
|
+
}
|
|
1127
|
+
async init() {
|
|
1128
|
+
(0, _seedcord_core_internal.clearStore)(emojiStorage);
|
|
1129
|
+
const configured = this.core.config.bot.emojis;
|
|
1130
|
+
if (!configured || Object.keys(configured).length === 0) {
|
|
1131
|
+
this.logger.debug("No emojis configured, skipping emoji injection.");
|
|
1132
|
+
return;
|
|
1133
|
+
}
|
|
1134
|
+
const failures = [];
|
|
1135
|
+
const guilds = /* @__PURE__ */ new Map();
|
|
1136
|
+
let application;
|
|
1137
|
+
for (const [key, value] of Object.entries(configured)) if ((0, _seedcord_core_internal.isEmojiTuple)(value)) await this.resolveTuple(key, value, guilds, failures);
|
|
1138
|
+
else if (typeof value === "string") {
|
|
1139
|
+
application ??= await this.applicationEmojis(failures);
|
|
1140
|
+
this.resolve(key, value, application, failures, "the application");
|
|
1141
|
+
} else failures.push(` - "${key}" has an invalid value (expected a name or [name, guildId])`);
|
|
1142
|
+
if (failures.length > 0) throw new _seedcord_errors_internal.SeedcordError(_seedcord_errors.SeedcordErrorCode.ConfigEmojiUnresolved, [failures.length, failures.join("\n")]);
|
|
1143
|
+
this.logger.utils.summary("Loaded emojis", { emojis: Object.keys(emojiStorage).length });
|
|
1144
|
+
}
|
|
1145
|
+
async applicationEmojis(failures) {
|
|
1146
|
+
try {
|
|
1147
|
+
const appId = await this.applicationId();
|
|
1148
|
+
return byName((await this.core.rest.get(discord_api_types_v10.Routes.applicationEmojis(appId))).items);
|
|
1149
|
+
} catch (error) {
|
|
1150
|
+
failures.push(` - the application emojis could not be read (${reasonOf(error)})`);
|
|
1151
|
+
return /* @__PURE__ */ new Map();
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
async resolveTuple(key, [name, guildId], guilds, failures) {
|
|
1155
|
+
let guild = guilds.get(guildId);
|
|
1156
|
+
if (!guild) {
|
|
1157
|
+
try {
|
|
1158
|
+
guild = byName(await this.core.rest.get(discord_api_types_v10.Routes.guildEmojis(guildId)));
|
|
1159
|
+
} catch (error) {
|
|
1160
|
+
failures.push(` - "${name}" for "${key}" targets guild ${guildId}, which could not be read (${reasonOf(error)})`);
|
|
1161
|
+
return;
|
|
1162
|
+
}
|
|
1163
|
+
guilds.set(guildId, guild);
|
|
1164
|
+
}
|
|
1165
|
+
this.resolve(key, name, guild, failures, `guild ${guildId}`);
|
|
1166
|
+
}
|
|
1167
|
+
resolve(key, name, source, failures, where) {
|
|
1168
|
+
const found = source.get(name);
|
|
1169
|
+
if (!found?.id) {
|
|
1170
|
+
failures.push(` - "${name}" for "${key}" was not found in ${where}`);
|
|
1171
|
+
return;
|
|
1172
|
+
}
|
|
1173
|
+
emojiStorage[key] = new Emoji(name, found.id, found.animated ?? false);
|
|
1174
|
+
}
|
|
1175
|
+
};
|
|
1176
|
+
|
|
836
1177
|
//#endregion
|
|
837
1178
|
//#region src/handlers/interaction/SlashHandler.ts
|
|
838
1179
|
/**
|
|
@@ -1013,12 +1354,12 @@ var SelectMenuHandler = class extends ComponentHandler {};
|
|
|
1013
1354
|
* @typeParam Defs - The customId definitions this handler decodes, e.g. `[typeof ConfigId]`.
|
|
1014
1355
|
*/
|
|
1015
1356
|
var ModalHandler = class extends ComponentHandler {
|
|
1016
|
-
/** Rewrite the message this modal was opened from
|
|
1357
|
+
/** Rewrite the message this modal was opened from. Throws when a command opened the modal. */
|
|
1017
1358
|
update(response) {
|
|
1018
1359
|
this.ensureSourceMessage("update");
|
|
1019
1360
|
return super.update(response);
|
|
1020
1361
|
}
|
|
1021
|
-
/**
|
|
1362
|
+
/** Acknowledge the submit without changing the source message. Throws when a command opened the modal. */
|
|
1022
1363
|
deferUpdate() {
|
|
1023
1364
|
this.ensureSourceMessage("deferUpdate");
|
|
1024
1365
|
return super.deferUpdate();
|
|
@@ -1029,6 +1370,98 @@ var ModalHandler = class extends ComponentHandler {
|
|
|
1029
1370
|
}
|
|
1030
1371
|
};
|
|
1031
1372
|
|
|
1373
|
+
//#endregion
|
|
1374
|
+
//#region src/subscribers/index.ts
|
|
1375
|
+
/**
|
|
1376
|
+
* Base class for an HTTP subscriber, binding `this.core` to the HTTP {@link Core}.
|
|
1377
|
+
*
|
|
1378
|
+
* @typeParam KeyOfSubscribers - The subscription key this handler receives
|
|
1379
|
+
*/
|
|
1380
|
+
var Subscriber = class extends _seedcord_core.Subscriber {};
|
|
1381
|
+
/**
|
|
1382
|
+
* Base class for an HTTP subscriber that delivers its event to a Discord webhook, binding
|
|
1383
|
+
* `this.core` to the HTTP {@link Core}.
|
|
1384
|
+
*
|
|
1385
|
+
* @typeParam KeyOfSubscribers - The subscription key this reporter receives
|
|
1386
|
+
*/
|
|
1387
|
+
var WebhookLog = class extends _seedcord_core.WebhookLog {};
|
|
1388
|
+
|
|
1389
|
+
//#endregion
|
|
1390
|
+
//#region src/pagination/Paginator.ts
|
|
1391
|
+
function contextOf(interaction, core) {
|
|
1392
|
+
return {
|
|
1393
|
+
interaction,
|
|
1394
|
+
user: interaction.member?.user ?? interaction.user,
|
|
1395
|
+
guildId: interaction.guild_id ?? null,
|
|
1396
|
+
core
|
|
1397
|
+
};
|
|
1398
|
+
}
|
|
1399
|
+
/**
|
|
1400
|
+
* A restart-proof paginator. Each nav button's customId encodes its full target page, so clicks are
|
|
1401
|
+
* idempotent and survive a restart. A persistent `@ButtonRoute` on `Handler` dispatches the clicks.
|
|
1402
|
+
*
|
|
1403
|
+
* @typeParam Item - The item type, inferred from the source.
|
|
1404
|
+
* @typeParam Prefix - The route prefix, inferred from `config.prefix`.
|
|
1405
|
+
*
|
|
1406
|
+
* @example
|
|
1407
|
+
* ```ts
|
|
1408
|
+
* export const Bans = new Paginator({
|
|
1409
|
+
* prefix: 'bans',
|
|
1410
|
+
* source: new ArraySource((ctx) => loadBans(ctx.guildId), { perPage: 10 }),
|
|
1411
|
+
* renderItem: (ban) => ban.user.username
|
|
1412
|
+
* });
|
|
1413
|
+
*
|
|
1414
|
+
* \@ButtonRoute(Bans.cursor)
|
|
1415
|
+
* export class BansNav extends Bans.Handler {}
|
|
1416
|
+
* ```
|
|
1417
|
+
*/
|
|
1418
|
+
var Paginator = class extends _seedcord_core.PaginatorBase {
|
|
1419
|
+
/** The nav handler base. Extend it with an empty body and decorate it, `@ButtonRoute(p.cursor)`. */
|
|
1420
|
+
Handler;
|
|
1421
|
+
constructor(config) {
|
|
1422
|
+
super(config);
|
|
1423
|
+
const loadPage = (ctx, n) => this.page(ctx, n);
|
|
1424
|
+
this.Handler = class Nav extends ButtonHandler {
|
|
1425
|
+
async execute() {
|
|
1426
|
+
await this.deferUpdate();
|
|
1427
|
+
const response = await loadPage(contextOf(this.event, this.core), this.params.page);
|
|
1428
|
+
await this.update(response);
|
|
1429
|
+
}
|
|
1430
|
+
};
|
|
1431
|
+
}
|
|
1432
|
+
/**
|
|
1433
|
+
* Render page 0 and send it through the handler's sender, which picks reply, edit, or followUp from its
|
|
1434
|
+
* ack state.
|
|
1435
|
+
*
|
|
1436
|
+
* @param handler - The handler starting the paginator, normally `this`.
|
|
1437
|
+
*/
|
|
1438
|
+
async start(handler) {
|
|
1439
|
+
const interaction = handler.getEvent();
|
|
1440
|
+
const response = await this.page(contextOf(interaction, handler.core), 0);
|
|
1441
|
+
return handler.getSender().send(response, { ephemeral: this.config.ephemeral ?? false });
|
|
1442
|
+
}
|
|
1443
|
+
};
|
|
1444
|
+
|
|
1445
|
+
//#endregion
|
|
1446
|
+
//#region src/pagination/sources.ts
|
|
1447
|
+
/**
|
|
1448
|
+
* A source for a bounded list you can load whole. It loads the full list on every click, then slices the
|
|
1449
|
+
* page, so the real total is known (a real last button, "Page X of Y"). Cache inside your loader if the
|
|
1450
|
+
* load is expensive.
|
|
1451
|
+
*
|
|
1452
|
+
* @typeParam Item - The item type, inferred from the loader's return.
|
|
1453
|
+
*/
|
|
1454
|
+
var ArraySource = class extends _seedcord_core.ArraySource {};
|
|
1455
|
+
/**
|
|
1456
|
+
* A source for a large or unknown-length set you fetch one page at a time (SQL LIMIT/OFFSET, a paged API).
|
|
1457
|
+
* The fetcher receives the page index and the page size and reports whether a next page exists. A cursor
|
|
1458
|
+
* source has no cheap total, so `totalPages` is undefined, the last button is omitted, and the indicator
|
|
1459
|
+
* reads "Page X".
|
|
1460
|
+
*
|
|
1461
|
+
* @typeParam Item - The item type, inferred from the fetcher's slice.
|
|
1462
|
+
*/
|
|
1463
|
+
var CursorSource = class extends _seedcord_core.CursorSource {};
|
|
1464
|
+
|
|
1032
1465
|
//#endregion
|
|
1033
1466
|
//#region src/gates/Gated.ts
|
|
1034
1467
|
/**
|
|
@@ -1073,9 +1506,15 @@ function Gated(...gates) {
|
|
|
1073
1506
|
//#endregion
|
|
1074
1507
|
//#region src/version.ts
|
|
1075
1508
|
/** Package version */
|
|
1076
|
-
const version = "0.1.0-next.
|
|
1509
|
+
const version = "0.1.0-next.3";
|
|
1077
1510
|
|
|
1078
1511
|
//#endregion
|
|
1512
|
+
Object.defineProperty(exports, 'ArraySource', {
|
|
1513
|
+
enumerable: true,
|
|
1514
|
+
get: function () {
|
|
1515
|
+
return ArraySource;
|
|
1516
|
+
}
|
|
1517
|
+
});
|
|
1079
1518
|
Object.defineProperty(exports, 'AutocompleteHandler', {
|
|
1080
1519
|
enumerable: true,
|
|
1081
1520
|
get: function () {
|
|
@@ -1100,6 +1539,24 @@ Object.defineProperty(exports, 'ContextMenuHandler', {
|
|
|
1100
1539
|
return ContextMenuHandler;
|
|
1101
1540
|
}
|
|
1102
1541
|
});
|
|
1542
|
+
Object.defineProperty(exports, 'CursorSource', {
|
|
1543
|
+
enumerable: true,
|
|
1544
|
+
get: function () {
|
|
1545
|
+
return CursorSource;
|
|
1546
|
+
}
|
|
1547
|
+
});
|
|
1548
|
+
Object.defineProperty(exports, 'EmojiInjector', {
|
|
1549
|
+
enumerable: true,
|
|
1550
|
+
get: function () {
|
|
1551
|
+
return EmojiInjector;
|
|
1552
|
+
}
|
|
1553
|
+
});
|
|
1554
|
+
Object.defineProperty(exports, 'Emojis', {
|
|
1555
|
+
enumerable: true,
|
|
1556
|
+
get: function () {
|
|
1557
|
+
return Emojis;
|
|
1558
|
+
}
|
|
1559
|
+
});
|
|
1103
1560
|
Object.defineProperty(exports, 'Gated', {
|
|
1104
1561
|
enumerable: true,
|
|
1105
1562
|
get: function () {
|
|
@@ -1118,6 +1575,12 @@ Object.defineProperty(exports, 'ModalHandler', {
|
|
|
1118
1575
|
return ModalHandler;
|
|
1119
1576
|
}
|
|
1120
1577
|
});
|
|
1578
|
+
Object.defineProperty(exports, 'Paginator', {
|
|
1579
|
+
enumerable: true,
|
|
1580
|
+
get: function () {
|
|
1581
|
+
return Paginator;
|
|
1582
|
+
}
|
|
1583
|
+
});
|
|
1121
1584
|
Object.defineProperty(exports, 'RepliableHandler', {
|
|
1122
1585
|
enumerable: true,
|
|
1123
1586
|
get: function () {
|
|
@@ -1136,6 +1599,18 @@ Object.defineProperty(exports, 'SlashHandler', {
|
|
|
1136
1599
|
return SlashHandler;
|
|
1137
1600
|
}
|
|
1138
1601
|
});
|
|
1602
|
+
Object.defineProperty(exports, 'Subscriber', {
|
|
1603
|
+
enumerable: true,
|
|
1604
|
+
get: function () {
|
|
1605
|
+
return Subscriber;
|
|
1606
|
+
}
|
|
1607
|
+
});
|
|
1608
|
+
Object.defineProperty(exports, 'WebhookLog', {
|
|
1609
|
+
enumerable: true,
|
|
1610
|
+
get: function () {
|
|
1611
|
+
return WebhookLog;
|
|
1612
|
+
}
|
|
1613
|
+
});
|
|
1139
1614
|
Object.defineProperty(exports, '__exportAll', {
|
|
1140
1615
|
enumerable: true,
|
|
1141
1616
|
get: function () {
|
|
@@ -1148,6 +1623,12 @@ Object.defineProperty(exports, '__reExport', {
|
|
|
1148
1623
|
return __reExport;
|
|
1149
1624
|
}
|
|
1150
1625
|
});
|
|
1626
|
+
Object.defineProperty(exports, '__toESM', {
|
|
1627
|
+
enumerable: true,
|
|
1628
|
+
get: function () {
|
|
1629
|
+
return __toESM;
|
|
1630
|
+
}
|
|
1631
|
+
});
|
|
1151
1632
|
Object.defineProperty(exports, 'buildEngine', {
|
|
1152
1633
|
enumerable: true,
|
|
1153
1634
|
get: function () {
|
|
@@ -1166,10 +1647,16 @@ Object.defineProperty(exports, 'createSeedcord', {
|
|
|
1166
1647
|
return createSeedcord;
|
|
1167
1648
|
}
|
|
1168
1649
|
});
|
|
1650
|
+
Object.defineProperty(exports, 'fetchApplicationId', {
|
|
1651
|
+
enumerable: true,
|
|
1652
|
+
get: function () {
|
|
1653
|
+
return fetchApplicationId;
|
|
1654
|
+
}
|
|
1655
|
+
});
|
|
1169
1656
|
Object.defineProperty(exports, 'version', {
|
|
1170
1657
|
enumerable: true,
|
|
1171
1658
|
get: function () {
|
|
1172
1659
|
return version;
|
|
1173
1660
|
}
|
|
1174
1661
|
});
|
|
1175
|
-
//# sourceMappingURL=edge.index-
|
|
1662
|
+
//# sourceMappingURL=edge.index-Bd6sbr37.cjs.map
|