diff --git a/src/telegram/bot.create-telegram-bot.accepts-group-messages-mentionpatterns-match-without-botusername.test.ts b/src/telegram/bot.create-telegram-bot.accepts-group-messages-mentionpatterns-match-without-botusername.test.ts index 1379b6e6f..a648b9511 100644 --- a/src/telegram/bot.create-telegram-bot.accepts-group-messages-mentionpatterns-match-without-botusername.test.ts +++ b/src/telegram/bot.create-telegram-bot.accepts-group-messages-mentionpatterns-match-without-botusername.test.ts @@ -1,8 +1,5 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; -import { - escapeRegExp, - formatLocalEnvelopeTimestamp, -} from "../../test/helpers/envelope-timestamp.js"; +import { escapeRegExp, formatEnvelopeTimestamp } from "../../test/helpers/envelope-timestamp.js"; import { resetInboundDedupe } from "../auto-reply/reply/inbound-dedupe.js"; import { createTelegramBot } from "./bot.js"; @@ -180,7 +177,7 @@ describe("createTelegramBot", () => { expect(payload.WasMentioned).toBe(true); expect(payload.SenderName).toBe("Ada"); expect(payload.SenderId).toBe("9"); - const expectedTimestamp = formatLocalEnvelopeTimestamp(new Date("2025-01-09T00:00:00Z")); + const expectedTimestamp = formatEnvelopeTimestamp(new Date("2025-01-09T00:00:00Z")); const timestampPattern = escapeRegExp(expectedTimestamp); expect(payload.Body).toMatch( new RegExp(`^\\[Telegram Test Group id:7 (\\+\\d+[smhd] )?${timestampPattern}\\]`), @@ -225,7 +222,7 @@ describe("createTelegramBot", () => { expect(payload.SenderName).toBe("Ada Lovelace"); expect(payload.SenderId).toBe("99"); expect(payload.SenderUsername).toBe("ada"); - const expectedTimestamp = formatLocalEnvelopeTimestamp(new Date("2025-01-09T00:00:00Z")); + const expectedTimestamp = formatEnvelopeTimestamp(new Date("2025-01-09T00:00:00Z")); const timestampPattern = escapeRegExp(expectedTimestamp); expect(payload.Body).toMatch( new RegExp(`^\\[Telegram Ops id:42 (\\+\\d+[smhd] )?${timestampPattern}\\]`), diff --git a/src/telegram/bot.create-telegram-bot.installs-grammy-throttler.test.ts b/src/telegram/bot.create-telegram-bot.installs-grammy-throttler.test.ts index 141532796..e27e119f0 100644 --- a/src/telegram/bot.create-telegram-bot.installs-grammy-throttler.test.ts +++ b/src/telegram/bot.create-telegram-bot.installs-grammy-throttler.test.ts @@ -1,8 +1,5 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; -import { - escapeRegExp, - formatLocalEnvelopeTimestamp, -} from "../../test/helpers/envelope-timestamp.js"; +import { escapeRegExp, formatEnvelopeTimestamp } from "../../test/helpers/envelope-timestamp.js"; import { resetInboundDedupe } from "../auto-reply/reply/inbound-dedupe.js"; import { createTelegramBot, getTelegramSequentialKey } from "./bot.js"; import { resolveTelegramFetch } from "./fetch.js"; @@ -332,7 +329,7 @@ describe("createTelegramBot", () => { expect(replySpy).toHaveBeenCalledTimes(1); const payload = replySpy.mock.calls[0][0]; - const expectedTimestamp = formatLocalEnvelopeTimestamp(new Date("2025-01-09T00:00:00Z")); + const expectedTimestamp = formatEnvelopeTimestamp(new Date("2025-01-09T00:00:00Z")); const timestampPattern = escapeRegExp(expectedTimestamp); expect(payload.Body).toMatch( new RegExp( diff --git a/src/telegram/bot.test.ts b/src/telegram/bot.test.ts index 2d04d6aa4..21f52c50d 100644 --- a/src/telegram/bot.test.ts +++ b/src/telegram/bot.test.ts @@ -10,10 +10,7 @@ import { listSkillCommandsForAgents } from "../auto-reply/skill-commands.js"; import { resetInboundDedupe } from "../auto-reply/reply/inbound-dedupe.js"; import * as replyModule from "../auto-reply/reply.js"; import { expectInboundContextContract } from "../../test/helpers/inbound-contract.js"; -import { - escapeRegExp, - formatLocalEnvelopeTimestamp, -} from "../../test/helpers/envelope-timestamp.js"; +import { escapeRegExp, formatEnvelopeTimestamp } from "../../test/helpers/envelope-timestamp.js"; import { createTelegramBot, getTelegramSequentialKey } from "./bot.js"; import { resolveTelegramFetch } from "./fetch.js"; @@ -454,7 +451,7 @@ describe("createTelegramBot", () => { expect(replySpy).toHaveBeenCalledTimes(1); const payload = replySpy.mock.calls[0][0]; - const expectedTimestamp = formatLocalEnvelopeTimestamp(new Date("2025-01-09T00:00:00Z")); + const expectedTimestamp = formatEnvelopeTimestamp(new Date("2025-01-09T00:00:00Z")); const timestampPattern = escapeRegExp(expectedTimestamp); expect(payload.Body).toMatch( new RegExp( @@ -593,7 +590,7 @@ describe("createTelegramBot", () => { const payload = replySpy.mock.calls[0][0]; expectInboundContextContract(payload); expect(payload.WasMentioned).toBe(true); - const expectedTimestamp = formatLocalEnvelopeTimestamp(new Date("2025-01-09T00:00:00Z")); + const expectedTimestamp = formatEnvelopeTimestamp(new Date("2025-01-09T00:00:00Z")); const timestampPattern = escapeRegExp(expectedTimestamp); expect(payload.Body).toMatch( new RegExp(`^\\[Telegram Test Group id:7 (\\+\\d+[smhd] )?${timestampPattern}\\]`), @@ -639,7 +636,7 @@ describe("createTelegramBot", () => { expect(replySpy).toHaveBeenCalledTimes(1); const payload = replySpy.mock.calls[0][0]; expectInboundContextContract(payload); - const expectedTimestamp = formatLocalEnvelopeTimestamp(new Date("2025-01-09T00:00:00Z")); + const expectedTimestamp = formatEnvelopeTimestamp(new Date("2025-01-09T00:00:00Z")); const timestampPattern = escapeRegExp(expectedTimestamp); expect(payload.Body).toMatch( new RegExp(`^\\[Telegram Ops id:42 (\\+\\d+[smhd] )?${timestampPattern}\\]`), diff --git a/src/web/auto-reply.web-auto-reply.reconnects-after-connection-close.test.ts b/src/web/auto-reply.web-auto-reply.reconnects-after-connection-close.test.ts index 870a018d5..f40e4e3ab 100644 --- a/src/web/auto-reply.web-auto-reply.reconnects-after-connection-close.test.ts +++ b/src/web/auto-reply.web-auto-reply.reconnects-after-connection-close.test.ts @@ -3,10 +3,7 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { - escapeRegExp, - formatLocalEnvelopeTimestamp, -} from "../../test/helpers/envelope-timestamp.js"; +import { escapeRegExp, formatEnvelopeTimestamp } from "../../test/helpers/envelope-timestamp.js"; vi.mock("../agents/pi-embedded.js", () => ({ abortEmbeddedPiRun: vi.fn().mockReturnValue(false), @@ -332,8 +329,8 @@ describe("web auto-reply", () => { expect(resolver).toHaveBeenCalledTimes(2); const firstArgs = resolver.mock.calls[0][0]; const secondArgs = resolver.mock.calls[1][0]; - const firstTimestamp = formatLocalEnvelopeTimestamp(new Date("2025-01-01T00:00:00Z")); - const secondTimestamp = formatLocalEnvelopeTimestamp(new Date("2025-01-01T01:00:00Z")); + const firstTimestamp = formatEnvelopeTimestamp(new Date("2025-01-01T00:00:00Z")); + const secondTimestamp = formatEnvelopeTimestamp(new Date("2025-01-01T01:00:00Z")); const firstPattern = escapeRegExp(firstTimestamp); const secondPattern = escapeRegExp(secondTimestamp); expect(firstArgs.Body).toMatch( diff --git a/test/helpers/envelope-timestamp.ts b/test/helpers/envelope-timestamp.ts index 135063e41..934608204 100644 --- a/test/helpers/envelope-timestamp.ts +++ b/test/helpers/envelope-timestamp.ts @@ -1,5 +1,17 @@ -export function formatLocalEnvelopeTimestamp(date: Date): string { +type EnvelopeTimestampZone = string; + +function formatUtcTimestamp(date: Date): string { + const yyyy = String(date.getUTCFullYear()).padStart(4, "0"); + const mm = String(date.getUTCMonth() + 1).padStart(2, "0"); + const dd = String(date.getUTCDate()).padStart(2, "0"); + const hh = String(date.getUTCHours()).padStart(2, "0"); + const min = String(date.getUTCMinutes()).padStart(2, "0"); + return `${yyyy}-${mm}-${dd}T${hh}:${min}Z`; +} + +function formatZonedTimestamp(date: Date, timeZone?: string): string { const parts = new Intl.DateTimeFormat("en-US", { + timeZone, year: "numeric", month: "2-digit", day: "2-digit", @@ -27,6 +39,17 @@ export function formatLocalEnvelopeTimestamp(date: Date): string { return `${yyyy}-${mm}-${dd} ${hh}:${min}${tz ? ` ${tz}` : ""}`; } +export function formatEnvelopeTimestamp(date: Date, zone: EnvelopeTimestampZone = "utc"): string { + const normalized = zone.trim().toLowerCase(); + if (normalized === "utc" || normalized === "gmt") return formatUtcTimestamp(date); + if (normalized === "local" || normalized === "host") return formatZonedTimestamp(date); + return formatZonedTimestamp(date, zone); +} + +export function formatLocalEnvelopeTimestamp(date: Date): string { + return formatEnvelopeTimestamp(date, "local"); +} + export function escapeRegExp(value: string): string { return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); }