diff --git a/src/telegram/bot.test.ts b/src/telegram/bot.test.ts index 65caf1ebf..15d095459 100644 --- a/src/telegram/bot.test.ts +++ b/src/telegram/bot.test.ts @@ -887,6 +887,53 @@ describe("createTelegramBot", () => { expect(replySpy).toHaveBeenCalledTimes(1); }); + it("routes DMs by telegram accountId binding", async () => { + onSpy.mockReset(); + const replySpy = replyModule.__replySpy as unknown as ReturnType< + typeof vi.fn + >; + replySpy.mockReset(); + + loadConfig.mockReturnValue({ + telegram: { + accounts: { + opie: { + botToken: "tok-opie", + dmPolicy: "open", + }, + }, + }, + bindings: [ + { + agentId: "opie", + match: { provider: "telegram", accountId: "opie" }, + }, + ], + }); + + createTelegramBot({ token: "tok", accountId: "opie" }); + const handler = getOnHandler("message") as ( + ctx: Record, + ) => Promise; + + await handler({ + message: { + chat: { id: 123, type: "private" }, + from: { id: 999, username: "testuser" }, + text: "hello", + date: 1736380800, + message_id: 42, + }, + me: { username: "clawdbot_bot" }, + getFile: async () => ({ download: async () => new Uint8Array() }), + }); + + expect(replySpy).toHaveBeenCalledTimes(1); + const payload = replySpy.mock.calls[0][0]; + expect(payload.AccountId).toBe("opie"); + expect(payload.SessionKey).toBe("agent:opie:main"); + }); + it("allows per-group requireMention override", async () => { onSpy.mockReset(); const replySpy = replyModule.__replySpy as unknown as ReturnType< diff --git a/src/telegram/monitor.ts b/src/telegram/monitor.ts index 6ae8e45bb..7ed3f2faf 100644 --- a/src/telegram/monitor.ts +++ b/src/telegram/monitor.ts @@ -96,6 +96,8 @@ export async function monitorTelegramProvider(opts: MonitorTelegramOpts = {}) { if (opts.useWebhook) { await startTelegramWebhook({ token, + accountId: account.accountId, + config: cfg, path: opts.webhookPath, port: opts.webhookPort, secret: opts.webhookSecret, diff --git a/src/telegram/webhook.test.ts b/src/telegram/webhook.test.ts index 657cab274..ba5aa83cc 100644 --- a/src/telegram/webhook.test.ts +++ b/src/telegram/webhook.test.ts @@ -14,25 +14,33 @@ const handlerSpy = vi.fn( const setWebhookSpy = vi.fn(); const stopSpy = vi.fn(); +const createTelegramBotSpy = vi.fn(() => ({ + api: { setWebhook: setWebhookSpy }, + stop: stopSpy, +})); + vi.mock("grammy", () => ({ webhookCallback: () => handlerSpy, })); vi.mock("./bot.js", () => ({ - createTelegramBot: () => ({ - api: { setWebhook: setWebhookSpy }, - stop: stopSpy, - }), + createTelegramBot: (...args: unknown[]) => createTelegramBotSpy(...args), })); describe("startTelegramWebhook", () => { it("starts server, registers webhook, and serves health", async () => { + createTelegramBotSpy.mockClear(); const abort = new AbortController(); const { server } = await startTelegramWebhook({ token: "tok", + accountId: "opie", + config: { bindings: [] }, port: 0, // random free port abortSignal: abort.signal, }); + expect(createTelegramBotSpy).toHaveBeenCalledWith( + expect.objectContaining({ accountId: "opie" }), + ); const address = server.address(); if (!address || typeof address === "string") throw new Error("no address"); const url = `http://127.0.0.1:${address.port}`; @@ -46,9 +54,12 @@ describe("startTelegramWebhook", () => { it("invokes webhook handler on matching path", async () => { handlerSpy.mockClear(); + createTelegramBotSpy.mockClear(); const abort = new AbortController(); const { server } = await startTelegramWebhook({ token: "tok", + accountId: "opie", + config: { bindings: [] }, port: 0, abortSignal: abort.signal, path: "/hook", diff --git a/src/telegram/webhook.ts b/src/telegram/webhook.ts index b0b26b6c7..479f98466 100644 --- a/src/telegram/webhook.ts +++ b/src/telegram/webhook.ts @@ -1,6 +1,7 @@ import { createServer } from "node:http"; import { webhookCallback } from "grammy"; +import type { ClawdbotConfig } from "../config/config.js"; import { formatErrorMessage } from "../infra/errors.js"; import type { RuntimeEnv } from "../runtime.js"; import { defaultRuntime } from "../runtime.js"; @@ -8,6 +9,8 @@ import { createTelegramBot } from "./bot.js"; export async function startTelegramWebhook(opts: { token: string; + accountId?: string; + config?: ClawdbotConfig; path?: string; port?: number; host?: string; @@ -27,6 +30,8 @@ export async function startTelegramWebhook(opts: { token: opts.token, runtime, proxyFetch: opts.fetch, + config: opts.config, + accountId: opts.accountId, }); const handler = webhookCallback(bot, "http", { secretToken: opts.secret,