@went.tf/discord-bot-framework 2.9.1 → 2.10.1
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/README.md +18 -0
- package/build/.tsbuildinfo +1 -1
- package/build/interactions/dispatch.d.ts +6 -0
- package/build/interactions/dispatch.d.ts.map +1 -1
- package/build/interactions/dispatch.js +23 -10
- package/build/interactions/dispatch.js.map +1 -1
- package/build/logger/discord-webhook-batcher.d.ts.map +1 -1
- package/build/logger/discord-webhook-batcher.js +16 -2
- package/build/logger/discord-webhook-batcher.js.map +1 -1
- package/build/webhook/handle-webhook-interaction-request.d.ts +16 -0
- package/build/webhook/handle-webhook-interaction-request.d.ts.map +1 -1
- package/build/webhook/handle-webhook-interaction-request.js +7 -3
- package/build/webhook/handle-webhook-interaction-request.js.map +1 -1
- package/build/webhook/index.d.ts +1 -0
- package/build/webhook/index.d.ts.map +1 -1
- package/build/webhook/index.js +1 -0
- package/build/webhook/index.js.map +1 -1
- package/build/webhook/is-discord-signature-conformance-check.d.ts +21 -0
- package/build/webhook/is-discord-signature-conformance-check.d.ts.map +1 -0
- package/build/webhook/is-discord-signature-conformance-check.js +43 -0
- package/build/webhook/is-discord-signature-conformance-check.js.map +1 -0
- package/package.json +1 -1
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { AutocompleteInteraction, ChatInputCommandInteraction, MessageComponentInteraction, ModalSubmitInteraction, UserContextMenuCommandInteraction, MessageContextMenuCommandInteraction } from 'discord.js';
|
|
2
2
|
import { BaseInteractionContext, BotChatInputCommand, BotContextMenuCommand, BotMessageComponent, BotModal } from './types.js';
|
|
3
3
|
export type OnDispatchError<Ctx> = (interaction: unknown, context: Ctx, error: unknown) => void | Promise<void>;
|
|
4
|
+
/**
|
|
5
|
+
* Discord invalidates an interaction's token if it isn't acknowledged within 3 seconds of being
|
|
6
|
+
* sent (or when a newer autocomplete interaction supersedes it), after which every response to it
|
|
7
|
+
* fails with this error - including the error reply `onError` would otherwise attempt.
|
|
8
|
+
*/
|
|
9
|
+
export declare const isUnknownInteractionError: (e: unknown) => boolean;
|
|
4
10
|
export interface DispatchChatInputCommandOptions<Ctx extends BaseInteractionContext> {
|
|
5
11
|
commands: Record<string, BotChatInputCommand<Ctx>>;
|
|
6
12
|
onUnknownCommand?: (interaction: ChatInputCommandInteraction, context: Ctx) => void | Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../../src/interactions/dispatch.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../../src/interactions/dispatch.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,22 @@
|
|
|
1
|
+
import { RESTJSONErrorCodes } from 'discord-api-types/v10';
|
|
2
|
+
import { DiscordAPIError, } from 'discord.js';
|
|
1
3
|
import { parseCustomIdSegments } from './custom-id.js';
|
|
4
|
+
/**
|
|
5
|
+
* Discord invalidates an interaction's token if it isn't acknowledged within 3 seconds of being
|
|
6
|
+
* sent (or when a newer autocomplete interaction supersedes it), after which every response to it
|
|
7
|
+
* fails with this error - including the error reply `onError` would otherwise attempt.
|
|
8
|
+
*/
|
|
9
|
+
export const isUnknownInteractionError = (e) => e instanceof DiscordAPIError && e.code === RESTJSONErrorCodes.UnknownInteraction;
|
|
10
|
+
async function handleDispatchError(interaction, context, onError, error, description) {
|
|
11
|
+
if (isUnknownInteractionError(error)) {
|
|
12
|
+
// Nothing can be sent for an expired interaction, so there's no point in a stack trace or an
|
|
13
|
+
// error reply - the age is what tells a slow handler apart from a late delivery by Discord.
|
|
14
|
+
context.logger.warn(`Interaction expired before it could be responded to: ${description}, age=${Date.now() - interaction.createdTimestamp}ms`);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
context.logger.error(`Error while responding to ${description}`, error);
|
|
18
|
+
await onError(interaction, context, error);
|
|
19
|
+
}
|
|
2
20
|
export async function dispatchChatInputCommand(interaction, context, options) {
|
|
3
21
|
const command = options.commands[interaction.commandName];
|
|
4
22
|
if (!command) {
|
|
@@ -12,8 +30,7 @@ export async function dispatchChatInputCommand(interaction, context, options) {
|
|
|
12
30
|
await command.handle(interaction, context);
|
|
13
31
|
}
|
|
14
32
|
catch (e) {
|
|
15
|
-
|
|
16
|
-
await options.onError(interaction, context, e);
|
|
33
|
+
await handleDispatchError(interaction, context, options.onError, e, `command interaction (commandName=${interaction.commandName})`);
|
|
17
34
|
}
|
|
18
35
|
}
|
|
19
36
|
export async function dispatchAutocomplete(interaction, context, options) {
|
|
@@ -27,8 +44,7 @@ export async function dispatchAutocomplete(interaction, context, options) {
|
|
|
27
44
|
await handler(interaction, context, focusedOption.name);
|
|
28
45
|
}
|
|
29
46
|
catch (e) {
|
|
30
|
-
|
|
31
|
-
await options.onError(interaction, context, e);
|
|
47
|
+
await handleDispatchError(interaction, context, options.onError, e, `command autocomplete (commandName=${interaction.commandName})`);
|
|
32
48
|
}
|
|
33
49
|
}
|
|
34
50
|
export async function dispatchComponent(interaction, context, options) {
|
|
@@ -45,8 +61,7 @@ export async function dispatchComponent(interaction, context, options) {
|
|
|
45
61
|
await component.handle(interaction, context, resourceId);
|
|
46
62
|
}
|
|
47
63
|
catch (e) {
|
|
48
|
-
|
|
49
|
-
await options.onError(interaction, context, e);
|
|
64
|
+
await handleDispatchError(interaction, context, options.onError, e, `component interaction (customId=${id},resourceId=${resourceId})`);
|
|
50
65
|
}
|
|
51
66
|
}
|
|
52
67
|
export async function dispatchModal(interaction, context, options) {
|
|
@@ -63,8 +78,7 @@ export async function dispatchModal(interaction, context, options) {
|
|
|
63
78
|
await modal.handle(interaction, context, resourceId);
|
|
64
79
|
}
|
|
65
80
|
catch (e) {
|
|
66
|
-
|
|
67
|
-
await options.onError(interaction, context, e);
|
|
81
|
+
await handleDispatchError(interaction, context, options.onError, e, `modal submit interaction (customId=${id},resourceId=${resourceId})`);
|
|
68
82
|
}
|
|
69
83
|
}
|
|
70
84
|
export async function dispatchContextMenu(interaction, context, options) {
|
|
@@ -80,8 +94,7 @@ export async function dispatchContextMenu(interaction, context, options) {
|
|
|
80
94
|
await command.handle(interaction, context);
|
|
81
95
|
}
|
|
82
96
|
catch (e) {
|
|
83
|
-
|
|
84
|
-
await options.onError(interaction, context, e);
|
|
97
|
+
await handleDispatchError(interaction, context, options.onError, e, `context menu interaction (commandName=${interaction.commandName})`);
|
|
85
98
|
}
|
|
86
99
|
}
|
|
87
100
|
//# sourceMappingURL=dispatch.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatch.js","sourceRoot":"","sources":["../../src/interactions/dispatch.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"dispatch.js","sourceRoot":"","sources":["../../src/interactions/dispatch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAIL,eAAe,GAKhB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAWvD;;;;GAIG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAU,EAAW,EAAE,CAC/D,CAAC,YAAY,eAAe,IAAI,CAAC,CAAC,IAAI,KAAK,kBAAkB,CAAC,kBAAkB,CAAC;AAEnF,KAAK,UAAU,mBAAmB,CAChC,WAA4B,EAC5B,OAAY,EACZ,OAA6B,EAC7B,KAAc,EACd,WAAmB;IAEnB,IAAI,yBAAyB,CAAC,KAAK,CAAC,EAAE,CAAC;QACrC,6FAA6F;QAC7F,4FAA4F;QAC5F,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,wDAAwD,WAAW,SAAS,IAAI,CAAC,GAAG,EAAE,GAAG,WAAW,CAAC,gBAAgB,IAAI,CAAC,CAAC;QAC/I,OAAO;IACT,CAAC;IACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,6BAA6B,WAAW,EAAE,EAAE,KAAK,CAAC,CAAC;IACxE,MAAM,OAAO,CAAC,WAAW,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;AAC7C,CAAC;AAQD,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,WAAwC,EACxC,OAAY,EACZ,OAA6C;IAE7C,MAAM,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;IAC1D,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;YAC7B,MAAM,OAAO,CAAC,gBAAgB,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;YACrD,OAAO;QACT,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,mBAAmB,WAAW,CAAC,WAAW,EAAE,CAAC,CAAC;IAChE,CAAC;IAED,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAC7C,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,mBAAmB,CAAC,WAAW,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,EAAE,oCAAoC,WAAW,CAAC,WAAW,GAAG,CAAC,CAAC;IACtI,CAAC;AACH,CAAC;AAOD,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,WAAoC,EACpC,OAAY,EACZ,OAAyC;IAEzC,MAAM,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;IAC1D,IAAI,CAAC;QACH,MAAM,aAAa,GAAG,WAAW,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAC3D,MAAM,OAAO,GAAG,OAAO,EAAE,YAAY,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;QAC5D,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,+BAA+B,aAAa,CAAC,IAAI,gBAAgB,WAAW,CAAC,WAAW,EAAE,CAAC,CAAC;QAC9G,CAAC;QACD,MAAM,OAAO,CAAC,WAAW,EAAE,OAAO,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC;IAC1D,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,mBAAmB,CAAC,WAAW,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,EAAE,qCAAqC,WAAW,CAAC,WAAW,GAAG,CAAC,CAAC;IACvI,CAAC;AACH,CAAC;AASD,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,WAAwC,EACxC,OAAY,EACZ,OAAsC;IAEtC,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,GAAG,qBAAqB,CAAC,WAAW,CAAC,QAAQ,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAClG,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;IACzC,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,IAAI,OAAO,CAAC,kBAAkB,EAAE,CAAC;YAC/B,MAAM,OAAO,CAAC,kBAAkB,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;YACvD,OAAO;QACT,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,8BAA8B,EAAE,EAAE,CAAC,CAAC;IACtD,CAAC;IAED,IAAI,CAAC;QACH,MAAM,SAAS,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;IAC3D,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,mBAAmB,CAAC,WAAW,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,EAAE,mCAAmC,EAAE,eAAe,UAAU,GAAG,CAAC,CAAC;IACzI,CAAC;AACH,CAAC;AASD,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,WAAmC,EACnC,OAAY,EACZ,OAAkC;IAElC,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,GAAG,qBAAqB,CAAC,WAAW,CAAC,QAAQ,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAClG,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACjC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;YAC3B,MAAM,OAAO,CAAC,cAAc,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;YACnD,OAAO;QACT,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,0BAA0B,EAAE,EAAE,CAAC,CAAC;IAClD,CAAC;IAED,IAAI,CAAC;QACH,MAAM,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;IACvD,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,mBAAmB,CAAC,WAAW,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,EAAE,sCAAsC,EAAE,eAAe,UAAU,GAAG,CAAC,CAAC;IAC5I,CAAC;AACH,CAAC;AAQD,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,WAAqF,EACrF,OAAY,EACZ,OAAwC;IAExC,MAAM,OAAO,GAAG,OAAO,CAAC,mBAAmB,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;IACrE,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;YAC7B,MAAM,OAAO,CAAC,gBAAgB,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;YACrD,OAAO;QACT,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,gCAAgC,WAAW,CAAC,WAAW,EAAE,CAAC,CAAC;IAC7E,CAAC;IAED,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAC7C,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,mBAAmB,CAAC,WAAW,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,EAAE,yCAAyC,WAAW,CAAC,WAAW,GAAG,CAAC,CAAC;IAC3I,CAAC;AACH,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discord-webhook-batcher.d.ts","sourceRoot":"","sources":["../../src/logger/discord-webhook-batcher.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,4BAA4B;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qBAAqB;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kBAAkB;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mBAAmB;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,6DAA6D;IAC7D,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;
|
|
1
|
+
{"version":3,"file":"discord-webhook-batcher.d.ts","sourceRoot":"","sources":["../../src/logger/discord-webhook-batcher.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,4BAA4B;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qBAAqB;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kBAAkB;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mBAAmB;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,6DAA6D;IAC7D,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAoBD;;;;;;GAMG;AACH,qBAAa,qBAAqB;IAOpB,OAAO,CAAC,QAAQ,CAAC,OAAO;IANpC,OAAO,CAAC,KAAK,CAAuB;IAEpC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiC;IAEvD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;gBAEZ,OAAO,EAAE,4BAA4B;IASlE,KAAK,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI;IAY5B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAkCtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAI7B"}
|
|
@@ -2,6 +2,9 @@ const DEFAULT_BATCH_INTERVAL_MS = 20_000;
|
|
|
2
2
|
const DEFAULT_MAX_BATCH_SIZE = 10;
|
|
3
3
|
const DEFAULT_MAX_QUEUE_LENGTH = 500;
|
|
4
4
|
const EMBED_DESCRIPTION_LIMIT = 4096;
|
|
5
|
+
// Discord rejects (HTTP 400) a message whose embeds' combined text exceeds this, even when every
|
|
6
|
+
// individual embed is within its own limits - a few long stack traces in one batch is enough.
|
|
7
|
+
const EMBEDS_TOTAL_TEXT_LIMIT = 6000;
|
|
5
8
|
// Discord embed side-bar colors, keyed by pino's numeric levels (trace=10 ... fatal=60).
|
|
6
9
|
const LEVEL_COLORS = {
|
|
7
10
|
10: 0x95a5a6,
|
|
@@ -47,11 +50,22 @@ export class DiscordWebhookBatcher {
|
|
|
47
50
|
if (this.queue.length === 0)
|
|
48
51
|
return;
|
|
49
52
|
const maxBatchSize = this.options.maxBatchSize ?? DEFAULT_MAX_BATCH_SIZE;
|
|
50
|
-
const
|
|
53
|
+
const embeds = [];
|
|
54
|
+
let totalTextLength = 0;
|
|
55
|
+
while (embeds.length < maxBatchSize && this.queue.length > 0) {
|
|
56
|
+
const embed = buildEmbed(this.queue[0]);
|
|
57
|
+
// The first embed always fits on its own (EMBED_DESCRIPTION_LIMIT < EMBEDS_TOTAL_TEXT_LIMIT),
|
|
58
|
+
// anything past the combined limit is carried over to the next tick.
|
|
59
|
+
if (embeds.length > 0 && totalTextLength + embed.description.length > EMBEDS_TOTAL_TEXT_LIMIT)
|
|
60
|
+
break;
|
|
61
|
+
this.queue.shift();
|
|
62
|
+
embeds.push(embed);
|
|
63
|
+
totalTextLength += embed.description.length;
|
|
64
|
+
}
|
|
51
65
|
const payload = {
|
|
52
66
|
username: this.options.username,
|
|
53
67
|
avatar_url: this.options.avatarUrl,
|
|
54
|
-
embeds
|
|
68
|
+
embeds,
|
|
55
69
|
};
|
|
56
70
|
try {
|
|
57
71
|
const response = await this.fetchImpl(this.options.url, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discord-webhook-batcher.js","sourceRoot":"","sources":["../../src/logger/discord-webhook-batcher.ts"],"names":[],"mappings":"AAsBA,MAAM,yBAAyB,GAAG,MAAM,CAAC;AACzC,MAAM,sBAAsB,GAAG,EAAE,CAAC;AAClC,MAAM,wBAAwB,GAAG,GAAG,CAAC;AACrC,MAAM,uBAAuB,GAAG,IAAI,CAAC;AAErC,yFAAyF;AACzF,MAAM,YAAY,GAA2B;IAC3C,EAAE,EAAE,QAAQ;IACZ,EAAE,EAAE,QAAQ;IACZ,EAAE,EAAE,QAAQ;IACZ,EAAE,EAAE,QAAQ;IACZ,EAAE,EAAE,QAAQ;IACZ,EAAE,EAAE,QAAQ;CACb,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,OAAO,qBAAqB;IAOH;IANrB,KAAK,GAAoB,EAAE,CAAC;IAEnB,KAAK,CAAiC;IAEtC,SAAS,CAAe;IAEzC,YAA6B,OAAqC;QAArC,YAAO,GAAP,OAAO,CAA8B;QAChE,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,KAAK,CAAC;QAC5C,MAAM,UAAU,GAAG,OAAO,CAAC,eAAe,IAAI,yBAAyB,CAAC;QACxE,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;YAC5B,KAAK,IAAI,CAAC,KAAK,EAAE,CAAC;QACpB,CAAC,EAAE,UAAU,CAAC,CAAC;QACf,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,MAAqB;QACzB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACxB,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,IAAI,wBAAwB,CAAC;QAC/E,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,cAAc,EAAE,CAAC;YACvC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,cAAc,CAAC;YACnD,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;YAC9B,2EAA2E;YAC3E,4CAA4C;YAC5C,OAAO,CAAC,KAAK,CAAC,8CAA8C,cAAc,qBAAqB,OAAO,wBAAwB,CAAC,CAAC;QAClI,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QACpC,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,IAAI,sBAAsB,CAAC;QACzE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,
|
|
1
|
+
{"version":3,"file":"discord-webhook-batcher.js","sourceRoot":"","sources":["../../src/logger/discord-webhook-batcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -55,6 +55,22 @@ export interface HandleWebhookInteractionRequestOptions {
|
|
|
55
55
|
* option values, etc.).
|
|
56
56
|
*/
|
|
57
57
|
verboseSignatureDiagnostics?: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* This application's Discord application/client ID. Only used to narrow
|
|
60
|
+
* `muteKnownConformanceCheckLogs` below - optional, but recommended when set.
|
|
61
|
+
*/
|
|
62
|
+
applicationId?: string;
|
|
63
|
+
/**
|
|
64
|
+
* Mutes the `warn`/`debug` signature-rejection logs for Discord's own known
|
|
65
|
+
* signature-conformance check (see `isDiscordSignatureConformanceCheck`) - a
|
|
66
|
+
* recurring, benign `Ping` sent with a deliberately-invalid signature,
|
|
67
|
+
* observed in production to otherwise show up as ordinary-looking
|
|
68
|
+
* signature-rejection noise roughly hourly. On by default, since it's
|
|
69
|
+
* expected and doesn't indicate a problem; the request is still rejected
|
|
70
|
+
* the same either way - this only affects whether that one case logs.
|
|
71
|
+
* @default true
|
|
72
|
+
*/
|
|
73
|
+
muteKnownConformanceCheckLogs?: boolean;
|
|
58
74
|
}
|
|
59
75
|
/**
|
|
60
76
|
* Framework-agnostic core of an HTTP Interactions endpoint: verifies the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handle-webhook-interaction-request.d.ts","sourceRoot":"","sources":["../../src/webhook/handle-webhook-interaction-request.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAA4C,MAAM,uBAAuB,CAAC;AACzH,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"handle-webhook-interaction-request.d.ts","sourceRoot":"","sources":["../../src/webhook/handle-webhook-interaction-request.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAA4C,MAAM,uBAAuB,CAAC;AACzH,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAIpD,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;IACzB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;CAC9C;AAED,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,sBAAsB,GAAG;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;CAC1E;AAED,MAAM,WAAW,sCAAsC;IACrD,oGAAoG;IACpG,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,cAAc,CAAC;IACvB;;;;;;;;;;;;;;;;OAgBG;IACH,aAAa,EAAE,CAAC,WAAW,EAAE,cAAc,KAAK,sBAAsB,GAAG,IAAI,GAAG,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAAC,CAAC;IACvH;;;;;;;;;;OAUG;IACH,2BAA2B,CAAC,EAAE,OAAO,CAAC;IACtC;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;;;;OASG;IACH,6BAA6B,CAAC,EAAE,OAAO,CAAC;CACzC;AA6CD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,+BAA+B,CACnD,OAAO,EAAE,yBAAyB,EAClC,OAAO,EAAE,sCAAsC,GAC9C,OAAO,CAAC,0BAA0B,CAAC,CAoDrC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { InteractionResponseType, InteractionType } from 'discord-api-types/v10';
|
|
3
3
|
import { verifyInteractionRequest } from './verify-interaction-request.js';
|
|
4
|
+
import { isDiscordSignatureConformanceCheck } from './is-discord-signature-conformance-check.js';
|
|
4
5
|
/**
|
|
5
6
|
* Best-effort source IP for the diagnostic log below - checks the headers a
|
|
6
7
|
* request sitting behind a reverse proxy (Cloudflare, nginx, ...) actually
|
|
@@ -63,7 +64,7 @@ function verboseSignatureDiagnostics(request) {
|
|
|
63
64
|
* fields; omit it and those two just come back `undefined`.
|
|
64
65
|
*/
|
|
65
66
|
export async function handleWebhookInteractionRequest(request, options) {
|
|
66
|
-
const { publicKey, logger, onInteraction, verboseSignatureDiagnostics: verbose = false } = options;
|
|
67
|
+
const { publicKey, logger, onInteraction, verboseSignatureDiagnostics: verbose = false, applicationId, muteKnownConformanceCheckLogs = true, } = options;
|
|
67
68
|
const isValid = verifyInteractionRequest({
|
|
68
69
|
publicKey,
|
|
69
70
|
signature: request.signature,
|
|
@@ -71,8 +72,11 @@ export async function handleWebhookInteractionRequest(request, options) {
|
|
|
71
72
|
rawBody: request.rawBody,
|
|
72
73
|
});
|
|
73
74
|
if (!isValid) {
|
|
74
|
-
|
|
75
|
-
|
|
75
|
+
const rejectionLogger = muteKnownConformanceCheckLogs && isDiscordSignatureConformanceCheck(request.rawBody, applicationId)
|
|
76
|
+
? logger.muteMethods(['warn', 'debug'])
|
|
77
|
+
: logger;
|
|
78
|
+
rejectionLogger.warn('Rejected webhook interaction request with invalid signature');
|
|
79
|
+
rejectionLogger.debug('Webhook interaction signature-rejection diagnostics', {
|
|
76
80
|
sourceIp: resolveSourceIp(request.headers),
|
|
77
81
|
userAgent: request.headers?.['user-agent'],
|
|
78
82
|
hasSignatureHeader: request.signature !== undefined,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handle-webhook-interaction-request.js","sourceRoot":"","sources":["../../src/webhook/handle-webhook-interaction-request.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAA0C,uBAAuB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAEzH,OAAO,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"handle-webhook-interaction-request.js","sourceRoot":"","sources":["../../src/webhook/handle-webhook-interaction-request.ts"],"names":[],"mappings":"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"}
|
package/build/webhook/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export * from './verify-interaction-request.js';
|
|
2
|
+
export * from './is-discord-signature-conformance-check.js';
|
|
2
3
|
export * from './handle-webhook-interaction-request.js';
|
|
3
4
|
export * from './create-webhook-interaction-responder.js';
|
|
4
5
|
export * from './create-webhook-only-client.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/webhook/index.ts"],"names":[],"mappings":"AAKA,cAAc,iCAAiC,CAAC;AAChD,cAAc,yCAAyC,CAAC;AACxD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,iCAAiC,CAAC;AAChD,cAAc,uCAAuC,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/webhook/index.ts"],"names":[],"mappings":"AAKA,cAAc,iCAAiC,CAAC;AAChD,cAAc,6CAA6C,CAAC;AAC5D,cAAc,yCAAyC,CAAC;AACxD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,iCAAiC,CAAC;AAChD,cAAc,uCAAuC,CAAC"}
|
package/build/webhook/index.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
// `handleWebhookInteractionRequest`/`interactionFromWebhookPayload` are
|
|
5
5
|
// unverified against live Discord traffic.
|
|
6
6
|
export * from './verify-interaction-request.js';
|
|
7
|
+
export * from './is-discord-signature-conformance-check.js';
|
|
7
8
|
export * from './handle-webhook-interaction-request.js';
|
|
8
9
|
export * from './create-webhook-interaction-responder.js';
|
|
9
10
|
export * from './create-webhook-only-client.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/webhook/index.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,uEAAuE;AACvE,oEAAoE;AACpE,wEAAwE;AACxE,2CAA2C;AAC3C,cAAc,iCAAiC,CAAC;AAChD,cAAc,yCAAyC,CAAC;AACxD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,iCAAiC,CAAC;AAChD,cAAc,uCAAuC,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/webhook/index.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,uEAAuE;AACvE,oEAAoE;AACpE,wEAAwE;AACxE,2CAA2C;AAC3C,cAAc,iCAAiC,CAAC;AAChD,cAAc,6CAA6C,CAAC;AAC5D,cAAc,yCAAyC,CAAC;AACxD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,iCAAiC,CAAC;AAChD,cAAc,uCAAuC,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recognizes a recurring, confirmed-benign pattern seen in production: a `Ping` (type 1)
|
|
3
|
+
* interaction payload, "from" Discord's own official system user, deliberately signed with a
|
|
4
|
+
* signature that doesn't verify against the app's public key. Everything else about it is genuine
|
|
5
|
+
* (real Discord source IP, `Discord-Interactions/1.0` UA, correct `application_id`) - manually
|
|
6
|
+
* re-verifying a captured sample confirmed the signature itself is the only thing wrong, and
|
|
7
|
+
* Discord confirms an application's public key can't be rotated, so a stale-key explanation is
|
|
8
|
+
* ruled out too. Most plausible explanation left: Discord intentionally sends a mis-signed `Ping`
|
|
9
|
+
* to verify endpoints actually reject bad signatures rather than trusting anything that looks like
|
|
10
|
+
* a Discord request. Rejecting it is correct either way - this only exists so
|
|
11
|
+
* `handleWebhookInteractionRequest` can recognize it and quiet its own expected, recurring
|
|
12
|
+
* rejection logs for just this case, without changing the rejection itself.
|
|
13
|
+
*
|
|
14
|
+
* Deliberately checks the interaction's shape, not a fixed byte length - a specific size is
|
|
15
|
+
* incidental and could shift if Discord adds/removes a field, but `type` + the official system
|
|
16
|
+
* user is what actually identifies this case. `applicationId` is optional extra narrowing (a
|
|
17
|
+
* request that already failed signature verification could in principle mimic this exact shape);
|
|
18
|
+
* omit it to match on shape alone.
|
|
19
|
+
*/
|
|
20
|
+
export declare function isDiscordSignatureConformanceCheck(rawBody: string | Buffer, applicationId?: string): boolean;
|
|
21
|
+
//# sourceMappingURL=is-discord-signature-conformance-check.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"is-discord-signature-conformance-check.d.ts","sourceRoot":"","sources":["../../src/webhook/is-discord-signature-conformance-check.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,kCAAkC,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAiB5G"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/** Discord's own official system user - the `user` on the payload this recognizes below. */
|
|
2
|
+
const DISCORD_SYSTEM_USER_ID = '643945264868098049';
|
|
3
|
+
/**
|
|
4
|
+
* Recognizes a recurring, confirmed-benign pattern seen in production: a `Ping` (type 1)
|
|
5
|
+
* interaction payload, "from" Discord's own official system user, deliberately signed with a
|
|
6
|
+
* signature that doesn't verify against the app's public key. Everything else about it is genuine
|
|
7
|
+
* (real Discord source IP, `Discord-Interactions/1.0` UA, correct `application_id`) - manually
|
|
8
|
+
* re-verifying a captured sample confirmed the signature itself is the only thing wrong, and
|
|
9
|
+
* Discord confirms an application's public key can't be rotated, so a stale-key explanation is
|
|
10
|
+
* ruled out too. Most plausible explanation left: Discord intentionally sends a mis-signed `Ping`
|
|
11
|
+
* to verify endpoints actually reject bad signatures rather than trusting anything that looks like
|
|
12
|
+
* a Discord request. Rejecting it is correct either way - this only exists so
|
|
13
|
+
* `handleWebhookInteractionRequest` can recognize it and quiet its own expected, recurring
|
|
14
|
+
* rejection logs for just this case, without changing the rejection itself.
|
|
15
|
+
*
|
|
16
|
+
* Deliberately checks the interaction's shape, not a fixed byte length - a specific size is
|
|
17
|
+
* incidental and could shift if Discord adds/removes a field, but `type` + the official system
|
|
18
|
+
* user is what actually identifies this case. `applicationId` is optional extra narrowing (a
|
|
19
|
+
* request that already failed signature verification could in principle mimic this exact shape);
|
|
20
|
+
* omit it to match on shape alone.
|
|
21
|
+
*/
|
|
22
|
+
export function isDiscordSignatureConformanceCheck(rawBody, applicationId) {
|
|
23
|
+
let parsed;
|
|
24
|
+
try {
|
|
25
|
+
parsed = JSON.parse(rawBody.toString());
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
if (typeof parsed !== 'object' || parsed === null)
|
|
31
|
+
return false;
|
|
32
|
+
const body = parsed;
|
|
33
|
+
if (body.type !== 1)
|
|
34
|
+
return false;
|
|
35
|
+
if (applicationId !== undefined && body.application_id !== applicationId)
|
|
36
|
+
return false;
|
|
37
|
+
const user = body.user;
|
|
38
|
+
if (typeof user !== 'object' || user === null)
|
|
39
|
+
return false;
|
|
40
|
+
const { id, system, bot } = user;
|
|
41
|
+
return id === DISCORD_SYSTEM_USER_ID && system === true && bot === true;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=is-discord-signature-conformance-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"is-discord-signature-conformance-check.js","sourceRoot":"","sources":["../../src/webhook/is-discord-signature-conformance-check.ts"],"names":[],"mappings":"AAAA,4FAA4F;AAC5F,MAAM,sBAAsB,GAAG,oBAAoB,CAAC;AAEpD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,kCAAkC,CAAC,OAAwB,EAAE,aAAsB;IACjG,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAChE,MAAM,IAAI,GAAG,MAAiC,CAAC;IAC/C,IAAI,IAAI,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAClC,IAAI,aAAa,KAAK,SAAS,IAAI,IAAI,CAAC,cAAc,KAAK,aAAa;QAAE,OAAO,KAAK,CAAC;IAEvF,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;IACvB,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC5D,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,IAA+B,CAAC;IAC5D,OAAO,EAAE,KAAK,sBAAsB,IAAI,MAAM,KAAK,IAAI,IAAI,GAAG,KAAK,IAAI,CAAC;AAC1E,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@went.tf/discord-bot-framework",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.10.1",
|
|
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",
|