@seedcord/http 0.1.0-next.3 → 0.2.0-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{RouteManifest-DE-HCMwd.d.mts → RouteManifest-4jc-Lpnm.d.mts} +1 -2
- package/dist/{edge.index-atnWgXP-.d.mts → edge.index-CrVektFx.d.mts} +17 -16
- package/dist/{edge.index-Cv4plNWY.mjs → edge.index-DPLzbHBG.mjs} +51 -35
- package/dist/edge.index-DPLzbHBG.mjs.map +1 -0
- package/dist/{edge.index-Bd6sbr37.cjs → edge.index-L811vbOj.cjs} +51 -34
- package/dist/edge.index-L811vbOj.cjs.map +1 -0
- package/dist/edge.index.cjs +1 -1
- package/dist/edge.index.d.mts +2 -2
- package/dist/edge.index.mjs +1 -1
- package/dist/index.cjs +11 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +5 -12
- package/dist/index.mjs +13 -48
- package/dist/index.mjs.map +1 -1
- package/dist/manifest.index.d.mts +1 -2
- package/package.json +16 -16
- package/dist/edge.index-Bd6sbr37.cjs.map +0 -1
- package/dist/edge.index-Cv4plNWY.mjs.map +0 -1
- package/dist/rolldown-runtime-BUUfjzRC.mjs +0 -36
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { SelectMenuKind, SubscriptionKey } from "@seedcord/core";
|
|
2
2
|
import { ApplicationCommandType } from "discord-api-types/v10";
|
|
3
3
|
import { EventFrequency } from "@seedcord/types";
|
|
4
|
-
|
|
5
4
|
//#region src/manifest/RouteManifest.d.ts
|
|
6
5
|
/** The command kinds a manifest row registers, the wire enum's other members have no handler base. */
|
|
7
6
|
type RegistrableCommandType = ApplicationCommandType.ChatInput | ApplicationCommandType.User | ApplicationCommandType.Message;
|
|
@@ -62,4 +61,4 @@ interface RouteManifest {
|
|
|
62
61
|
}
|
|
63
62
|
//#endregion
|
|
64
63
|
export { RouteManifest as t };
|
|
65
|
-
//# sourceMappingURL=RouteManifest-
|
|
64
|
+
//# sourceMappingURL=RouteManifest-4jc-Lpnm.d.mts.map
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { t as RouteManifest } from "./RouteManifest-
|
|
1
|
+
import { t as RouteManifest } from "./RouteManifest-4jc-Lpnm.mjs";
|
|
2
|
+
import "reflect-metadata";
|
|
2
3
|
import { REST } from "@discordjs/rest";
|
|
3
4
|
import { ArraySource, AutocompleteOptions, BaseHandler, Bus, ContextMenuKind, CoreBase, CursorSource, DispatchContext, Gate, GateContextBase, GuildPermissionsContext, PaginatorBase, PaginatorConfig, RepliableHandler, RequiredOf, SelectMenuKind, SlashOptionRegistry, Subscriber, SubscriptionKey, WebhookLog } from "@seedcord/core";
|
|
4
5
|
import { AnyCustomId, AutocompletableNames, BaseReplySender, ChoiceValueOf, EntryFor, FocusedField, GateFitsWith, HasComponentDefs, InteractionRoutes, MatchArms, ModalLike, PageCursor, SingleParams, SlashOptions } from "@seedcord/core/internal";
|
|
5
6
|
import { APIApplicationCommandAutocompleteInteraction, APIApplicationCommandOptionChoice, APIAttachment, APIChatInputApplicationCommandInteraction, APIContextMenuInteraction, APIInteraction, APIInteractionDataResolvedChannelBase, APIInteractionDataResolvedGuildMember, APIInteractionGuildMember, APIMessage, APIMessageApplicationCommandInteraction, APIMessageChannelSelectInteractionData, APIMessageComponentButtonInteraction, APIMessageComponentEmoji, APIMessageComponentInteraction, APIMessageComponentSelectMenuInteraction, APIMessageMentionableSelectInteractionData, APIMessageRoleSelectInteractionData, APIMessageStringSelectInteractionData, APIMessageUserSelectInteractionData, APIModalInteractionResponseCallbackData, APIModalSubmitInteraction, APIRole, APIThreadChannel, APIUser, APIUserApplicationCommandInteraction, ApplicationCommandType, ChannelType, ThreadChannelType } from "discord-api-types/v10";
|
|
6
7
|
import { API } from "@discordjs/core/http-only";
|
|
7
|
-
import { Config, DeferOpts, EmojiMap,
|
|
8
|
+
import { Config, DeferOpts, EmojiMap, ReplyResponse, SendOpts } from "@seedcord/types";
|
|
8
9
|
import { NonEmptyTuple, Promisable } from "type-fest";
|
|
9
|
-
|
|
10
10
|
//#region src/subscriptions.d.ts
|
|
11
11
|
declare module '@seedcord/core/internal' {
|
|
12
12
|
interface DefaultSubscriptions {
|
|
@@ -19,15 +19,16 @@ declare module '@seedcord/core/internal' {
|
|
|
19
19
|
//#endregion
|
|
20
20
|
//#region src/interfaces/Config.d.ts
|
|
21
21
|
/**
|
|
22
|
-
* Config for a long-running node server. Pass to `new Seedcord(config).start(
|
|
22
|
+
* Config for a long-running node server. Pass to `new Seedcord(config).start()`.
|
|
23
23
|
*/
|
|
24
24
|
interface HttpServerConfig extends Config {
|
|
25
25
|
runtime?: 'server';
|
|
26
26
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
27
|
+
* Port the interactions server uses.
|
|
28
|
+
*
|
|
29
|
+
* @defaultValue `3000`
|
|
29
30
|
*/
|
|
30
|
-
|
|
31
|
+
port?: number;
|
|
31
32
|
}
|
|
32
33
|
/**
|
|
33
34
|
* Config for a bundled isolate deployment. `seedcord build` generates a worker entry that calls
|
|
@@ -35,7 +36,7 @@ interface HttpServerConfig extends Config {
|
|
|
35
36
|
*/
|
|
36
37
|
interface HttpEdgeConfig extends Config {
|
|
37
38
|
runtime: 'edge';
|
|
38
|
-
|
|
39
|
+
port?: never;
|
|
39
40
|
}
|
|
40
41
|
/**
|
|
41
42
|
* The http transport's configuration, discriminated on `runtime`.
|
|
@@ -86,7 +87,7 @@ declare function createSeedcord(config: HttpConfig, manifest: RouteManifest): (r
|
|
|
86
87
|
//#endregion
|
|
87
88
|
//#region src/inputs/SlashOptions.d.ts
|
|
88
89
|
type ResolvedChannelOf<Type extends ChannelType> = APIInteractionDataResolvedChannelBase<Type> & (Type extends ThreadChannelType ? Pick<APIThreadChannel, 'parent_id' | 'thread_metadata'> : unknown);
|
|
89
|
-
type AnyResolvedChannel = { [Type in ChannelType]: ResolvedChannelOf<Type
|
|
90
|
+
type AnyResolvedChannel = { [Type in ChannelType]: ResolvedChannelOf<Type>; }[ChannelType];
|
|
90
91
|
interface HttpLens {
|
|
91
92
|
user: APIUser;
|
|
92
93
|
member: APIInteractionDataResolvedGuildMember;
|
|
@@ -117,7 +118,7 @@ interface ResolvedEmoji extends APIMessageComponentEmoji {
|
|
|
117
118
|
toString(): string;
|
|
118
119
|
}
|
|
119
120
|
/** The global {@link Emojis} accessor type. */
|
|
120
|
-
type InjectedEmojiMap = { [K in keyof EmojiMap]: ResolvedEmoji };
|
|
121
|
+
type InjectedEmojiMap = { [K in keyof EmojiMap]: ResolvedEmoji; };
|
|
121
122
|
/**
|
|
122
123
|
* The bot's resolved emojis, keyed by {@link EmojiMap}. Filled by {@link EmojiInjector} during startup.
|
|
123
124
|
* A read before that throws.
|
|
@@ -140,7 +141,7 @@ type SentMessage = APIMessage;
|
|
|
140
141
|
declare class ReplySender extends BaseReplySender<SentMessage> {
|
|
141
142
|
private readonly ref;
|
|
142
143
|
private readonly rest;
|
|
143
|
-
constructor(ref: InteractionRef, rest: REST, routeId: string, bus
|
|
144
|
+
constructor(ref: InteractionRef, rest: REST, routeId: string, bus: Bus);
|
|
144
145
|
protected writeReply(response: ReplyResponse | string, opts?: SendOpts): Promise<SentMessage | undefined>;
|
|
145
146
|
protected writeDefer(opts?: DeferOpts): Promise<void>;
|
|
146
147
|
protected writeDeferUpdate(): Promise<void>;
|
|
@@ -171,7 +172,7 @@ type Repliables = Exclude<ValidInteractionTypes, APIApplicationCommandAutocomple
|
|
|
171
172
|
*
|
|
172
173
|
* @typeParam Event - The repliable interaction type this handler processes
|
|
173
174
|
*/
|
|
174
|
-
declare abstract class RepliableHandler$1<Event extends Repliables> extends RepliableHandler<Event, Core, SentMessage, ReplySender> {
|
|
175
|
+
declare abstract class RepliableHandler$1<Event extends Repliables> extends RepliableHandler<Event, Core, SentMessage, never, ReplySender> {
|
|
175
176
|
/** @internal */
|
|
176
177
|
getEvent(): Event;
|
|
177
178
|
protected buildSender(event: Event, core: Core, routeId: string): ReplySender;
|
|
@@ -203,7 +204,7 @@ declare abstract class InteractionHandler<Event extends Repliables> extends Repl
|
|
|
203
204
|
}
|
|
204
205
|
//#endregion
|
|
205
206
|
//#region src/handlers/interaction/SlashHandler.d.ts
|
|
206
|
-
type SlashMatchArms<Route extends keyof SlashOptionRegistry, Ret> = { [Key in Route]: (options: SlashOptions$1<Key>) => Promisable<Ret
|
|
207
|
+
type SlashMatchArms<Route extends keyof SlashOptionRegistry, Ret> = { [Key in Route]: (options: SlashOptions$1<Key>) => Promisable<Ret>; };
|
|
207
208
|
/**
|
|
208
209
|
* Base class for a chat-input (slash) command handler on the HTTP transport.
|
|
209
210
|
*
|
|
@@ -263,7 +264,7 @@ declare abstract class ContextMenuHandler<Kind extends ContextMenuKind> extends
|
|
|
263
264
|
}
|
|
264
265
|
//#endregion
|
|
265
266
|
//#region src/handlers/interaction/AutocompleteHandler.d.ts
|
|
266
|
-
type FocusedArms<Route extends keyof SlashOptionRegistry, Ret> = { [Name in AutocompletableNames<Route>]: (value: string, respond: (choices: readonly APIApplicationCommandOptionChoice<ChoiceValueOf<EntryFor<Route, Name>>>[]) => Promise<void>) => Promisable<Ret
|
|
267
|
+
type FocusedArms<Route extends keyof SlashOptionRegistry, Ret> = { [Name in AutocompletableNames<Route>]: (value: string, respond: (choices: readonly APIApplicationCommandOptionChoice<ChoiceValueOf<EntryFor<Route, Name>>>[]) => Promise<void>) => Promisable<Ret>; };
|
|
267
268
|
/**
|
|
268
269
|
* Base class for an autocomplete handler on the HTTP transport.
|
|
269
270
|
*
|
|
@@ -559,7 +560,7 @@ interface Ungateable {
|
|
|
559
560
|
type ProvidedContext<TCtor extends AnyHandlerCtor> = InstanceType<TCtor> extends RepliableHandler$1<infer Event> ? InteractionGateContext<Event> : Ungateable;
|
|
560
561
|
type SelectData<Repliable> = Repliable extends APIMessageComponentSelectMenuInteraction ? Repliable['data'] : never;
|
|
561
562
|
type KindName<Ctx> = Ctx extends Ungateable ? 'autocomplete' : Ctx extends GuildPermissionsContext ? 'gateway (guild permissions)' : Ctx extends InteractionGateContext<infer Repliable> ? Repliable extends APIChatInputApplicationCommandInteraction ? 'Slash' : Repliable extends APIMessageApplicationCommandInteraction ? 'MessageMenu' : Repliable extends APIUserApplicationCommandInteraction ? 'UserMenu' : SelectData<Repliable> extends APIMessageStringSelectInteractionData ? 'StringSelect' : SelectData<Repliable> extends APIMessageUserSelectInteractionData ? 'UserSelect' : SelectData<Repliable> extends APIMessageRoleSelectInteractionData ? 'RoleSelect' : SelectData<Repliable> extends APIMessageChannelSelectInteractionData ? 'ChannelSelect' : SelectData<Repliable> extends APIMessageMentionableSelectInteractionData ? 'MentionableSelect' : Repliable extends APIMessageComponentButtonInteraction ? 'Button' : Repliable extends APIModalSubmitInteraction ? 'Modal' : 'an interaction' : 'an agnostic';
|
|
562
|
-
type FitAll<TCtor extends AnyHandlerCtor, Gates extends readonly Gate<GateContextBase>[]> = { [Index in keyof Gates]: GateFitsWith<ProvidedContext<TCtor>, KindName<ProvidedContext<TCtor>>, Gates[Index], KindName<RequiredOf<Gates[Index]
|
|
563
|
+
type FitAll<TCtor extends AnyHandlerCtor, Gates extends readonly Gate<GateContextBase>[]> = { [Index in keyof Gates]: GateFitsWith<ProvidedContext<TCtor>, KindName<ProvidedContext<TCtor>>, Gates[Index], KindName<RequiredOf<Gates[Index]>>>; };
|
|
563
564
|
//#endregion
|
|
564
565
|
//#region src/gates/Gated.d.ts
|
|
565
566
|
/**
|
|
@@ -601,4 +602,4 @@ declare function Gated<const Gates extends NonEmptyTuple<Gate<GateContextBase>>>
|
|
|
601
602
|
declare const version: string;
|
|
602
603
|
//#endregion
|
|
603
604
|
export { createSeedcord as C, HttpEdgeConfig as D, HttpConfig as E, HttpServerConfig as O, SlashOptions$1 as S, Core as T, InteractionHandler as _, CursorSource$1 as a, InjectedEmojiMap as b, Subscriber$1 as c, SelectMenuHandler as d, ButtonHandler as f, SlashHandler as g, ContextMenuHandler as h, ArraySource$1 as i, WebhookLog$1 as l, AutocompleteHandler as m, Gated as n, Paginator as o, ComponentHandler as p, InteractionGateContext as r, PageContext as s, version as t, ModalHandler as u, RepliableHandler$1 as v, EngineContext as w, ResolvedEmoji as x, Emojis as y };
|
|
604
|
-
//# sourceMappingURL=edge.index-
|
|
605
|
+
//# sourceMappingURL=edge.index-CrVektFx.d.mts.map
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import "reflect-metadata";
|
|
2
2
|
import { SeedcordError, validateDiscordToken } from "@seedcord/errors/internal";
|
|
3
3
|
import { Converters, Envapter } from "envapt";
|
|
4
4
|
import { DiscordAPIError, REST } from "@discordjs/rest";
|
|
5
5
|
import { ArraySource, BaseHandler, Bus, CursorSource, DispatchContext, Fault, Notice, PaginatorBase, RepliableHandler, Silence, Subscriber, WebhookLog } from "@seedcord/core";
|
|
6
|
-
import { BaseReplySender, FaultThrottle, GatedMetadataKey, PublishDefault, accessorStore, asError, clearStore, decodeComponentRoute, deferFlags,
|
|
6
|
+
import { BaseReplySender, FaultThrottle, GatedMetadataKey, PublishDefault, accessorStore, asError, clearStore, decodeComponentRoute, deferFlags, guardedAccessor, isEmojiTuple, outcomeFor, prefixOf, queuedMsFor, reportDispatch, reportedWrite, runHandlerGates, sendFlags, slowGateMonitor } from "@seedcord/core/internal";
|
|
7
7
|
import { Logger, paint } from "@seedcord/logger";
|
|
8
8
|
import { MemoryRateLimiter } from "@seedcord/rate-limiter";
|
|
9
9
|
import { ApplicationCommandOptionType, ApplicationCommandType, ComponentType, InteractionResponseType, InteractionType, MessageFlags, RESTJSONErrorCodes, Routes } from "discord-api-types/v10";
|
|
@@ -20,11 +20,12 @@ function wireAllowedMentions(mentions) {
|
|
|
20
20
|
};
|
|
21
21
|
}
|
|
22
22
|
function wireAttachments(files) {
|
|
23
|
-
if (!files?.some((file) => file.description)) return null;
|
|
23
|
+
if (!files?.some((file) => file.description ?? file.title)) return null;
|
|
24
24
|
return files.map((file, index) => ({
|
|
25
25
|
id: index,
|
|
26
|
-
|
|
27
|
-
...file.description && { description: file.description }
|
|
26
|
+
filename: file.name,
|
|
27
|
+
...file.description && { description: file.description },
|
|
28
|
+
...file.title && { title: file.title }
|
|
28
29
|
}));
|
|
29
30
|
}
|
|
30
31
|
function callbackData(reply, flags) {
|
|
@@ -44,7 +45,7 @@ var ReplySender = class extends BaseReplySender {
|
|
|
44
45
|
ref;
|
|
45
46
|
rest;
|
|
46
47
|
constructor(ref, rest, routeId, bus) {
|
|
47
|
-
super(routeId,
|
|
48
|
+
super(routeId, {
|
|
48
49
|
bus,
|
|
49
50
|
interactionId: ref.id
|
|
50
51
|
});
|
|
@@ -109,9 +110,9 @@ var ReplySender = class extends BaseReplySender {
|
|
|
109
110
|
});
|
|
110
111
|
}
|
|
111
112
|
rawFiles(files) {
|
|
112
|
-
return files.map((file
|
|
113
|
-
name: file.name
|
|
114
|
-
data: file.
|
|
113
|
+
return files.map((file) => ({
|
|
114
|
+
name: file.name,
|
|
115
|
+
data: file.data
|
|
115
116
|
}));
|
|
116
117
|
}
|
|
117
118
|
createdMessage(result) {
|
|
@@ -148,11 +149,12 @@ var RepliableHandler$1 = class extends RepliableHandler {
|
|
|
148
149
|
return this.event;
|
|
149
150
|
}
|
|
150
151
|
buildSender(event, core, routeId) {
|
|
151
|
-
|
|
152
|
+
const ref = {
|
|
152
153
|
application_id: event.application_id,
|
|
153
154
|
id: event.id,
|
|
154
155
|
token: event.token
|
|
155
|
-
}
|
|
156
|
+
};
|
|
157
|
+
return new ReplySender(ref, core.rest, routeId, core.bus);
|
|
156
158
|
}
|
|
157
159
|
/**
|
|
158
160
|
* Typed Discord REST API (`@discordjs/core/http-only`) over `core.rest`, one instance per core.
|
|
@@ -311,7 +313,7 @@ function createCore(config, token) {
|
|
|
311
313
|
function isHandlerCtor(value) {
|
|
312
314
|
return typeof value === "function" && value.prototype instanceof BaseHandler;
|
|
313
315
|
}
|
|
314
|
-
const HARMLESS_API_CODES = new Set([
|
|
316
|
+
const HARMLESS_API_CODES = /* @__PURE__ */ new Set([
|
|
315
317
|
RESTJSONErrorCodes.UnknownInteraction,
|
|
316
318
|
RESTJSONErrorCodes.InteractionHasAlreadyBeenAcknowledged,
|
|
317
319
|
RESTJSONErrorCodes.UnknownMessage
|
|
@@ -328,10 +330,11 @@ async function sendGuarded(routeId, send) {
|
|
|
328
330
|
}
|
|
329
331
|
}
|
|
330
332
|
async function respondEmptyChoices(scope) {
|
|
331
|
-
|
|
333
|
+
const telemetry = {
|
|
332
334
|
bus: scope.core.bus,
|
|
333
335
|
interactionId: scope.payload.id
|
|
334
|
-
}
|
|
336
|
+
};
|
|
337
|
+
await reportedWrite(telemetry, scope.routeId, "respond", () => scope.core.rest.post(Routes.interactionCallback(scope.payload.id, scope.payload.token), { body: {
|
|
335
338
|
type: InteractionResponseType.ApplicationCommandAutocompleteResult,
|
|
336
339
|
data: { choices: [] }
|
|
337
340
|
} }));
|
|
@@ -410,7 +413,7 @@ function dispatchReporter(match, payload, core) {
|
|
|
410
413
|
const startedAt = performance.now();
|
|
411
414
|
const queuedMs = queuedMsFor(payload.id);
|
|
412
415
|
return (outcome) => {
|
|
413
|
-
core.bus
|
|
416
|
+
reportDispatch(core.bus, {
|
|
414
417
|
routeId: unhandledRouteId(match),
|
|
415
418
|
interactionId: payload.id,
|
|
416
419
|
kind: match.kind,
|
|
@@ -418,7 +421,7 @@ function dispatchReporter(match, payload, core) {
|
|
|
418
421
|
fallback: match.routeId === null,
|
|
419
422
|
startedAt,
|
|
420
423
|
queuedMs
|
|
421
|
-
})
|
|
424
|
+
});
|
|
422
425
|
};
|
|
423
426
|
}
|
|
424
427
|
async function answer(caught, scope, report, outcome = outcomeFor(caught)) {
|
|
@@ -453,7 +456,9 @@ async function dispatchInteraction(args) {
|
|
|
453
456
|
const report = dispatchReporter(match, payload, core);
|
|
454
457
|
const ctor = await loadHandlerCtor(match, payload, core, report);
|
|
455
458
|
if (!ctor) return null;
|
|
456
|
-
const
|
|
459
|
+
const routeId = unhandledRouteId(match);
|
|
460
|
+
logger().debug(`Processing ${paint.mint.bold(routeId)} with ${paint.mute(ctor.name)}`);
|
|
461
|
+
const dispatch = new DispatchContext(routeId);
|
|
457
462
|
let handler;
|
|
458
463
|
try {
|
|
459
464
|
handler = new ctor(payload, core, dispatch);
|
|
@@ -464,7 +469,7 @@ async function dispatchInteraction(args) {
|
|
|
464
469
|
const scope = {
|
|
465
470
|
core,
|
|
466
471
|
payload,
|
|
467
|
-
routeId
|
|
472
|
+
routeId,
|
|
468
473
|
sender: handler instanceof RepliableHandler$1 ? handler.getSender() : null
|
|
469
474
|
};
|
|
470
475
|
const refusal = await gateRefusal(ctor, match, payload, core);
|
|
@@ -914,12 +919,14 @@ const TIMESTAMP_PATTERN = /^\d+$/;
|
|
|
914
919
|
var ReplayGuard = class {
|
|
915
920
|
seen = /* @__PURE__ */ new Map();
|
|
916
921
|
lastSweep = Date.now();
|
|
917
|
-
|
|
922
|
+
fresh(timestampHeader) {
|
|
923
|
+
if (!TIMESTAMP_PATTERN.test(timestampHeader)) return null;
|
|
924
|
+
const timestampMs = Number(timestampHeader) * MS_PER_SECOND;
|
|
925
|
+
return Math.abs(Date.now() - timestampMs) <= REPLAY_WINDOW_MS ? timestampMs : null;
|
|
926
|
+
}
|
|
927
|
+
unseen(signatureHex, timestampMs) {
|
|
918
928
|
const now = Date.now();
|
|
919
929
|
this.maybeSweep(now);
|
|
920
|
-
if (!TIMESTAMP_PATTERN.test(timestampHeader)) return false;
|
|
921
|
-
const timestampMs = Number(timestampHeader) * MS_PER_SECOND;
|
|
922
|
-
if (Math.abs(now - timestampMs) > REPLAY_WINDOW_MS) return false;
|
|
923
930
|
const rejectUntil = this.seen.get(signatureHex);
|
|
924
931
|
if (rejectUntil !== void 0 && rejectUntil >= now) return false;
|
|
925
932
|
this.seen.set(signatureHex, timestampMs + REPLAY_WINDOW_MS);
|
|
@@ -947,7 +954,8 @@ function hasInteractionType(payload) {
|
|
|
947
954
|
}
|
|
948
955
|
function buildEngine(core, maps) {
|
|
949
956
|
if (!Envapter.has("DISCORD_PUBLIC_KEY")) throw new SeedcordError(SeedcordErrorCode.ConfigMissingPublicKey);
|
|
950
|
-
const
|
|
957
|
+
const publicKey = Envapter.getRequired("DISCORD_PUBLIC_KEY", Converters.String);
|
|
958
|
+
const verifier = new Ed25519Verifier(publicKey);
|
|
951
959
|
const replays = new ReplayGuard();
|
|
952
960
|
const inFlight = /* @__PURE__ */ new Set();
|
|
953
961
|
const logger = new Logger("Engine", { channel: "bot" });
|
|
@@ -971,24 +979,31 @@ function buildEngine(core, maps) {
|
|
|
971
979
|
core.bus[PublishDefault]("unhandledInteractionError", { error });
|
|
972
980
|
}
|
|
973
981
|
const handle = async (request, ctx) => {
|
|
974
|
-
|
|
982
|
+
const [response, outcome] = await respond(request, ctx);
|
|
983
|
+
logger.debug(`${request.method} ${outcome} ${paint.amber(String(response.status))}`);
|
|
984
|
+
return response;
|
|
985
|
+
};
|
|
986
|
+
const respond = async (request, ctx) => {
|
|
987
|
+
if (request.method !== "POST") return [new Response(null, {
|
|
975
988
|
status: METHOD_NOT_ALLOWED,
|
|
976
989
|
headers: { allow: "POST" }
|
|
977
|
-
});
|
|
990
|
+
}), "not-post"];
|
|
978
991
|
const signature = request.headers.get(SIGNATURE_HEADER);
|
|
979
992
|
const timestamp = request.headers.get(TIMESTAMP_HEADER);
|
|
980
|
-
if (signature === null || timestamp === null) return new Response(null, { status: UNAUTHORIZED });
|
|
993
|
+
if (signature === null || timestamp === null) return [new Response(null, { status: UNAUTHORIZED }), "unsigned"];
|
|
994
|
+
const timestampMs = replays.fresh(timestamp);
|
|
995
|
+
if (timestampMs === null) return [new Response(null, { status: UNAUTHORIZED }), "stale"];
|
|
981
996
|
const body = new Uint8Array(await request.arrayBuffer());
|
|
982
|
-
if (!await verifier.verify(signature, timestamp, body)) return new Response(null, { status: UNAUTHORIZED });
|
|
983
|
-
if (!replays.accepts(signature, timestamp)) return new Response(null, { status: UNAUTHORIZED });
|
|
997
|
+
if (!await verifier.verify(signature, timestamp, body)) return [new Response(null, { status: UNAUTHORIZED }), "bad-signature"];
|
|
984
998
|
let payload;
|
|
985
999
|
try {
|
|
986
1000
|
payload = JSON.parse(decoder.decode(body));
|
|
987
1001
|
} catch {
|
|
988
|
-
return new Response(null, { status: BAD_REQUEST });
|
|
1002
|
+
return [new Response(null, { status: BAD_REQUEST }), "bad-body"];
|
|
989
1003
|
}
|
|
990
|
-
if (!hasInteractionType(payload)) return new Response(null, { status: BAD_REQUEST });
|
|
991
|
-
if (payload.type === PING) return Response.json({ type: InteractionResponseType.Pong });
|
|
1004
|
+
if (!hasInteractionType(payload)) return [new Response(null, { status: BAD_REQUEST }), "bad-body"];
|
|
1005
|
+
if (payload.type === PING) return [Response.json({ type: InteractionResponseType.Pong }), "ping"];
|
|
1006
|
+
if (!replays.unseen(signature, timestampMs)) return [new Response(null, { status: UNAUTHORIZED }), "replay"];
|
|
992
1007
|
try {
|
|
993
1008
|
core.bus[PublishDefault]("anyInteraction", { interaction: payload });
|
|
994
1009
|
const match = resolve(maps, payload);
|
|
@@ -996,7 +1011,7 @@ function buildEngine(core, maps) {
|
|
|
996
1011
|
} catch (caught) {
|
|
997
1012
|
rootFault(caught);
|
|
998
1013
|
}
|
|
999
|
-
return new Response(null, { status: ACCEPTED });
|
|
1014
|
+
return [new Response(null, { status: ACCEPTED }), "dispatched"];
|
|
1000
1015
|
};
|
|
1001
1016
|
return {
|
|
1002
1017
|
handle,
|
|
@@ -1020,7 +1035,8 @@ function buildEngine(core, maps) {
|
|
|
1020
1035
|
* `SeedcordError` when either is missing or malformed.
|
|
1021
1036
|
*/
|
|
1022
1037
|
function createSeedcord(config, manifest) {
|
|
1023
|
-
const
|
|
1038
|
+
const token = validateDiscordToken(Envapter.get("DISCORD_BOT_TOKEN"));
|
|
1039
|
+
const core = createCore(config, token);
|
|
1024
1040
|
core.bus.registerDefaults();
|
|
1025
1041
|
registerSubscribers(core.bus, manifest);
|
|
1026
1042
|
return buildEngine(core, buildRouteMaps(manifest)).handle;
|
|
@@ -1466,8 +1482,8 @@ function Gated(...gates) {
|
|
|
1466
1482
|
//#endregion
|
|
1467
1483
|
//#region src/version.ts
|
|
1468
1484
|
/** Package version */
|
|
1469
|
-
const version = "0.
|
|
1485
|
+
const version = "0.2.0-next.0";
|
|
1470
1486
|
|
|
1471
1487
|
//#endregion
|
|
1472
1488
|
export { RepliableHandler$1 as S, createSeedcord as _, Paginator as a, InteractionHandler as b, ModalHandler as c, ComponentHandler as d, ContextMenuHandler as f, fetchApplicationId as g, Emojis as h, CursorSource$1 as i, SelectMenuHandler as l, EmojiInjector as m, Gated as n, Subscriber$1 as o, SlashHandler as p, ArraySource$1 as r, WebhookLog$1 as s, version as t, ButtonHandler as u, buildEngine as v, AutocompleteHandler as x, buildRouteMaps as y };
|
|
1473
|
-
//# sourceMappingURL=edge.index-
|
|
1489
|
+
//# sourceMappingURL=edge.index-DPLzbHBG.mjs.map
|