From d0e67632638f8421b8acec8ca960ed9a09eec1dc Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 22 Feb 2026 17:49:59 +0100 Subject: [PATCH] fix(telegram): wire webhookPort through config and startup Co-authored-by: xrf9268-hue <244283935+xrf9268-hue@users.noreply.github.com> --- CHANGELOG.md | 1 + extensions/telegram/src/channel.test.ts | 40 ++++++++++++++++++++++++ extensions/telegram/src/channel.ts | 1 + src/config/telegram-webhook-port.test.ts | 33 +++++++++++++++++++ src/config/types.telegram.ts | 2 ++ src/config/zod-schema.providers-core.ts | 1 + 6 files changed, 78 insertions(+) create mode 100644 src/config/telegram-webhook-port.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 1496961e577..0925a614b1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ Docs: https://docs.openclaw.ai - Telegram/Webhook: keep webhook monitors alive until gateway abort signals fire, preventing false channel exits and immediate webhook auto-restart loops. - Telegram/Polling: retry recoverable setup-time network failures in monitor startup and await runner teardown before retry to avoid overlapping polling sessions. - Telegram/Polling: clear Telegram webhooks (`deleteWebhook`) before starting long-poll `getUpdates`, including retry handling for transient cleanup failures. +- Telegram/Webhook: add `channels.telegram.webhookPort` config support and pass it through plugin startup wiring to the monitor listener. - Signal/RPC: guard malformed Signal RPC JSON responses with a clear status-scoped error and add regression coverage for invalid JSON responses. (#22995) Thanks @adhitShet. - Gateway/Subagents: guard gateway and subagent session-key/message trim paths against undefined inputs to prevent early `Cannot read properties of undefined (reading 'trim')` crashes during subagent spawn and wait flows. - Agents/Workspace: guard `resolveUserPath` against undefined/null input to prevent `Cannot read properties of undefined (reading 'trim')` crashes when workspace paths are missing in embedded runner flows. diff --git a/extensions/telegram/src/channel.test.ts b/extensions/telegram/src/channel.test.ts index 60ceec6d98b..07399ef2ba7 100644 --- a/extensions/telegram/src/channel.test.ts +++ b/extensions/telegram/src/channel.test.ts @@ -122,4 +122,44 @@ describe("telegramPlugin duplicate token guard", () => { expect(probeTelegram).not.toHaveBeenCalled(); expect(monitorTelegramProvider).not.toHaveBeenCalled(); }); + + it("passes webhookPort through to monitor startup options", async () => { + const monitorTelegramProvider = vi.fn(async () => undefined); + const probeTelegram = vi.fn(async () => ({ ok: true, bot: { username: "opsbot" } })); + const runtime = { + channel: { + telegram: { + monitorTelegramProvider, + probeTelegram, + }, + }, + logging: { + shouldLogVerbose: () => false, + }, + } as unknown as PluginRuntime; + setTelegramRuntime(runtime); + + const cfg = createCfg(); + cfg.channels!.telegram!.accounts!.ops = { + ...cfg.channels!.telegram!.accounts!.ops, + webhookUrl: "https://example.test/telegram-webhook", + webhookSecret: "secret", + webhookPort: 9876, + }; + + await telegramPlugin.gateway!.startAccount!( + createStartAccountCtx({ + cfg, + accountId: "ops", + runtime: createRuntimeEnv(), + }), + ); + + expect(monitorTelegramProvider).toHaveBeenCalledWith( + expect.objectContaining({ + useWebhook: true, + webhookPort: 9876, + }), + ); + }); }); diff --git a/extensions/telegram/src/channel.ts b/extensions/telegram/src/channel.ts index 91ccba95e01..e82950ed5bf 100644 --- a/extensions/telegram/src/channel.ts +++ b/extensions/telegram/src/channel.ts @@ -492,6 +492,7 @@ export const telegramPlugin: ChannelPlugin { diff --git a/src/config/telegram-webhook-port.test.ts b/src/config/telegram-webhook-port.test.ts new file mode 100644 index 00000000000..c7dd79237fd --- /dev/null +++ b/src/config/telegram-webhook-port.test.ts @@ -0,0 +1,33 @@ +import { describe, expect, it } from "vitest"; +import { validateConfigObject } from "./config.js"; + +describe("Telegram webhookPort config", () => { + it("accepts a positive webhookPort", () => { + const res = validateConfigObject({ + channels: { + telegram: { + webhookUrl: "https://example.com/telegram-webhook", + webhookSecret: "secret", + webhookPort: 8787, + }, + }, + }); + expect(res.ok).toBe(true); + }); + + it("rejects non-positive webhookPort", () => { + const res = validateConfigObject({ + channels: { + telegram: { + webhookUrl: "https://example.com/telegram-webhook", + webhookSecret: "secret", + webhookPort: 0, + }, + }, + }); + expect(res.ok).toBe(false); + if (!res.ok) { + expect(res.issues.some((issue) => issue.path === "channels.telegram.webhookPort")).toBe(true); + } + }); +}); diff --git a/src/config/types.telegram.ts b/src/config/types.telegram.ts index 486bfc104aa..3417cbb496e 100644 --- a/src/config/types.telegram.ts +++ b/src/config/types.telegram.ts @@ -133,6 +133,8 @@ export type TelegramAccountConfig = { webhookPath?: string; /** Local webhook listener bind host (default: 127.0.0.1). */ webhookHost?: string; + /** Local webhook listener bind port (default: 8787). */ + webhookPort?: number; /** Per-action tool gating (default: true for all). */ actions?: TelegramActionConfig; /** diff --git a/src/config/zod-schema.providers-core.ts b/src/config/zod-schema.providers-core.ts index 21bfa047f3d..7282bc4792d 100644 --- a/src/config/zod-schema.providers-core.ts +++ b/src/config/zod-schema.providers-core.ts @@ -169,6 +169,7 @@ export const TelegramAccountSchemaBase = z webhookSecret: z.string().optional().register(sensitive), webhookPath: z.string().optional(), webhookHost: z.string().optional(), + webhookPort: z.number().int().positive().optional(), actions: z .object({ reactions: z.boolean().optional(),