This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
||||
validInteractionEnvelope,
|
||||
verifiedDiscordBody
|
||||
} from '$lib/server/discord/webhook.js';
|
||||
import { discordCredentials } from '$lib/server/discord/config.js';
|
||||
|
||||
const seen = new Map<string, number>();
|
||||
const actors = new Map<string, number>();
|
||||
@@ -23,11 +24,23 @@ export async function POST({ request }: { request: Request }) {
|
||||
try {
|
||||
const body = await verifiedDiscordBody(request);
|
||||
if (body.type === 1) return json({ type: 1 });
|
||||
if (![2, 3].includes(body.type as number) || !validInteractionEnvelope(body))
|
||||
if (![2, 3].includes(body.type as number) || !validInteractionEnvelope(body)) {
|
||||
if (
|
||||
[2, 3].includes(body.type as number) &&
|
||||
body.application_id !== discordCredentials().applicationId
|
||||
) {
|
||||
console.error(
|
||||
'[discord] Interaction application ID does not match EC_DISCORD_APPLICATION_ID.'
|
||||
);
|
||||
return json(
|
||||
privateReply(
|
||||
'Electric Cherry’s Discord application ID is misconfigured. Ask an administrator to check EC_DISCORD_APPLICATION_ID.'
|
||||
)
|
||||
);
|
||||
}
|
||||
console.warn('[discord] Rejected an invalid interaction envelope.');
|
||||
return json({ error: 'Invalid Discord interaction.' }, { status: 400 });
|
||||
const settings = getDiscordSettings();
|
||||
if (!settings.enabled || body.guild_id !== settings.guildId)
|
||||
return json(privateReply('Electric Cherry is not enabled for this Discord server.'));
|
||||
}
|
||||
const now = Date.now();
|
||||
for (const [key, at] of seen) if (now - at > 5 * 60_000) seen.delete(key);
|
||||
for (const [key, at] of actors) if (now - at > 60_000) actors.delete(key);
|
||||
@@ -36,15 +49,27 @@ export async function POST({ request }: { request: Request }) {
|
||||
if (seen.has(id)) return json({ type: 5, data: { flags: 64 } });
|
||||
if (pending >= 32 || seen.size >= 10_000 || now - (actors.get(actor) ?? 0) < 1000)
|
||||
return json(privateReply('Please wait a moment before trying again.'));
|
||||
if (!claimDiscordInteraction(id)) return json({ type: 5, data: { flags: 64 } });
|
||||
seen.set(id, now);
|
||||
actors.set(actor, now);
|
||||
pending++;
|
||||
// Acknowledge immediately; report queries and Discord delivery must not consume
|
||||
// the three-second response window. Durable watches are saved by the handler.
|
||||
// Do not read or write SQLite before returning the acknowledgement. In particular,
|
||||
// acquiring the replay-claim write lock can outlast Discord's three-second deadline.
|
||||
console.info('[discord] Deferring interaction.', { id, type: body.type });
|
||||
setImmediate(() => {
|
||||
void (async () => {
|
||||
try {
|
||||
if (!claimDiscordInteraction(id)) return;
|
||||
const settings = getDiscordSettings();
|
||||
if (!settings.enabled || body.guild_id !== settings.guildId) {
|
||||
await completeDiscordInteraction(
|
||||
body.application_id as string,
|
||||
body.token as string,
|
||||
privateMessage(
|
||||
'Electric Cherry is not enabled for this Discord server.'
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
const reply = await handleDiscordInteraction(
|
||||
body as unknown as DiscordInteraction
|
||||
);
|
||||
@@ -56,6 +81,7 @@ export async function POST({ request }: { request: Request }) {
|
||||
() => JSON.stringify(getDiscordSettings()) === JSON.stringify(settings)
|
||||
);
|
||||
} catch {
|
||||
console.error('[discord] Deferred interaction failed.', { id });
|
||||
try {
|
||||
await completeDiscordInteraction(
|
||||
body.application_id as string,
|
||||
@@ -74,8 +100,14 @@ export async function POST({ request }: { request: Request }) {
|
||||
});
|
||||
return json({ type: 5, data: { flags: 64 } });
|
||||
} catch (cause) {
|
||||
if (isHttpError(cause))
|
||||
if (isHttpError(cause)) {
|
||||
console.warn('[discord] Interaction request rejected.', {
|
||||
status: cause.status,
|
||||
reason: cause.body.message
|
||||
});
|
||||
return json({ error: cause.body.message }, { status: cause.status });
|
||||
}
|
||||
console.error('[discord] Interaction request failed before acknowledgement.');
|
||||
return json({ error: 'Could not process Discord interaction.' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user