openclaw-matrix-multiaccounts/src/agents/tools/telegram-actions.test.ts
mneves75 33e2d53be3 feat(telegram): wire replyToMode config, add forum topic support, fix messaging tool duplicates
Changes:
- Default replyToMode from "off" to "first" for better threading UX
- Add messageThreadId and replyToMessageId params for forum topic support
- Add messaging tool duplicate detection to suppress redundant block replies
- Add sendMessage action to telegram tool schema
- Add @grammyjs/types devDependency for proper TypeScript typing
- Remove @ts-nocheck and fix all type errors in send.ts
- Add comprehensive docs/telegram.md documentation
- Add PR-326-REVIEW.md with John Carmack-level code review

Test coverage:
- normalizeTextForComparison: 5 cases
- isMessagingToolDuplicate: 7 cases
- sendMessageTelegram thread params: 5 cases
- handleTelegramAction sendMessage: 4 cases
- Forum topic isolation: 4 cases

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 00:50:47 +00:00

171 lines
4.4 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import type { ClawdbotConfig } from "../../config/config.js";
import { handleTelegramAction } from "./telegram-actions.js";
const reactMessageTelegram = vi.fn(async () => ({ ok: true }));
const sendMessageTelegram = vi.fn(async () => ({
messageId: "789",
chatId: "123",
}));
const originalToken = process.env.TELEGRAM_BOT_TOKEN;
vi.mock("../../telegram/send.js", () => ({
reactMessageTelegram: (...args: unknown[]) => reactMessageTelegram(...args),
sendMessageTelegram: (...args: unknown[]) => sendMessageTelegram(...args),
}));
describe("handleTelegramAction", () => {
beforeEach(() => {
reactMessageTelegram.mockClear();
sendMessageTelegram.mockClear();
process.env.TELEGRAM_BOT_TOKEN = "tok";
});
afterEach(() => {
if (originalToken === undefined) {
delete process.env.TELEGRAM_BOT_TOKEN;
} else {
process.env.TELEGRAM_BOT_TOKEN = originalToken;
}
});
it("adds reactions", async () => {
const cfg = { telegram: { botToken: "tok" } } as ClawdbotConfig;
await handleTelegramAction(
{
action: "react",
chatId: "123",
messageId: "456",
emoji: "✅",
},
cfg,
);
expect(reactMessageTelegram).toHaveBeenCalledWith("123", 456, "✅", {
token: "tok",
remove: false,
});
});
it("removes reactions on empty emoji", async () => {
const cfg = { telegram: { botToken: "tok" } } as ClawdbotConfig;
await handleTelegramAction(
{
action: "react",
chatId: "123",
messageId: "456",
emoji: "",
},
cfg,
);
expect(reactMessageTelegram).toHaveBeenCalledWith("123", 456, "", {
token: "tok",
remove: false,
});
});
it("removes reactions when remove flag set", async () => {
const cfg = { telegram: { botToken: "tok" } } as ClawdbotConfig;
await handleTelegramAction(
{
action: "react",
chatId: "123",
messageId: "456",
emoji: "✅",
remove: true,
},
cfg,
);
expect(reactMessageTelegram).toHaveBeenCalledWith("123", 456, "✅", {
token: "tok",
remove: true,
});
});
it("respects reaction gating", async () => {
const cfg = {
telegram: { botToken: "tok", actions: { reactions: false } },
} as ClawdbotConfig;
await expect(
handleTelegramAction(
{
action: "react",
chatId: "123",
messageId: "456",
emoji: "✅",
},
cfg,
),
).rejects.toThrow(/Telegram reactions are disabled/);
});
it("sends a text message", async () => {
const cfg = { telegram: { botToken: "tok" } } as ClawdbotConfig;
const result = await handleTelegramAction(
{
action: "sendMessage",
to: "@testchannel",
content: "Hello, Telegram!",
},
cfg,
);
expect(sendMessageTelegram).toHaveBeenCalledWith(
"@testchannel",
"Hello, Telegram!",
{ token: "tok", mediaUrl: undefined },
);
expect(result.content).toContainEqual({
type: "text",
text: expect.stringContaining('"ok": true'),
});
});
it("sends a message with media", async () => {
const cfg = { telegram: { botToken: "tok" } } as ClawdbotConfig;
await handleTelegramAction(
{
action: "sendMessage",
to: "123456",
content: "Check this image!",
mediaUrl: "https://example.com/image.jpg",
},
cfg,
);
expect(sendMessageTelegram).toHaveBeenCalledWith(
"123456",
"Check this image!",
{ token: "tok", mediaUrl: "https://example.com/image.jpg" },
);
});
it("respects sendMessage gating", async () => {
const cfg = {
telegram: { botToken: "tok", actions: { sendMessage: false } },
} as ClawdbotConfig;
await expect(
handleTelegramAction(
{
action: "sendMessage",
to: "@testchannel",
content: "Hello!",
},
cfg,
),
).rejects.toThrow(/Telegram sendMessage is disabled/);
});
it("throws on missing bot token for sendMessage", async () => {
delete process.env.TELEGRAM_BOT_TOKEN;
const cfg = {} as ClawdbotConfig;
await expect(
handleTelegramAction(
{
action: "sendMessage",
to: "@testchannel",
content: "Hello!",
},
cfg,
),
).rejects.toThrow(/Telegram bot token missing/);
});
});