From eb7656d68c3ceb8f84dc37b042ab1ff093884d6c Mon Sep 17 00:00:00 2001 From: Bohdan Podvirnyi Date: Thu, 15 Jan 2026 18:13:49 +0200 Subject: [PATCH] fix: lint errors --- src/config/types.ts | 27 +- src/config/zod-schema.ts | 224 ++++-------- src/telegram/bot.test.ts | 452 +++++++----------------- src/telegram/bot.ts | 37 +- src/telegram/monitor.ts | 5 +- src/telegram/reaction-level.ts | 3 +- src/telegram/send.ts | 5 +- src/telegram/sent-message-cache.test.ts | 6 +- src/telegram/sent-message-cache.ts | 10 +- src/telegram/webhook.ts | 5 +- 10 files changed, 202 insertions(+), 572 deletions(-) diff --git a/src/config/types.ts b/src/config/types.ts index 3ded5f7e2..fb0edc1ad 100644 --- a/src/config/types.ts +++ b/src/config/types.ts @@ -91,14 +91,7 @@ export type SessionConfig = { export type LoggingConfig = { level?: "silent" | "fatal" | "error" | "warn" | "info" | "debug" | "trace"; file?: string; - consoleLevel?: - | "silent" - | "fatal" - | "error" - | "warn" - | "info" - | "debug" - | "trace"; + consoleLevel?: "silent" | "fatal" | "error" | "warn" | "info" | "debug" | "trace"; consoleStyle?: "pretty" | "compact" | "json"; /** Redact sensitive tokens in tool summaries. Default: "tools". */ redactSensitive?: "off" | "tools"; @@ -122,9 +115,7 @@ export type WebConfig = { }; // Provider docking: allowlists keyed by provider id (and internal "webchat"). -export type AgentElevatedAllowFromConfig = Partial< - Record> ->; +export type AgentElevatedAllowFromConfig = Partial>>; export type IdentityConfig = { name?: string; @@ -495,11 +486,7 @@ export type DiscordGuildChannelConfig = { systemPrompt?: string; }; -export type DiscordReactionNotificationMode = - | "off" - | "own" - | "all" - | "allowlist"; +export type DiscordReactionNotificationMode = "off" | "own" | "all" | "allowlist"; export type DiscordGuildEntry = { slug?: string; @@ -614,11 +601,7 @@ export type SlackChannelConfig = { systemPrompt?: string; }; -export type SignalReactionNotificationMode = - | "off" - | "own" - | "all" - | "allowlist"; +export type SignalReactionNotificationMode = "off" | "own" | "all" | "allowlist"; export type SlackReactionNotificationMode = "off" | "own" | "all" | "allowlist"; @@ -1863,4 +1846,4 @@ export type ConfigFileSnapshot = { config: ClawdbotConfig; issues: ConfigValidationIssue[]; legacyIssues: LegacyConfigIssue[]; -}; \ No newline at end of file +}; diff --git a/src/config/zod-schema.ts b/src/config/zod-schema.ts index 0ac31345c..ed6140ace 100644 --- a/src/config/zod-schema.ts +++ b/src/config/zod-schema.ts @@ -87,16 +87,8 @@ const QueueModeSchema = z.union([ z.literal("queue"), z.literal("interrupt"), ]); -const QueueDropSchema = z.union([ - z.literal("old"), - z.literal("new"), - z.literal("summarize"), -]); -const ReplyToModeSchema = z.union([ - z.literal("off"), - z.literal("first"), - z.literal("all"), -]); +const QueueDropSchema = z.union([z.literal("old"), z.literal("new"), z.literal("summarize")]); +const ReplyToModeSchema = z.union([z.literal("off"), z.literal("first"), z.literal("all")]); // GroupPolicySchema: controls how group messages are handled // Used with .default("allowlist").optional() pattern: @@ -116,18 +108,12 @@ const BlockStreamingChunkSchema = z.object({ minChars: z.number().int().positive().optional(), maxChars: z.number().int().positive().optional(), breakPreference: z - .union([ - z.literal("paragraph"), - z.literal("newline"), - z.literal("sentence"), - ]) + .union([z.literal("paragraph"), z.literal("newline"), z.literal("sentence")]) .optional(), }); const HumanDelaySchema = z.object({ - mode: z - .union([z.literal("off"), z.literal("natural"), z.literal("custom")]) - .optional(), + mode: z.union([z.literal("off"), z.literal("natural"), z.literal("custom")]).optional(), minMs: z.number().int().nonnegative().optional(), maxMs: z.number().int().nonnegative().optional(), }); @@ -135,12 +121,8 @@ const HumanDelaySchema = z.object({ const CliBackendSchema = z.object({ command: z.string(), args: z.array(z.string()).optional(), - output: z - .union([z.literal("json"), z.literal("text"), z.literal("jsonl")]) - .optional(), - resumeOutput: z - .union([z.literal("json"), z.literal("text"), z.literal("jsonl")]) - .optional(), + output: z.union([z.literal("json"), z.literal("text"), z.literal("jsonl")]).optional(), + resumeOutput: z.union([z.literal("json"), z.literal("text"), z.literal("jsonl")]).optional(), input: z.union([z.literal("arg"), z.literal("stdin")]).optional(), maxPromptArgChars: z.number().int().positive().optional(), env: z.record(z.string(), z.string()).optional(), @@ -150,14 +132,10 @@ const CliBackendSchema = z.object({ sessionArg: z.string().optional(), sessionArgs: z.array(z.string()).optional(), resumeArgs: z.array(z.string()).optional(), - sessionMode: z - .union([z.literal("always"), z.literal("existing"), z.literal("none")]) - .optional(), + sessionMode: z.union([z.literal("always"), z.literal("existing"), z.literal("none")]).optional(), sessionIdFields: z.array(z.string()).optional(), systemPromptArg: z.string().optional(), - systemPromptMode: z - .union([z.literal("append"), z.literal("replace")]) - .optional(), + systemPromptMode: z.union([z.literal("append"), z.literal("replace")]).optional(), systemPromptWhen: z .union([z.literal("first"), z.literal("always"), z.literal("never")]) .optional(), @@ -236,9 +214,7 @@ const TranscribeAudioSchema = z }) .optional(); -const HexColorSchema = z - .string() - .regex(/^#?[0-9a-fA-F]{6}$/, "expected hex color (RRGGBB)"); +const HexColorSchema = z.string().regex(/^#?[0-9a-fA-F]{6}$/, "expected hex color (RRGGBB)"); const ExecutableTokenSchema = z .string() @@ -312,18 +288,16 @@ const TelegramAccountSchemaBase = z.object({ .optional(), }); -const TelegramAccountSchema = TelegramAccountSchemaBase.superRefine( - (value, ctx) => { - requireOpenAllowFrom({ - policy: value.dmPolicy, - allowFrom: value.allowFrom, - ctx, - path: ["allowFrom"], - message: - 'channels.telegram.dmPolicy="open" requires channels.telegram.allowFrom to include "*"', - }); - }, -); +const TelegramAccountSchema = TelegramAccountSchemaBase.superRefine((value, ctx) => { + requireOpenAllowFrom({ + policy: value.dmPolicy, + allowFrom: value.allowFrom, + ctx, + path: ["allowFrom"], + message: + 'channels.telegram.dmPolicy="open" requires channels.telegram.allowFrom to include "*"', + }); +}); const TelegramConfigSchema = TelegramAccountSchemaBase.extend({ accounts: z.record(z.string(), TelegramAccountSchema.optional()).optional(), @@ -372,9 +346,7 @@ const DiscordGuildSchema = z.object({ requireMention: z.boolean().optional(), reactionNotifications: z.enum(["off", "own", "all", "allowlist"]).optional(), users: z.array(z.union([z.string(), z.number()])).optional(), - channels: z - .record(z.string(), DiscordGuildChannelSchema.optional()) - .optional(), + channels: z.record(z.string(), DiscordGuildChannelSchema.optional()).optional(), }); const DiscordAccountSchema = z.object({ @@ -527,18 +499,15 @@ const SignalAccountSchemaBase = z.object({ reactionAllowlist: z.array(z.union([z.string(), z.number()])).optional(), }); -const SignalAccountSchema = SignalAccountSchemaBase.superRefine( - (value, ctx) => { - requireOpenAllowFrom({ - policy: value.dmPolicy, - allowFrom: value.allowFrom, - ctx, - path: ["allowFrom"], - message: - 'channels.signal.dmPolicy="open" requires channels.signal.allowFrom to include "*"', - }); - }, -); +const SignalAccountSchema = SignalAccountSchemaBase.superRefine((value, ctx) => { + requireOpenAllowFrom({ + policy: value.dmPolicy, + allowFrom: value.allowFrom, + ctx, + path: ["allowFrom"], + message: 'channels.signal.dmPolicy="open" requires channels.signal.allowFrom to include "*"', + }); +}); const SignalConfigSchema = SignalAccountSchemaBase.extend({ accounts: z.record(z.string(), SignalAccountSchema.optional()).optional(), @@ -548,8 +517,7 @@ const SignalConfigSchema = SignalAccountSchemaBase.extend({ allowFrom: value.allowFrom, ctx, path: ["allowFrom"], - message: - 'channels.signal.dmPolicy="open" requires channels.signal.allowFrom to include "*"', + message: 'channels.signal.dmPolicy="open" requires channels.signal.allowFrom to include "*"', }); }); @@ -559,9 +527,7 @@ const IMessageAccountSchemaBase = z.object({ enabled: z.boolean().optional(), cliPath: ExecutableTokenSchema.optional(), dbPath: z.string().optional(), - service: z - .union([z.literal("imessage"), z.literal("sms"), z.literal("auto")]) - .optional(), + service: z.union([z.literal("imessage"), z.literal("sms"), z.literal("auto")]).optional(), region: z.string().optional(), dmPolicy: DmPolicySchema.optional().default("pairing"), allowFrom: z.array(z.union([z.string(), z.number()])).optional(), @@ -587,18 +553,16 @@ const IMessageAccountSchemaBase = z.object({ .optional(), }); -const IMessageAccountSchema = IMessageAccountSchemaBase.superRefine( - (value, ctx) => { - requireOpenAllowFrom({ - policy: value.dmPolicy, - allowFrom: value.allowFrom, - ctx, - path: ["allowFrom"], - message: - 'channels.imessage.dmPolicy="open" requires channels.imessage.allowFrom to include "*"', - }); - }, -); +const IMessageAccountSchema = IMessageAccountSchemaBase.superRefine((value, ctx) => { + requireOpenAllowFrom({ + policy: value.dmPolicy, + allowFrom: value.allowFrom, + ctx, + path: ["allowFrom"], + message: + 'channels.imessage.dmPolicy="open" requires channels.imessage.allowFrom to include "*"', + }); +}); const IMessageConfigSchema = IMessageAccountSchemaBase.extend({ accounts: z.record(z.string(), IMessageAccountSchema.optional()).optional(), @@ -696,24 +660,18 @@ const WhatsAppAccountSchema = z .object({ emoji: z.string().optional(), direct: z.boolean().optional().default(true), - group: z - .enum(["always", "mentions", "never"]) - .optional() - .default("mentions"), + group: z.enum(["always", "mentions", "never"]).optional().default("mentions"), }) .optional(), }) .superRefine((value, ctx) => { if (value.dmPolicy !== "open") return; - const allow = (value.allowFrom ?? []) - .map((v) => String(v).trim()) - .filter(Boolean); + const allow = (value.allowFrom ?? []).map((v) => String(v).trim()).filter(Boolean); if (allow.includes("*")) return; ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["allowFrom"], - message: - 'channels.whatsapp.accounts.*.dmPolicy="open" requires allowFrom to include "*"', + message: 'channels.whatsapp.accounts.*.dmPolicy="open" requires allowFrom to include "*"', }); }); @@ -755,18 +713,13 @@ const WhatsAppConfigSchema = z .object({ emoji: z.string().optional(), direct: z.boolean().optional().default(true), - group: z - .enum(["always", "mentions", "never"]) - .optional() - .default("mentions"), + group: z.enum(["always", "mentions", "never"]).optional().default("mentions"), }) .optional(), }) .superRefine((value, ctx) => { if (value.dmPolicy !== "open") return; - const allow = (value.allowFrom ?? []) - .map((v) => String(v).trim()) - .filter(Boolean); + const allow = (value.allowFrom ?? []).map((v) => String(v).trim()).filter(Boolean); if (allow.includes("*")) return; ctx.addIssue({ code: z.ZodIssueCode.custom, @@ -816,11 +769,7 @@ const SessionSchema = z .object({ channel: z.string().optional(), chatType: z - .union([ - z.literal("direct"), - z.literal("group"), - z.literal("room"), - ]) + .union([z.literal("direct"), z.literal("group"), z.literal("room")]) .optional(), keyPrefix: z.string().optional(), }) @@ -845,9 +794,7 @@ const MessagesSchema = z groupChat: GroupChatSchema, queue: QueueSchema, ackReaction: z.string().optional(), - ackReactionScope: z - .enum(["group-mentions", "group-all", "direct", "all"]) - .optional(), + ackReactionScope: z.enum(["group-mentions", "group-all", "direct", "all"]).optional(), removeAckAfterReply: z.boolean().optional(), }) .optional(); @@ -972,12 +919,7 @@ const ToolPolicySchema = z .optional(); const ToolProfileSchema = z - .union([ - z.literal("minimal"), - z.literal("coding"), - z.literal("messaging"), - z.literal("full"), - ]) + .union([z.literal("minimal"), z.literal("coding"), z.literal("messaging"), z.literal("full")]) .optional(); // Provider docking: allowlists keyed by provider id (no schema updates when adding providers). @@ -987,18 +929,10 @@ const ElevatedAllowFromSchema = z const AgentSandboxSchema = z .object({ - mode: z - .union([z.literal("off"), z.literal("non-main"), z.literal("all")]) - .optional(), - workspaceAccess: z - .union([z.literal("none"), z.literal("ro"), z.literal("rw")]) - .optional(), - sessionToolsVisibility: z - .union([z.literal("spawned"), z.literal("all")]) - .optional(), - scope: z - .union([z.literal("session"), z.literal("agent"), z.literal("shared")]) - .optional(), + mode: z.union([z.literal("off"), z.literal("non-main"), z.literal("all")]).optional(), + workspaceAccess: z.union([z.literal("none"), z.literal("ro"), z.literal("rw")]).optional(), + sessionToolsVisibility: z.union([z.literal("spawned"), z.literal("all")]).optional(), + scope: z.union([z.literal("session"), z.literal("agent"), z.literal("shared")]).optional(), perSession: z.boolean().optional(), workspaceRoot: z.string().optional(), docker: SandboxDockerSchema, @@ -1181,11 +1115,7 @@ const BindingsSchema = z accountId: z.string().optional(), peer: z .object({ - kind: z.union([ - z.literal("dm"), - z.literal("group"), - z.literal("channel"), - ]), + kind: z.union([z.literal("dm"), z.literal("group"), z.literal("channel")]), id: z.string(), }) .optional(), @@ -1221,9 +1151,7 @@ const HookMappingSchema = z }) .optional(), action: z.union([z.literal("wake"), z.literal("agent")]).optional(), - wakeMode: z - .union([z.literal("now"), z.literal("next-heartbeat")]) - .optional(), + wakeMode: z.union([z.literal("now"), z.literal("next-heartbeat")]).optional(), name: z.string().optional(), sessionKey: z.string().optional(), messageTemplate: z.string().optional(), @@ -1274,9 +1202,7 @@ const HooksGmailSchema = z .optional(), tailscale: z .object({ - mode: z - .union([z.literal("off"), z.literal("serve"), z.literal("funnel")]) - .optional(), + mode: z.union([z.literal("off"), z.literal("serve"), z.literal("funnel")]).optional(), path: z.string().optional(), target: z.string().optional(), }) @@ -1328,11 +1254,7 @@ const AgentDefaultsSchema = z contextPruning: z .object({ mode: z - .union([ - z.literal("off"), - z.literal("adaptive"), - z.literal("aggressive"), - ]) + .union([z.literal("off"), z.literal("adaptive"), z.literal("aggressive")]) .optional(), keepLastAssistants: z.number().int().nonnegative().optional(), softTrimRatio: z.number().min(0).max(1).optional(), @@ -1361,9 +1283,7 @@ const AgentDefaultsSchema = z .optional(), compaction: z .object({ - mode: z - .union([z.literal("default"), z.literal("safeguard")]) - .optional(), + mode: z.union([z.literal("default"), z.literal("safeguard")]).optional(), reserveTokensFloor: z.number().int().nonnegative().optional(), memoryFlush: z .object({ @@ -1387,12 +1307,8 @@ const AgentDefaultsSchema = z .optional(), verboseDefault: z.union([z.literal("off"), z.literal("on")]).optional(), elevatedDefault: z.union([z.literal("off"), z.literal("on")]).optional(), - blockStreamingDefault: z - .union([z.literal("off"), z.literal("on")]) - .optional(), - blockStreamingBreak: z - .union([z.literal("text_end"), z.literal("message_end")]) - .optional(), + blockStreamingDefault: z.union([z.literal("off"), z.literal("on")]).optional(), + blockStreamingBreak: z.union([z.literal("text_end"), z.literal("message_end")]).optional(), blockStreamingChunk: BlockStreamingChunkSchema.optional(), blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(), humanDelay: HumanDelaySchema.optional(), @@ -1426,22 +1342,10 @@ const AgentDefaultsSchema = z .optional(), sandbox: z .object({ - mode: z - .union([z.literal("off"), z.literal("non-main"), z.literal("all")]) - .optional(), - workspaceAccess: z - .union([z.literal("none"), z.literal("ro"), z.literal("rw")]) - .optional(), - sessionToolsVisibility: z - .union([z.literal("spawned"), z.literal("all")]) - .optional(), - scope: z - .union([ - z.literal("session"), - z.literal("agent"), - z.literal("shared"), - ]) - .optional(), + mode: z.union([z.literal("off"), z.literal("non-main"), z.literal("all")]).optional(), + workspaceAccess: z.union([z.literal("none"), z.literal("ro"), z.literal("rw")]).optional(), + sessionToolsVisibility: z.union([z.literal("spawned"), z.literal("all")]).optional(), + scope: z.union([z.literal("session"), z.literal("agent"), z.literal("shared")]).optional(), perSession: z.boolean().optional(), workspaceRoot: z.string().optional(), docker: SandboxDockerSchema, diff --git a/src/telegram/bot.test.ts b/src/telegram/bot.test.ts index 3aa8af1ac..8854908e7 100644 --- a/src/telegram/bot.test.ts +++ b/src/telegram/bot.test.ts @@ -26,15 +26,13 @@ vi.mock("../config/config.js", async (importOriginal) => { }; }); -const { readTelegramAllowFromStore, upsertTelegramPairingRequest } = vi.hoisted( - () => ({ - readTelegramAllowFromStore: vi.fn(async () => [] as string[]), - upsertTelegramPairingRequest: vi.fn(async () => ({ - code: "PAIRCODE", - created: true, - })), - }), -); +const { readTelegramAllowFromStore, upsertTelegramPairingRequest } = vi.hoisted(() => ({ + readTelegramAllowFromStore: vi.fn(async () => [] as string[]), + upsertTelegramPairingRequest: vi.fn(async () => ({ + code: "PAIRCODE", + created: true, + })), +})); vi.mock("./pairing-store.js", () => ({ readTelegramAllowFromStore, @@ -217,13 +215,9 @@ describe("createTelegramBot", () => { it("sequentializes updates by chat and thread", () => { createTelegramBot({ token: "tok" }); expect(sequentializeSpy).toHaveBeenCalledTimes(1); - expect(middlewareUseSpy).toHaveBeenCalledWith( - sequentializeSpy.mock.results[0]?.value, - ); + expect(middlewareUseSpy).toHaveBeenCalledWith(sequentializeSpy.mock.results[0]?.value); expect(sequentializeKey).toBe(getTelegramSequentialKey); - expect(getTelegramSequentialKey({ message: { chat: { id: 123 } } })).toBe( - "telegram:123", - ); + expect(getTelegramSequentialKey({ message: { chat: { id: 123 } } })).toBe("telegram:123"); expect( getTelegramSequentialKey({ message: { chat: { id: 123 }, message_thread_id: 9 }, @@ -243,15 +237,13 @@ describe("createTelegramBot", () => { it("routes callback_query payloads as messages and answers callbacks", async () => { onSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); createTelegramBot({ token: "tok" }); - const callbackHandler = onSpy.mock.calls.find( - (call) => call[0] === "callback_query", - )?.[1] as (ctx: Record) => Promise; + const callbackHandler = onSpy.mock.calls.find((call) => call[0] === "callback_query")?.[1] as ( + ctx: Record, + ) => Promise; expect(callbackHandler).toBeDefined(); await callbackHandler({ @@ -281,16 +273,12 @@ describe("createTelegramBot", () => { try { onSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); createTelegramBot({ token: "tok" }); expect(onSpy).toHaveBeenCalledWith("message", expect.any(Function)); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; const message = { chat: { id: 1234, type: "private" }, @@ -322,9 +310,7 @@ describe("createTelegramBot", () => { it("requests pairing by default for unknown DM senders", async () => { onSpy.mockReset(); sendMessageSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); loadConfig.mockReturnValue({ @@ -337,9 +323,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { @@ -355,21 +339,15 @@ describe("createTelegramBot", () => { expect(replySpy).not.toHaveBeenCalled(); expect(sendMessageSpy).toHaveBeenCalledTimes(1); expect(sendMessageSpy.mock.calls[0]?.[0]).toBe(1234); - expect(String(sendMessageSpy.mock.calls[0]?.[1])).toContain( - "Your Telegram user id: 999", - ); - expect(String(sendMessageSpy.mock.calls[0]?.[1])).toContain( - "Pairing code:", - ); + expect(String(sendMessageSpy.mock.calls[0]?.[1])).toContain("Your Telegram user id: 999"); + expect(String(sendMessageSpy.mock.calls[0]?.[1])).toContain("Pairing code:"); expect(String(sendMessageSpy.mock.calls[0]?.[1])).toContain("PAIRME12"); }); it("does not resend pairing code when a request is already pending", async () => { onSpy.mockReset(); sendMessageSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); loadConfig.mockReturnValue({ @@ -381,9 +359,7 @@ describe("createTelegramBot", () => { .mockResolvedValueOnce({ code: "PAIRME12", created: false }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; const message = { chat: { id: 1234, type: "private" }, @@ -412,9 +388,7 @@ describe("createTelegramBot", () => { sendChatActionSpy.mockReset(); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { chat: { id: 42, type: "private" }, text: "hi" }, me: { username: "clawdbot_bot" }, @@ -426,9 +400,7 @@ describe("createTelegramBot", () => { it("accepts group messages when mentionPatterns match (without @botUsername)", async () => { onSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); loadConfig.mockReturnValue({ @@ -443,9 +415,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { @@ -462,16 +432,12 @@ describe("createTelegramBot", () => { expect(replySpy).toHaveBeenCalledTimes(1); const payload = replySpy.mock.calls[0][0]; expect(payload.WasMentioned).toBe(true); - expect(payload.Body).toMatch( - /^\[Telegram Test Group id:7 from Ada id:9 2025-01-09T00:00Z\]/, - ); + expect(payload.Body).toMatch(/^\[Telegram Test Group id:7 from Ada id:9 2025-01-09T00:00Z\]/); }); it("includes sender identity in group envelope headers", async () => { onSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); loadConfig.mockReturnValue({ @@ -484,9 +450,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { @@ -515,9 +479,7 @@ describe("createTelegramBot", () => { it("reacts to mention-gated group messages when ackReaction is enabled", async () => { onSpy.mockReset(); setMessageReactionSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); loadConfig.mockReturnValue({ @@ -535,9 +497,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { @@ -551,9 +511,7 @@ describe("createTelegramBot", () => { getFile: async () => ({ download: async () => new Uint8Array() }), }); - expect(setMessageReactionSpy).toHaveBeenCalledWith(7, 123, [ - { type: "emoji", emoji: "👀" }, - ]); + expect(setMessageReactionSpy).toHaveBeenCalledWith(7, 123, [{ type: "emoji", emoji: "👀" }]); }); it("clears native commands when disabled", () => { @@ -568,9 +526,7 @@ describe("createTelegramBot", () => { it("skips group messages when requireMention is enabled and no mention matches", async () => { onSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); loadConfig.mockReturnValue({ @@ -584,9 +540,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { @@ -605,9 +559,7 @@ describe("createTelegramBot", () => { it("allows group messages when requireMention is enabled but mentions cannot be detected", async () => { onSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); loadConfig.mockReturnValue({ @@ -621,9 +573,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { @@ -645,15 +595,11 @@ describe("createTelegramBot", () => { it("includes reply-to context when a Telegram reply is received", async () => { onSpy.mockReset(); sendMessageSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { @@ -682,16 +628,12 @@ describe("createTelegramBot", () => { it("sends replies without native reply threading", async () => { onSpy.mockReset(); sendMessageSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); replySpy.mockResolvedValue({ text: "a".repeat(4500) }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { chat: { id: 5, type: "private" }, @@ -712,9 +654,7 @@ describe("createTelegramBot", () => { it("honors replyToMode=first for threaded replies", async () => { onSpy.mockReset(); sendMessageSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); replySpy.mockResolvedValue({ text: "a".repeat(4500), @@ -722,9 +662,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok", replyToMode: "first" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { chat: { id: 5, type: "private" }, @@ -747,9 +685,7 @@ describe("createTelegramBot", () => { it("prefixes tool and final replies with responsePrefix", async () => { onSpy.mockReset(); sendMessageSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); replySpy.mockImplementation(async (_ctx, opts) => { await opts?.onToolResult?.({ text: "tool result" }); @@ -763,9 +699,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { chat: { id: 5, type: "private" }, @@ -784,9 +718,7 @@ describe("createTelegramBot", () => { it("honors replyToMode=all for threaded replies", async () => { onSpy.mockReset(); sendMessageSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); replySpy.mockResolvedValue({ text: "a".repeat(4500), @@ -794,9 +726,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok", replyToMode: "all" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { chat: { id: 5, type: "private" }, @@ -816,9 +746,7 @@ describe("createTelegramBot", () => { it("blocks group messages when telegram.groups is set without a wildcard", async () => { onSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); loadConfig.mockReturnValue({ channels: { @@ -831,9 +759,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { @@ -850,9 +776,7 @@ describe("createTelegramBot", () => { it("skips group messages without mention when requireMention is enabled", async () => { onSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); loadConfig.mockReturnValue({ channels: { @@ -861,9 +785,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { @@ -880,13 +802,9 @@ describe("createTelegramBot", () => { it("honors routed group activation from session store", async () => { onSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); - const storeDir = fs.mkdtempSync( - path.join(os.tmpdir(), "clawdbot-telegram-"), - ); + const storeDir = fs.mkdtempSync(path.join(os.tmpdir(), "clawdbot-telegram-")); const storePath = path.join(storeDir, "sessions.json"); fs.writeFileSync( storePath, @@ -915,9 +833,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { @@ -934,9 +850,7 @@ describe("createTelegramBot", () => { it("routes DMs by telegram accountId binding", async () => { onSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); loadConfig.mockReturnValue({ @@ -959,9 +873,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok", accountId: "opie" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { @@ -983,9 +895,7 @@ describe("createTelegramBot", () => { it("allows per-group requireMention override", async () => { onSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); loadConfig.mockReturnValue({ channels: { @@ -1000,9 +910,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { @@ -1019,9 +927,7 @@ describe("createTelegramBot", () => { it("allows per-topic requireMention override", async () => { onSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); loadConfig.mockReturnValue({ channels: { @@ -1041,9 +947,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { @@ -1066,9 +970,7 @@ describe("createTelegramBot", () => { it("honors groups default when no explicit group override exists", async () => { onSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); loadConfig.mockReturnValue({ channels: { @@ -1080,9 +982,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { @@ -1099,9 +999,7 @@ describe("createTelegramBot", () => { it("does not block group messages when bot username is unknown", async () => { onSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); loadConfig.mockReturnValue({ channels: { @@ -1113,9 +1011,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { @@ -1131,9 +1027,7 @@ describe("createTelegramBot", () => { it("sends GIF replies as animations", async () => { onSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); replySpy.mockResolvedValueOnce({ @@ -1148,9 +1042,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { @@ -1175,9 +1067,7 @@ describe("createTelegramBot", () => { // groupPolicy tests it("blocks all group messages when groupPolicy is 'disabled'", async () => { onSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); loadConfig.mockReturnValue({ channels: { @@ -1189,9 +1079,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { @@ -1210,9 +1098,7 @@ describe("createTelegramBot", () => { it("blocks group messages from senders not in allowFrom when groupPolicy is 'allowlist'", async () => { onSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); loadConfig.mockReturnValue({ channels: { @@ -1224,9 +1110,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { @@ -1244,9 +1128,7 @@ describe("createTelegramBot", () => { it("allows group messages from senders in allowFrom (by ID) when groupPolicy is 'allowlist'", async () => { onSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); loadConfig.mockReturnValue({ channels: { @@ -1259,9 +1141,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { @@ -1279,9 +1159,7 @@ describe("createTelegramBot", () => { it("allows group messages from senders in allowFrom (by username) when groupPolicy is 'allowlist'", async () => { onSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); loadConfig.mockReturnValue({ channels: { @@ -1294,9 +1172,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { @@ -1314,9 +1190,7 @@ describe("createTelegramBot", () => { it("allows group messages from telegram:-prefixed allowFrom entries when groupPolicy is 'allowlist'", async () => { onSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); loadConfig.mockReturnValue({ channels: { @@ -1329,9 +1203,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { @@ -1349,9 +1221,7 @@ describe("createTelegramBot", () => { it("allows group messages from tg:-prefixed allowFrom entries case-insensitively when groupPolicy is 'allowlist'", async () => { onSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); loadConfig.mockReturnValue({ channels: { @@ -1364,9 +1234,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { @@ -1384,9 +1252,7 @@ describe("createTelegramBot", () => { it("allows all group messages when groupPolicy is 'open'", async () => { onSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); loadConfig.mockReturnValue({ channels: { @@ -1398,9 +1264,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { @@ -1418,9 +1282,7 @@ describe("createTelegramBot", () => { it("matches usernames case-insensitively when groupPolicy is 'allowlist'", async () => { onSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); loadConfig.mockReturnValue({ channels: { @@ -1433,9 +1295,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { @@ -1453,9 +1313,7 @@ describe("createTelegramBot", () => { it("allows direct messages regardless of groupPolicy", async () => { onSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); loadConfig.mockReturnValue({ channels: { @@ -1467,9 +1325,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { @@ -1487,9 +1343,7 @@ describe("createTelegramBot", () => { it("allows direct messages with tg/Telegram-prefixed allowFrom entries", async () => { onSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); loadConfig.mockReturnValue({ channels: { @@ -1500,9 +1354,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { @@ -1520,9 +1372,7 @@ describe("createTelegramBot", () => { it("allows direct messages with telegram:-prefixed allowFrom entries", async () => { onSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); loadConfig.mockReturnValue({ channels: { @@ -1533,9 +1383,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { @@ -1553,9 +1401,7 @@ describe("createTelegramBot", () => { it("allows group messages with wildcard in allowFrom when groupPolicy is 'allowlist'", async () => { onSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); loadConfig.mockReturnValue({ channels: { @@ -1568,9 +1414,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { @@ -1588,9 +1432,7 @@ describe("createTelegramBot", () => { it("blocks group messages with no sender ID when groupPolicy is 'allowlist'", async () => { onSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); loadConfig.mockReturnValue({ channels: { @@ -1602,9 +1444,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { @@ -1622,9 +1462,7 @@ describe("createTelegramBot", () => { it("matches telegram:-prefixed allowFrom entries in group allowlist", async () => { onSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); loadConfig.mockReturnValue({ channels: { @@ -1637,9 +1475,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { @@ -1658,9 +1494,7 @@ describe("createTelegramBot", () => { it("matches tg:-prefixed allowFrom entries case-insensitively in group allowlist", async () => { onSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); loadConfig.mockReturnValue({ channels: { @@ -1673,9 +1507,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { @@ -1694,9 +1526,7 @@ describe("createTelegramBot", () => { it("blocks group messages when groupPolicy allowlist has no groupAllowFrom", async () => { onSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); loadConfig.mockReturnValue({ channels: { @@ -1708,9 +1538,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { @@ -1728,9 +1556,7 @@ describe("createTelegramBot", () => { it("allows control commands with TG-prefixed groupAllowFrom entries", async () => { onSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); loadConfig.mockReturnValue({ channels: { @@ -1743,9 +1569,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { @@ -1764,9 +1588,7 @@ describe("createTelegramBot", () => { it("isolates forum topic sessions and carries thread metadata", async () => { onSpy.mockReset(); sendChatActionSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); loadConfig.mockReturnValue({ @@ -1779,9 +1601,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { @@ -1803,9 +1623,7 @@ describe("createTelegramBot", () => { expect(replySpy).toHaveBeenCalledTimes(1); const payload = replySpy.mock.calls[0][0]; - expect(payload.SessionKey).toContain( - "telegram:group:-1001234567890:topic:99", - ); + expect(payload.SessionKey).toContain("telegram:group:-1001234567890:topic:99"); expect(payload.From).toBe("group:-1001234567890:topic:99"); expect(payload.MessageThreadId).toBe(99); expect(payload.IsForum).toBe(true); @@ -1817,9 +1635,7 @@ describe("createTelegramBot", () => { it("falls back to General topic thread id for typing in forums", async () => { onSpy.mockReset(); sendChatActionSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); loadConfig.mockReturnValue({ @@ -1832,9 +1648,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { @@ -1862,9 +1676,7 @@ describe("createTelegramBot", () => { it("routes General topic replies using thread id 1", async () => { onSpy.mockReset(); sendMessageSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); replySpy.mockResolvedValue({ text: "response" }); @@ -1878,9 +1690,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { @@ -1908,9 +1718,7 @@ describe("createTelegramBot", () => { it("applies topic skill filters and system prompts", async () => { onSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); loadConfig.mockReturnValue({ @@ -1935,9 +1743,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { @@ -1968,9 +1774,7 @@ describe("createTelegramBot", () => { onSpy.mockReset(); sendMessageSpy.mockReset(); commandSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); replySpy.mockResolvedValue({ text: "response" }); @@ -1984,9 +1788,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; await handler({ message: { @@ -2017,9 +1819,7 @@ describe("createTelegramBot", () => { onSpy.mockReset(); sendMessageSpy.mockReset(); commandSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); replySpy.mockResolvedValue({ text: "response" }); @@ -2036,9 +1836,7 @@ describe("createTelegramBot", () => { createTelegramBot({ token: "tok" }); expect(commandSpy).toHaveBeenCalled(); - const handler = commandSpy.mock.calls[0][1] as ( - ctx: Record, - ) => Promise; + const handler = commandSpy.mock.calls[0][1] as (ctx: Record) => Promise; await handler({ message: { @@ -2068,9 +1866,7 @@ describe("createTelegramBot", () => { onSpy.mockReset(); sendMessageSpy.mockReset(); commandSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); replySpy.mockImplementation(async (_ctx, opts) => { await opts?.onToolResult?.({ text: "tool update" }); @@ -2086,9 +1882,9 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok" }); - const verboseHandler = commandSpy.mock.calls.find( - (call) => call[0] === "verbose", - )?.[1] as ((ctx: Record) => Promise) | undefined; + const verboseHandler = commandSpy.mock.calls.find((call) => call[0] === "verbose")?.[1] as + | ((ctx: Record) => Promise) + | undefined; if (!verboseHandler) throw new Error("verbose command handler missing"); await verboseHandler({ @@ -2109,9 +1905,7 @@ describe("createTelegramBot", () => { it("dedupes duplicate message updates by update_id", async () => { onSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); loadConfig.mockReturnValue({ @@ -2121,9 +1915,7 @@ describe("createTelegramBot", () => { }); createTelegramBot({ token: "tok" }); - const handler = getOnHandler("message") as ( - ctx: Record, - ) => Promise; + const handler = getOnHandler("message") as (ctx: Record) => Promise; const ctx = { update: { update_id: 111 }, @@ -2146,9 +1938,7 @@ describe("createTelegramBot", () => { it("dedupes duplicate callback_query updates by update_id", async () => { onSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); loadConfig.mockReturnValue({ @@ -2186,9 +1976,7 @@ describe("createTelegramBot", () => { it("allows distinct callback_query ids without update_id", async () => { onSpy.mockReset(); - const replySpy = replyModule.__replySpy as unknown as ReturnType< - typeof vi.fn - >; + const replySpy = replyModule.__replySpy as unknown as ReturnType; replySpy.mockReset(); loadConfig.mockReturnValue({ @@ -2238,9 +2026,7 @@ describe("createTelegramBot", () => { it("registers message_reaction handler", () => { onSpy.mockReset(); createTelegramBot({ token: "tok" }); - const reactionHandler = onSpy.mock.calls.find( - (call) => call[0] === "message_reaction", - ); + const reactionHandler = onSpy.mock.calls.find((call) => call[0] === "message_reaction"); expect(reactionHandler).toBeDefined(); }); diff --git a/src/telegram/bot.ts b/src/telegram/bot.ts index 2a02b7503..ffd50f7f9 100644 --- a/src/telegram/bot.ts +++ b/src/telegram/bot.ts @@ -17,15 +17,8 @@ import { resolveChannelGroupPolicy, resolveChannelGroupRequireMention, } from "../config/group-policy.js"; -import { - loadSessionStore, - resolveStorePath, - updateLastRoute, -} from "../config/sessions.js"; +import { loadSessionStore, resolveStorePath } from "../config/sessions.js"; import { danger, logVerbose, shouldLogVerbose } from "../globals.js"; -import { recordChannelActivity } from "../infra/channel-activity.js"; -import { createDedupeCache } from "../infra/dedupe.js"; -import { formatErrorMessage } from "../infra/errors.js"; import { enqueueSystemEvent } from "../infra/system-events.js"; import { getChildLogger } from "../logging.js"; import { resolveAgentRoute } from "../routing/resolve-route.js"; @@ -47,12 +40,6 @@ import { type TelegramUpdateKeyContext, } from "./bot-updates.js"; import { resolveTelegramFetch } from "./fetch.js"; -import { - readTelegramAllowFromStore, - upsertTelegramPairingRequest, -} from "./pairing-store.js"; -import { wasSentByBot } from "./sent-message-cache.js"; -import { resolveTelegramVoiceSend } from "./voice.js"; export type TelegramBotOptions = { token: string; @@ -334,23 +321,18 @@ export function createTelegramBot(opts: TelegramBotOptions) { // Detect added reactions const oldEmojis = new Set( reaction.old_reaction - .filter( - (r): r is { type: "emoji"; emoji: string } => r.type === "emoji", - ) + .filter((r): r is { type: "emoji"; emoji: string } => r.type === "emoji") .map((r) => r.emoji), ); const addedReactions = reaction.new_reaction - .filter( - (r): r is { type: "emoji"; emoji: string } => r.type === "emoji", - ) + .filter((r): r is { type: "emoji"; emoji: string } => r.type === "emoji") .filter((r) => !oldEmojis.has(r.emoji)); if (addedReactions.length === 0) return; // Build sender label const senderName = user - ? [user.first_name, user.last_name].filter(Boolean).join(" ").trim() || - user.username + ? [user.first_name, user.last_name].filter(Boolean).join(" ").trim() || user.username : undefined; const senderUsername = user?.username ? `@${user.username}` : undefined; let senderLabel = senderName; @@ -373,11 +355,8 @@ export function createTelegramBot(opts: TelegramBotOptions) { }); // Resolve agent route for session - const isGroup = - reaction.chat.type === "group" || reaction.chat.type === "supergroup"; - const peerId = isGroup - ? buildTelegramGroupPeerId(chatId, resolvedThreadId) - : String(chatId); + const isGroup = reaction.chat.type === "group" || reaction.chat.type === "supergroup"; + const peerId = isGroup ? buildTelegramGroupPeerId(chatId, resolvedThreadId) : String(chatId); const route = resolveAgentRoute({ cfg, channel: "telegram", @@ -396,9 +375,7 @@ export function createTelegramBot(opts: TelegramBotOptions) { logVerbose(`telegram: reaction event enqueued: ${text}`); } } catch (err) { - runtime.error?.( - danger(`telegram reaction handler failed: ${String(err)}`), - ); + runtime.error?.(danger(`telegram reaction handler failed: ${String(err)}`)); } }); diff --git a/src/telegram/monitor.ts b/src/telegram/monitor.ts index d0417669b..19b47058f 100644 --- a/src/telegram/monitor.ts +++ b/src/telegram/monitor.ts @@ -34,10 +34,7 @@ export function createTelegramRunnerOptions(cfg: ClawdbotConfig): RunOptions { afterEach(() => { diff --git a/src/telegram/sent-message-cache.ts b/src/telegram/sent-message-cache.ts index 5951d4061..05c12ddf7 100644 --- a/src/telegram/sent-message-cache.ts +++ b/src/telegram/sent-message-cache.ts @@ -29,10 +29,7 @@ function cleanupExpired(entry: CacheEntry): void { /** * Record a message ID as sent by the bot. */ -export function recordSentMessage( - chatId: number | string, - messageId: number, -): void { +export function recordSentMessage(chatId: number | string, messageId: number): void { const key = getChatKey(chatId); let entry = sentMessages.get(key); if (!entry) { @@ -50,10 +47,7 @@ export function recordSentMessage( /** * Check if a message was sent by the bot. */ -export function wasSentByBot( - chatId: number | string, - messageId: number, -): boolean { +export function wasSentByBot(chatId: number | string, messageId: number): boolean { const key = getChatKey(chatId); const entry = sentMessages.get(key); if (!entry) return false; diff --git a/src/telegram/webhook.ts b/src/telegram/webhook.ts index 363aecec0..a6f31ec94 100644 --- a/src/telegram/webhook.ts +++ b/src/telegram/webhook.ts @@ -63,10 +63,7 @@ export async function startTelegramWebhook(opts: { await bot.api.setWebhook(publicUrl, { secret_token: opts.secret, - allowed_updates: [ - "message", - "message_reaction", - ], + allowed_updates: ["message", "message_reaction"], }); await new Promise((resolve) => server.listen(port, host, resolve));