From 3c41e1722faee3f331d0f1bca92bfdaf62ae393d Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 30 May 2026 08:29:10 -0400 Subject: [PATCH] fix(discord): guard timeout expiry dates --- extensions/discord/src/send.creates-thread.test.ts | 13 +++++++++++++ extensions/discord/src/send.guild.ts | 6 +++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/extensions/discord/src/send.creates-thread.test.ts b/extensions/discord/src/send.creates-thread.test.ts index 91c6f3dbf33..b8b21ed3813 100644 --- a/extensions/discord/src/send.creates-thread.test.ts +++ b/extensions/discord/src/send.creates-thread.test.ts @@ -121,6 +121,7 @@ beforeEach(() => { afterEach(() => { vi.useRealTimers(); + vi.restoreAllMocks(); }); afterAll(() => { @@ -322,6 +323,18 @@ describe("sendMessageDiscord", () => { ).toBeTypeOf("string"); }); + it("rejects timeout durations outside Date range", async () => { + const { rest, patchMock } = makeDiscordRest(); + + await expect( + timeoutMemberDiscord( + { guildId: "g1", userId: "u1", durationMinutes: 8_640_000_000_000_001 }, + discordClientOpts(rest), + ), + ).rejects.toThrow("Discord timeout duration is outside the supported Date range"); + expect(patchMock).not.toHaveBeenCalled(); + }); + it("adds and removes roles", async () => { const { rest, putMock, deleteMock } = makeDiscordRest(); putMock.mockResolvedValue({}); diff --git a/extensions/discord/src/send.guild.ts b/extensions/discord/src/send.guild.ts index 69be3a620dd..16f3e2d30cd 100644 --- a/extensions/discord/src/send.guild.ts +++ b/extensions/discord/src/send.guild.ts @@ -6,6 +6,7 @@ import type { APIVoiceState, RESTPostAPIGuildScheduledEventJSONBody, } from "discord-api-types/v10"; +import { timestampMsToIsoString } from "openclaw/plugin-sdk/number-runtime"; import { normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/string-coerce-runtime"; import { loadWebMediaRaw } from "openclaw/plugin-sdk/web-media"; import { @@ -139,7 +140,10 @@ export async function timeoutMemberDiscord( let until = payload.until; if (!until && payload.durationMinutes) { const ms = payload.durationMinutes * 60 * 1000; - until = new Date(Date.now() + ms).toISOString(); + until = timestampMsToIsoString(Date.now() + ms); + if (!until) { + throw new Error("Discord timeout duration is outside the supported Date range"); + } } return await timeoutGuildMember(rest, payload.guildId, payload.userId, { body: { communication_disabled_until: until ?? null },