@went.tf/discord-bot-framework 2.4.0 → 2.5.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/build/index.d.ts CHANGED
@@ -4,5 +4,6 @@ export * from './api-client/index.js';
4
4
  export * from './interactions/index.js';
5
5
  export * from './commands/index.js';
6
6
  export * from './client/index.js';
7
+ export * from './webhook/index.js';
7
8
  export * from './utils/index.js';
8
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC"}
package/build/index.js CHANGED
@@ -8,5 +8,6 @@ export * from './api-client/index.js';
8
8
  export * from './interactions/index.js';
9
9
  export * from './commands/index.js';
10
10
  export * from './client/index.js';
11
+ export * from './webhook/index.js';
11
12
  export * from './utils/index.js';
12
13
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,6CAA6C;AAC7C,sCAAsC;AACtC,wCAAwC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,6CAA6C;AAC7C,sCAAsC;AACtC,wCAAwC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC"}
@@ -0,0 +1,33 @@
1
+ import { REST } from '@discordjs/rest';
2
+ import { RESTPatchAPIWebhookWithTokenMessageJSONBody, RESTPostAPIWebhookWithTokenJSONBody, RESTPostAPIWebhookWithTokenResult } from 'discord-api-types/v10';
3
+ export interface CreateWebhookInteractionResponderOptions {
4
+ rest: REST;
5
+ applicationId: string;
6
+ /** The responding interaction's own token (`APIInteraction.token`), valid for 15 minutes after receipt. */
7
+ interactionToken: string;
8
+ }
9
+ export interface WebhookInteractionResponder {
10
+ /** `PATCH /webhooks/{application.id}/{interaction.token}/messages/@original` - edits the initial response (including a deferred one). */
11
+ editReply(body: RESTPatchAPIWebhookWithTokenMessageJSONBody): Promise<unknown>;
12
+ /** `POST /webhooks/{application.id}/{interaction.token}` - sends an additional message after the initial response. */
13
+ followUp(body: RESTPostAPIWebhookWithTokenJSONBody): Promise<RESTPostAPIWebhookWithTokenResult>;
14
+ /** `DELETE /webhooks/{application.id}/{interaction.token}/messages/@original` - deletes the initial response. */
15
+ deleteReply(): Promise<void>;
16
+ }
17
+ /**
18
+ * REST counterpart to editing/following-up on an interaction once it's
19
+ * already been given its initial response - built on `@discordjs/rest` (an
20
+ * existing peer dependency, same client `createCommandRegistrar` already
21
+ * uses) rather than a live discord.js `Client`, since webhook mode has none.
22
+ *
23
+ * This is *not* a drop-in replacement for discord.js's
24
+ * `Interaction#reply()`/`editReply()`/`options.get*()` surface - it only
25
+ * covers what happens after `handleWebhookInteractionRequest`'s synchronous
26
+ * response (the initial `reply()`/`deferReply()` equivalent is just the
27
+ * `APIInteractionResponse` your `onInteraction` callback returns). Mimicking
28
+ * discord.js's full `Interaction` shape closely enough for existing
29
+ * gateway-based handler code to run unmodified against it is real,
30
+ * unvalidated design work - see CLAUDE.md's `./webhook` design-decision entry.
31
+ */
32
+ export declare function createWebhookInteractionResponder(options: CreateWebhookInteractionResponderOptions): WebhookInteractionResponder;
33
+ //# sourceMappingURL=create-webhook-interaction-responder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-webhook-interaction-responder.d.ts","sourceRoot":"","sources":["../../src/webhook/create-webhook-interaction-responder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACvC,OAAO,EAAE,2CAA2C,EAAE,mCAAmC,EAAE,iCAAiC,EAAU,MAAM,uBAAuB,CAAC;AAEpK,MAAM,WAAW,wCAAwC;IACvD,IAAI,EAAE,IAAI,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,2GAA2G;IAC3G,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,2BAA2B;IAC1C,yIAAyI;IACzI,SAAS,CAAC,IAAI,EAAE,2CAA2C,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/E,sHAAsH;IACtH,QAAQ,CAAC,IAAI,EAAE,mCAAmC,GAAG,OAAO,CAAC,iCAAiC,CAAC,CAAC;IAChG,iHAAiH;IACjH,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iCAAiC,CAAC,OAAO,EAAE,wCAAwC,GAAG,2BAA2B,CAQhI"}
@@ -0,0 +1,25 @@
1
+ import { Routes } from 'discord-api-types/v10';
2
+ /**
3
+ * REST counterpart to editing/following-up on an interaction once it's
4
+ * already been given its initial response - built on `@discordjs/rest` (an
5
+ * existing peer dependency, same client `createCommandRegistrar` already
6
+ * uses) rather than a live discord.js `Client`, since webhook mode has none.
7
+ *
8
+ * This is *not* a drop-in replacement for discord.js's
9
+ * `Interaction#reply()`/`editReply()`/`options.get*()` surface - it only
10
+ * covers what happens after `handleWebhookInteractionRequest`'s synchronous
11
+ * response (the initial `reply()`/`deferReply()` equivalent is just the
12
+ * `APIInteractionResponse` your `onInteraction` callback returns). Mimicking
13
+ * discord.js's full `Interaction` shape closely enough for existing
14
+ * gateway-based handler code to run unmodified against it is real,
15
+ * unvalidated design work - see CLAUDE.md's `./webhook` design-decision entry.
16
+ */
17
+ export function createWebhookInteractionResponder(options) {
18
+ const { rest, applicationId, interactionToken } = options;
19
+ return {
20
+ editReply: (body) => rest.patch(Routes.webhookMessage(applicationId, interactionToken, '@original'), { body }),
21
+ followUp: (body) => rest.post(Routes.webhook(applicationId, interactionToken), { body }),
22
+ deleteReply: () => rest.delete(Routes.webhookMessage(applicationId, interactionToken, '@original')),
23
+ };
24
+ }
25
+ //# sourceMappingURL=create-webhook-interaction-responder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-webhook-interaction-responder.js","sourceRoot":"","sources":["../../src/webhook/create-webhook-interaction-responder.ts"],"names":[],"mappings":"AACA,OAAO,EAAuH,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAkBpK;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,iCAAiC,CAAC,OAAiD;IACjG,MAAM,EAAE,IAAI,EAAE,aAAa,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAC;IAE1D,OAAO;QACL,SAAS,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,cAAc,CAAC,aAAa,EAAE,gBAAgB,EAAE,WAAW,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC;QAC9G,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,aAAa,EAAE,gBAAgB,CAAC,EAAE,EAAE,IAAI,EAAE,CAA+C;QACtI,WAAW,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,aAAa,EAAE,gBAAgB,EAAE,WAAW,CAAC,CAAkB;KACrH,CAAC;AACJ,CAAC"}
@@ -0,0 +1,41 @@
1
+ import { APIInteraction, APIInteractionResponse } from 'discord-api-types/v10';
2
+ import { NestableLogger } from '../logger/types.js';
3
+ export interface WebhookInteractionRequest {
4
+ /** The request's `X-Signature-Ed25519` header. */
5
+ signature: string | undefined;
6
+ /** The request's `X-Signature-Timestamp` header. */
7
+ timestamp: string | undefined;
8
+ /** The raw (unparsed) request body, exactly as received - required for signature verification. */
9
+ rawBody: string | Buffer;
10
+ }
11
+ export interface WebhookInteractionResponse {
12
+ status: number;
13
+ body: APIInteractionResponse | {
14
+ error: string;
15
+ };
16
+ }
17
+ export interface HandleWebhookInteractionRequestOptions {
18
+ /** The bot's Ed25519 application public key, from the Discord developer portal, as a hex string. */
19
+ publicKey: string;
20
+ logger: NestableLogger;
21
+ /**
22
+ * Called for every interaction that isn't Discord's own PING handshake
23
+ * check. Its return value becomes the HTTP response body sent back to
24
+ * Discord as the interaction's initial response (e.g. a `CHANNEL_MESSAGE_WITH_SOURCE`
25
+ * or a `DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE` to be followed up on later via
26
+ * `createWebhookInteractionResponder`).
27
+ */
28
+ onInteraction: (interaction: APIInteraction) => APIInteractionResponse | Promise<APIInteractionResponse>;
29
+ }
30
+ /**
31
+ * Framework-agnostic core of an HTTP Interactions endpoint: verifies the
32
+ * request's Ed25519 signature, answers Discord's PING (type 1) validation
33
+ * handshake with PONG directly, and otherwise hands the parsed interaction to
34
+ * `onInteraction`. Deliberately takes/returns plain data (raw body + headers
35
+ * in, `{ status, body }` out) instead of an `http.IncomingMessage`/Express
36
+ * `Request`/etc., so it can be wired into any HTTP layer a bot already uses -
37
+ * this package doesn't otherwise depend on one, and picking one here would be
38
+ * a bigger commitment than this function needs to make.
39
+ */
40
+ export declare function handleWebhookInteractionRequest(request: WebhookInteractionRequest, options: HandleWebhookInteractionRequestOptions): Promise<WebhookInteractionResponse>;
41
+ //# sourceMappingURL=handle-webhook-interaction-request.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handle-webhook-interaction-request.d.ts","sourceRoot":"","sources":["../../src/webhook/handle-webhook-interaction-request.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAA4C,MAAM,uBAAuB,CAAC;AACzH,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAGpD,MAAM,WAAW,yBAAyB;IACxC,kDAAkD;IAClD,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,oDAAoD;IACpD,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,kGAAkG;IAClG,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,sBAAsB,GAAG;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CAClD;AAED,MAAM,WAAW,sCAAsC;IACrD,oGAAoG;IACpG,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,cAAc,CAAC;IACvB;;;;;;OAMG;IACH,aAAa,EAAE,CAAC,WAAW,EAAE,cAAc,KAAK,sBAAsB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAC;CAC1G;AAED;;;;;;;;;GASG;AACH,wBAAsB,+BAA+B,CACnD,OAAO,EAAE,yBAAyB,EAClC,OAAO,EAAE,sCAAsC,GAC9C,OAAO,CAAC,0BAA0B,CAAC,CAiCrC"}
@@ -0,0 +1,45 @@
1
+ import { InteractionResponseType, InteractionType } from 'discord-api-types/v10';
2
+ import { verifyInteractionRequest } from './verify-interaction-request.js';
3
+ /**
4
+ * Framework-agnostic core of an HTTP Interactions endpoint: verifies the
5
+ * request's Ed25519 signature, answers Discord's PING (type 1) validation
6
+ * handshake with PONG directly, and otherwise hands the parsed interaction to
7
+ * `onInteraction`. Deliberately takes/returns plain data (raw body + headers
8
+ * in, `{ status, body }` out) instead of an `http.IncomingMessage`/Express
9
+ * `Request`/etc., so it can be wired into any HTTP layer a bot already uses -
10
+ * this package doesn't otherwise depend on one, and picking one here would be
11
+ * a bigger commitment than this function needs to make.
12
+ */
13
+ export async function handleWebhookInteractionRequest(request, options) {
14
+ const { publicKey, logger, onInteraction } = options;
15
+ const isValid = verifyInteractionRequest({
16
+ publicKey,
17
+ signature: request.signature,
18
+ timestamp: request.timestamp,
19
+ rawBody: request.rawBody,
20
+ });
21
+ if (!isValid) {
22
+ logger.warn('Rejected webhook interaction request with invalid signature');
23
+ return { status: 401, body: { error: 'Invalid request signature' } };
24
+ }
25
+ let interaction;
26
+ try {
27
+ interaction = JSON.parse(request.rawBody.toString());
28
+ }
29
+ catch (e) {
30
+ logger.error('Failed to parse webhook interaction request body as JSON', e);
31
+ return { status: 400, body: { error: 'Invalid JSON body' } };
32
+ }
33
+ if (interaction.type === InteractionType.Ping) {
34
+ return { status: 200, body: { type: InteractionResponseType.Pong } };
35
+ }
36
+ try {
37
+ const body = await onInteraction(interaction);
38
+ return { status: 200, body };
39
+ }
40
+ catch (e) {
41
+ logger.error('Error while handling webhook interaction', e);
42
+ return { status: 500, body: { error: 'Internal error' } };
43
+ }
44
+ }
45
+ //# sourceMappingURL=handle-webhook-interaction-request.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handle-webhook-interaction-request.js","sourceRoot":"","sources":["../../src/webhook/handle-webhook-interaction-request.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0C,uBAAuB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAEzH,OAAO,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC;AA8B3E;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,+BAA+B,CACnD,OAAkC,EAClC,OAA+C;IAE/C,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC;IAErD,MAAM,OAAO,GAAG,wBAAwB,CAAC;QACvC,SAAS;QACT,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,OAAO,EAAE,OAAO,CAAC,OAAO;KACzB,CAAC,CAAC;IACH,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,CAAC,IAAI,CAAC,6DAA6D,CAAC,CAAC;QAC3E,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,2BAA2B,EAAE,EAAE,CAAC;IACvE,CAAC;IAED,IAAI,WAA2B,CAAC;IAChC,IAAI,CAAC;QACH,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAmB,CAAC;IACzE,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,CAAC,KAAK,CAAC,0DAA0D,EAAE,CAAC,CAAC,CAAC;QAC5E,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,mBAAmB,EAAE,EAAE,CAAC;IAC/D,CAAC;IAED,IAAI,WAAW,CAAC,IAAI,KAAK,eAAe,CAAC,IAAI,EAAE,CAAC;QAC9C,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,uBAAuB,CAAC,IAAI,EAAE,EAAE,CAAC;IACvE,CAAC;IAED,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,WAAW,CAAC,CAAC;QAC9C,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;IAC/B,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,CAAC,KAAK,CAAC,0CAA0C,EAAE,CAAC,CAAC,CAAC;QAC5D,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE,EAAE,CAAC;IAC5D,CAAC;AACH,CAAC"}
@@ -0,0 +1,4 @@
1
+ export * from './verify-interaction-request.js';
2
+ export * from './handle-webhook-interaction-request.js';
3
+ export * from './create-webhook-interaction-responder.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/webhook/index.ts"],"names":[],"mappings":"AAIA,cAAc,iCAAiC,CAAC;AAChD,cAAc,yCAAyC,CAAC;AACxD,cAAc,2CAA2C,CAAC"}
@@ -0,0 +1,8 @@
1
+ // Experimental: unvalidated against a real bot migration yet. See this
2
+ // module's design-decision entry in CLAUDE.md before relying on it, in
3
+ // particular that `createWebhookInteractionResponder` is not a drop-in
4
+ // discord.js `Interaction` shim.
5
+ export * from './verify-interaction-request.js';
6
+ export * from './handle-webhook-interaction-request.js';
7
+ export * from './create-webhook-interaction-responder.js';
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/webhook/index.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,uEAAuE;AACvE,uEAAuE;AACvE,iCAAiC;AACjC,cAAc,iCAAiC,CAAC;AAChD,cAAc,yCAAyC,CAAC;AACxD,cAAc,2CAA2C,CAAC"}
@@ -0,0 +1,22 @@
1
+ export interface VerifyInteractionRequestOptions {
2
+ /** The bot's Ed25519 application public key, from the Discord developer portal, as a hex string. */
3
+ publicKey: string;
4
+ /** The request's `X-Signature-Ed25519` header. */
5
+ signature: string | undefined;
6
+ /** The request's `X-Signature-Timestamp` header. */
7
+ timestamp: string | undefined;
8
+ /** The raw (unparsed) request body, exactly as received - verification fails against a re-serialized body. */
9
+ rawBody: string | Buffer;
10
+ }
11
+ /**
12
+ * Verifies a Discord Interactions-webhook request's Ed25519 signature, per
13
+ * https://discord.com/developers/docs/interactions/overview#setting-up-an-endpoint.
14
+ *
15
+ * Built on Node's native `crypto` module (`verify`/`createPublicKey`, both
16
+ * Ed25519-capable since Node 12) instead of `tweetnacl` or
17
+ * `discord-interactions` - matching this package's standing preference for a
18
+ * native API over a new dependency when one suffices (see the discord-webhook
19
+ * pino transport, and `createHandlerWatcher`'s use of native `fs.watch`).
20
+ */
21
+ export declare function verifyInteractionRequest(options: VerifyInteractionRequestOptions): boolean;
22
+ //# sourceMappingURL=verify-interaction-request.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify-interaction-request.d.ts","sourceRoot":"","sources":["../../src/webhook/verify-interaction-request.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,+BAA+B;IAC9C,oGAAoG;IACpG,SAAS,EAAE,MAAM,CAAC;IAClB,kDAAkD;IAClD,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,oDAAoD;IACpD,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,8GAA8G;IAC9G,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;CAC1B;AAED;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,+BAA+B,GAAG,OAAO,CAkB1F"}
@@ -0,0 +1,37 @@
1
+ import { createPublicKey, verify } from 'node:crypto';
2
+ // DER SPKI wrapper for a raw 32-byte Ed25519 public key
3
+ // (302a300506032b6570032100 = SEQUENCE { SEQUENCE { OID 1.3.101.112 }, BIT STRING (32 bytes) }).
4
+ // Node's `crypto.createPublicKey` has no raw-Ed25519-key input format, only DER/PEM/JWK, and
5
+ // Discord's developer portal only ever gives bots the raw 32-byte hex key - this prefix is the
6
+ // standard, fixed way to wrap one into the DER SPKI shape `createPublicKey` accepts.
7
+ const ed25519SpkiDerPrefix = Buffer.from('302a300506032b6570032100', 'hex');
8
+ /**
9
+ * Verifies a Discord Interactions-webhook request's Ed25519 signature, per
10
+ * https://discord.com/developers/docs/interactions/overview#setting-up-an-endpoint.
11
+ *
12
+ * Built on Node's native `crypto` module (`verify`/`createPublicKey`, both
13
+ * Ed25519-capable since Node 12) instead of `tweetnacl` or
14
+ * `discord-interactions` - matching this package's standing preference for a
15
+ * native API over a new dependency when one suffices (see the discord-webhook
16
+ * pino transport, and `createHandlerWatcher`'s use of native `fs.watch`).
17
+ */
18
+ export function verifyInteractionRequest(options) {
19
+ const { publicKey, signature, timestamp, rawBody } = options;
20
+ if (!signature || !timestamp) {
21
+ return false;
22
+ }
23
+ try {
24
+ const key = createPublicKey({
25
+ key: Buffer.concat([ed25519SpkiDerPrefix, Buffer.from(publicKey, 'hex')]),
26
+ format: 'der',
27
+ type: 'spki',
28
+ });
29
+ const message = Buffer.concat([Buffer.from(timestamp, 'utf8'), Buffer.from(rawBody)]);
30
+ return verify(null, message, key, Buffer.from(signature, 'hex'));
31
+ }
32
+ catch {
33
+ // Malformed hex in publicKey/signature, wrong-length key, etc. - all just "not valid".
34
+ return false;
35
+ }
36
+ }
37
+ //# sourceMappingURL=verify-interaction-request.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify-interaction-request.js","sourceRoot":"","sources":["../../src/webhook/verify-interaction-request.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAEtD,wDAAwD;AACxD,iGAAiG;AACjG,6FAA6F;AAC7F,+FAA+F;AAC/F,qFAAqF;AACrF,MAAM,oBAAoB,GAAG,MAAM,CAAC,IAAI,CAAC,0BAA0B,EAAE,KAAK,CAAC,CAAC;AAa5E;;;;;;;;;GASG;AACH,MAAM,UAAU,wBAAwB,CAAC,OAAwC;IAC/E,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IAC7D,IAAI,CAAC,SAAS,IAAI,CAAC,SAAS,EAAE,CAAC;QAC7B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,eAAe,CAAC;YAC1B,GAAG,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,oBAAoB,EAAE,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC;YACzE,MAAM,EAAE,KAAK;YACb,IAAI,EAAE,MAAM;SACb,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACtF,OAAO,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC;IACnE,CAAC;IAAC,MAAM,CAAC;QACP,uFAAuF;QACvF,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@went.tf/discord-bot-framework",
3
- "version": "2.4.0",
3
+ "version": "2.5.0",
4
4
  "description": "Shared framework for building Discord bots with discord.js: logging, env validation, interaction dispatch, API client, and optional Postgres/i18next helpers",
5
5
  "author": "WentTheFox <mail@went.tf> (https://github.com/WentTheFox)",
6
6
  "license": "MIT",
@@ -48,6 +48,10 @@
48
48
  "types": "./build/client/index.d.ts",
49
49
  "default": "./build/client/index.js"
50
50
  },
51
+ "./webhook": {
52
+ "types": "./build/webhook/index.d.ts",
53
+ "default": "./build/webhook/index.js"
54
+ },
51
55
  "./dev": {
52
56
  "types": "./build/dev/index.d.ts",
53
57
  "default": "./build/dev/index.js"