From 5939363eed48bc741ac152c7c7b021a395d732a7 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 6 Jan 2026 22:34:02 +0000 Subject: [PATCH] fix: include telegram group sender in envelope headers --- CHANGELOG.md | 1 + src/telegram/bot.test.ts | 43 ++++++++++++++++++++++++++++++++++++++++ src/telegram/bot.ts | 32 +++++++++++++++++++++++++----- 3 files changed, 71 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31acd0c15..5386ac4e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ - Browser: fix `browser snapshot`/`browser act` timeouts under Bun by patching Playwright’s CDP WebSocket selection. Thanks @azade-c for PR #307. - Browser: add `--browser-profile` flag and honor profile in tabs routes + browser tool. Thanks @jamesgroat for PR #324. - Telegram: stop typing after tool results. Thanks @AbhisekBasu1 for PR #322. +- Telegram: include sender identity in group envelope headers. (#336) - Messages: stop defaulting ack reactions to 👀 when identity emoji is missing. - Auto-reply: require slash for control commands to avoid false triggers in normal text. - Auto-reply: flag error payloads and improve Bun socket error messaging. Thanks @emanuelst for PR #331. diff --git a/src/telegram/bot.test.ts b/src/telegram/bot.test.ts index e9553508e..99e6634c1 100644 --- a/src/telegram/bot.test.ts +++ b/src/telegram/bot.test.ts @@ -241,6 +241,49 @@ 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\]/, + ); + }); + + it("includes sender identity in group envelope headers", async () => { + onSpy.mockReset(); + const replySpy = replyModule.__replySpy as unknown as ReturnType< + typeof vi.fn + >; + replySpy.mockReset(); + + loadConfig.mockReturnValue({ + telegram: { groups: { "*": { requireMention: false } } }, + }); + + createTelegramBot({ token: "tok" }); + const handler = onSpy.mock.calls[0][1] as ( + ctx: Record, + ) => Promise; + + await handler({ + message: { + chat: { id: 42, type: "group", title: "Ops" }, + text: "hello", + date: 1736380800, + message_id: 2, + from: { + id: 99, + first_name: "Ada", + last_name: "Lovelace", + username: "ada", + }, + }, + me: { username: "clawdbot_bot" }, + getFile: async () => ({ download: async () => new Uint8Array() }), + }); + + expect(replySpy).toHaveBeenCalledTimes(1); + const payload = replySpy.mock.calls[0][0]; + expect(payload.Body).toMatch( + /^\[Telegram Ops id:42 from Ada Lovelace \(@ada\) id:99 2025-01-09T00:00Z\]/, + ); }); it("reacts to mention-gated group messages when ackReaction is enabled", async () => { diff --git a/src/telegram/bot.ts b/src/telegram/bot.ts index 296f5b15c..650cae7b9 100644 --- a/src/telegram/bot.ts +++ b/src/telegram/bot.ts @@ -375,8 +375,8 @@ export function createTelegramBot(opts: TelegramBotOptions) { const body = formatAgentEnvelope({ provider: "Telegram", from: isGroup - ? buildGroupLabel(msg, chatId) - : buildSenderLabel(msg, chatId), + ? buildGroupFromLabel(msg, chatId, senderId) + : buildSenderLabel(msg, senderId || chatId), timestamp: msg.date ? msg.date * 1000 : undefined, body: `${bodyText}${replySuffix}`, }); @@ -874,7 +874,10 @@ function buildSenderName(msg: TelegramMessage) { return name || undefined; } -function buildSenderLabel(msg: TelegramMessage, chatId: number | string) { +function buildSenderLabel( + msg: TelegramMessage, + senderId?: number | string, +) { const name = buildSenderName(msg); const username = msg.from?.username ? `@${msg.from.username}` : undefined; let label = name; @@ -883,8 +886,17 @@ function buildSenderLabel(msg: TelegramMessage, chatId: number | string) { } else if (!name && username) { label = username; } - const idPart = `id:${chatId}`; - return label ? `${label} ${idPart}` : idPart; + const normalizedSenderId = + senderId != null && `${senderId}`.trim() + ? `${senderId}`.trim() + : undefined; + const fallbackId = + normalizedSenderId ?? + (msg.from?.id != null ? String(msg.from.id) : undefined); + const idPart = fallbackId ? `id:${fallbackId}` : undefined; + if (label && idPart) return `${label} ${idPart}`; + if (label) return label; + return idPart ?? "id:unknown"; } function buildGroupLabel(msg: TelegramMessage, chatId: number | string) { @@ -893,6 +905,16 @@ function buildGroupLabel(msg: TelegramMessage, chatId: number | string) { return `group:${chatId}`; } +function buildGroupFromLabel( + msg: TelegramMessage, + chatId: number | string, + senderId?: number | string, +) { + const groupLabel = buildGroupLabel(msg, chatId); + const senderLabel = buildSenderLabel(msg, senderId); + return `${groupLabel} from ${senderLabel}`; +} + function hasBotMention(msg: TelegramMessage, botUsername: string) { const text = (msg.text ?? msg.caption ?? "").toLowerCase(); if (text.includes(`@${botUsername}`)) return true;