mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-09 08:11:09 +00:00
Telegram tests: harden dispatch and pairing scenarios
This commit is contained in:
@@ -10,7 +10,14 @@ import {
|
||||
const createTelegramDraftStream = vi.hoisted(() => vi.fn());
|
||||
const dispatchReplyWithBufferedBlockDispatcher = vi.hoisted(() => vi.fn());
|
||||
const deliverReplies = vi.hoisted(() => vi.fn());
|
||||
const createForumTopicTelegram = vi.hoisted(() => vi.fn());
|
||||
const deleteMessageTelegram = vi.hoisted(() => vi.fn());
|
||||
const editForumTopicTelegram = vi.hoisted(() => vi.fn());
|
||||
const editMessageTelegram = vi.hoisted(() => vi.fn());
|
||||
const reactMessageTelegram = vi.hoisted(() => vi.fn());
|
||||
const sendMessageTelegram = vi.hoisted(() => vi.fn());
|
||||
const sendPollTelegram = vi.hoisted(() => vi.fn());
|
||||
const sendStickerTelegram = vi.hoisted(() => vi.fn());
|
||||
const loadSessionStore = vi.hoisted(() => vi.fn());
|
||||
const resolveStorePath = vi.hoisted(() => vi.fn(() => "/tmp/sessions.json"));
|
||||
|
||||
@@ -18,23 +25,30 @@ vi.mock("./draft-stream.js", () => ({
|
||||
createTelegramDraftStream,
|
||||
}));
|
||||
|
||||
vi.mock("../../../src/auto-reply/reply/provider-dispatcher.js", () => ({
|
||||
dispatchReplyWithBufferedBlockDispatcher,
|
||||
}));
|
||||
vi.mock("./bot-deps.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("./bot-deps.js")>();
|
||||
return {
|
||||
...actual,
|
||||
defaultTelegramBotDeps: {
|
||||
...actual.defaultTelegramBotDeps,
|
||||
dispatchReplyWithBufferedBlockDispatcher,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("./bot/delivery.js", () => ({
|
||||
deliverReplies,
|
||||
}));
|
||||
|
||||
vi.mock("./send.js", () => ({
|
||||
createForumTopicTelegram: vi.fn(),
|
||||
deleteMessageTelegram: vi.fn(),
|
||||
editForumTopicTelegram: vi.fn(),
|
||||
createForumTopicTelegram,
|
||||
deleteMessageTelegram,
|
||||
editForumTopicTelegram,
|
||||
editMessageTelegram,
|
||||
reactMessageTelegram: vi.fn(),
|
||||
sendMessageTelegram: vi.fn(),
|
||||
sendPollTelegram: vi.fn(),
|
||||
sendStickerTelegram: vi.fn(),
|
||||
reactMessageTelegram,
|
||||
sendMessageTelegram,
|
||||
sendPollTelegram,
|
||||
sendStickerTelegram,
|
||||
}));
|
||||
|
||||
vi.mock("openclaw/plugin-sdk/config-runtime", async (importOriginal) => {
|
||||
|
||||
@@ -13,7 +13,6 @@ const {
|
||||
commandSpy,
|
||||
dispatchReplyWithBufferedBlockDispatcher,
|
||||
getLoadConfigMock,
|
||||
getLoadWebMediaMock,
|
||||
getOnHandler,
|
||||
getReadChannelAllowFromStoreMock,
|
||||
getUpsertChannelPairingRequestMock,
|
||||
@@ -51,7 +50,6 @@ const createTelegramBot = (opts: Parameters<typeof createTelegramBotBase>[0]) =>
|
||||
});
|
||||
|
||||
const loadConfig = getLoadConfigMock();
|
||||
const loadWebMedia = getLoadWebMediaMock();
|
||||
const readChannelAllowFromStore = getReadChannelAllowFromStoreMock();
|
||||
const upsertChannelPairingRequest = getUpsertChannelPairingRequestMock();
|
||||
|
||||
@@ -61,6 +59,30 @@ const TELEGRAM_TEST_TIMINGS = {
|
||||
textFragmentGapMs: 30,
|
||||
} as const;
|
||||
|
||||
async function withIsolatedStateDirAsync<T>(fn: () => Promise<T>): Promise<T> {
|
||||
const stateDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-telegram-state-"));
|
||||
return await withEnvAsync({ OPENCLAW_STATE_DIR: stateDir }, async () => {
|
||||
try {
|
||||
return await fn();
|
||||
} finally {
|
||||
fs.rmSync(stateDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function withConfigPathAsync<T>(cfg: unknown, fn: () => Promise<T>): Promise<T> {
|
||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-telegram-cfg-"));
|
||||
const configPath = path.join(dir, "openclaw.json");
|
||||
fs.writeFileSync(configPath, JSON.stringify(cfg), "utf-8");
|
||||
return await withEnvAsync({ OPENCLAW_CONFIG_PATH: configPath }, async () => {
|
||||
try {
|
||||
return await fn();
|
||||
} finally {
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
describe("createTelegramBot", () => {
|
||||
beforeAll(() => {
|
||||
process.env.TZ = "UTC";
|
||||
@@ -199,107 +221,102 @@ describe("createTelegramBot", () => {
|
||||
const cases = [
|
||||
{
|
||||
name: "new unknown sender",
|
||||
upsertResults: [{ code: "PAIRME12", created: true }],
|
||||
messages: ["hello"],
|
||||
expectedSendCount: 1,
|
||||
expectPairingText: true,
|
||||
},
|
||||
{
|
||||
name: "already pending request",
|
||||
upsertResults: [
|
||||
{ code: "PAIRME12", created: true },
|
||||
{ code: "PAIRME12", created: false },
|
||||
],
|
||||
messages: ["hello", "hello again"],
|
||||
expectedSendCount: 1,
|
||||
expectPairingText: false,
|
||||
},
|
||||
] as const;
|
||||
|
||||
for (const testCase of cases) {
|
||||
onSpy.mockClear();
|
||||
sendMessageSpy.mockClear();
|
||||
replySpy.mockClear();
|
||||
await withIsolatedStateDirAsync(async () => {
|
||||
for (const [index, testCase] of cases.entries()) {
|
||||
onSpy.mockClear();
|
||||
sendMessageSpy.mockClear();
|
||||
replySpy.mockClear();
|
||||
loadConfig.mockReturnValue({
|
||||
channels: { telegram: { dmPolicy: "pairing" } },
|
||||
});
|
||||
readChannelAllowFromStore.mockResolvedValue([]);
|
||||
upsertChannelPairingRequest.mockClear();
|
||||
upsertChannelPairingRequest.mockResolvedValue({ code: "PAIRCODE", created: true });
|
||||
|
||||
createTelegramBot({ token: "tok" });
|
||||
const handler = getOnHandler("message") as (ctx: Record<string, unknown>) => Promise<void>;
|
||||
const senderId = Number(`${Date.now()}${index}`.slice(-9));
|
||||
for (const text of testCase.messages) {
|
||||
await handler({
|
||||
message: {
|
||||
chat: { id: 1234, type: "private" },
|
||||
text,
|
||||
date: 1736380800,
|
||||
from: { id: senderId, username: "random" },
|
||||
},
|
||||
me: { username: "openclaw_bot" },
|
||||
getFile: async () => ({ download: async () => new Uint8Array() }),
|
||||
});
|
||||
}
|
||||
|
||||
expect(replySpy, testCase.name).not.toHaveBeenCalled();
|
||||
expect(sendMessageSpy, testCase.name).toHaveBeenCalledTimes(testCase.expectedSendCount);
|
||||
expect(sendMessageSpy.mock.calls[0]?.[0], testCase.name).toBe(1234);
|
||||
const pairingText = String(sendMessageSpy.mock.calls[0]?.[1]);
|
||||
expect(pairingText, testCase.name).toContain(`Your Telegram user id: ${senderId}`);
|
||||
expect(pairingText, testCase.name).toContain("Pairing code:");
|
||||
const code = pairingText.match(/Pairing code:\s*([A-Z2-9]{8})/)?.[1];
|
||||
expect(code, testCase.name).toBeDefined();
|
||||
expect(pairingText, testCase.name).toContain(`openclaw pairing approve telegram ${code}`);
|
||||
expect(pairingText, testCase.name).not.toContain("<code>");
|
||||
}
|
||||
});
|
||||
});
|
||||
it("blocks unauthorized DM media before download and sends pairing reply", async () => {
|
||||
await withIsolatedStateDirAsync(async () => {
|
||||
loadConfig.mockReturnValue({
|
||||
channels: { telegram: { dmPolicy: "pairing" } },
|
||||
});
|
||||
readChannelAllowFromStore.mockResolvedValue([]);
|
||||
upsertChannelPairingRequest.mockClear();
|
||||
upsertChannelPairingRequest.mockResolvedValue({ code: "PAIRCODE", created: true });
|
||||
for (const result of testCase.upsertResults) {
|
||||
upsertChannelPairingRequest.mockResolvedValueOnce(result);
|
||||
}
|
||||
upsertChannelPairingRequest.mockResolvedValue({ code: "PAIRME12", created: true });
|
||||
sendMessageSpy.mockClear();
|
||||
replySpy.mockClear();
|
||||
const senderId = Number(`${Date.now()}01`.slice(-9));
|
||||
|
||||
const fetchSpy = vi.spyOn(globalThis, "fetch").mockImplementation(
|
||||
async () =>
|
||||
new Response(new Uint8Array([0xff, 0xd8, 0xff, 0x00]), {
|
||||
status: 200,
|
||||
headers: { "content-type": "image/jpeg" },
|
||||
}),
|
||||
);
|
||||
const getFileSpy = vi.fn(async () => ({ file_path: "photos/p1.jpg" }));
|
||||
|
||||
try {
|
||||
createTelegramBot({ token: "tok" });
|
||||
const handler = getOnHandler("message") as (ctx: Record<string, unknown>) => Promise<void>;
|
||||
|
||||
createTelegramBot({ token: "tok" });
|
||||
const handler = getOnHandler("message") as (ctx: Record<string, unknown>) => Promise<void>;
|
||||
for (const text of testCase.messages) {
|
||||
await handler({
|
||||
message: {
|
||||
chat: { id: 1234, type: "private" },
|
||||
text,
|
||||
message_id: 410,
|
||||
date: 1736380800,
|
||||
from: { id: 999, username: "random" },
|
||||
photo: [{ file_id: "p1" }],
|
||||
from: { id: senderId, username: "random" },
|
||||
},
|
||||
me: { username: "openclaw_bot" },
|
||||
getFile: async () => ({ download: async () => new Uint8Array() }),
|
||||
getFile: getFileSpy,
|
||||
});
|
||||
}
|
||||
|
||||
expect(replySpy, testCase.name).not.toHaveBeenCalled();
|
||||
expect(sendMessageSpy, testCase.name).toHaveBeenCalledTimes(testCase.expectedSendCount);
|
||||
if (testCase.expectPairingText) {
|
||||
expect(sendMessageSpy.mock.calls[0]?.[0], testCase.name).toBe(1234);
|
||||
const pairingText = String(sendMessageSpy.mock.calls[0]?.[1]);
|
||||
expect(pairingText, testCase.name).toContain("Your Telegram user id: 999");
|
||||
expect(pairingText, testCase.name).toContain("Pairing code:");
|
||||
expect(pairingText, testCase.name).toContain("PAIRME12");
|
||||
expect(pairingText, testCase.name).toContain("openclaw pairing approve telegram PAIRME12");
|
||||
expect(pairingText, testCase.name).not.toContain("<code>");
|
||||
expect(getFileSpy).not.toHaveBeenCalled();
|
||||
expect(fetchSpy).not.toHaveBeenCalled();
|
||||
expect(sendMessageSpy).toHaveBeenCalledTimes(1);
|
||||
expect(String(sendMessageSpy.mock.calls[0]?.[1])).toContain("Pairing code:");
|
||||
expect(replySpy).not.toHaveBeenCalled();
|
||||
} finally {
|
||||
fetchSpy.mockRestore();
|
||||
}
|
||||
}
|
||||
});
|
||||
it("blocks unauthorized DM media before download and sends pairing reply", async () => {
|
||||
loadConfig.mockReturnValue({
|
||||
channels: { telegram: { dmPolicy: "pairing" } },
|
||||
});
|
||||
readChannelAllowFromStore.mockResolvedValue([]);
|
||||
upsertChannelPairingRequest.mockResolvedValue({ code: "PAIRME12", created: true });
|
||||
sendMessageSpy.mockClear();
|
||||
replySpy.mockClear();
|
||||
|
||||
const fetchSpy = vi.spyOn(globalThis, "fetch").mockImplementation(
|
||||
async () =>
|
||||
new Response(new Uint8Array([0xff, 0xd8, 0xff, 0x00]), {
|
||||
status: 200,
|
||||
headers: { "content-type": "image/jpeg" },
|
||||
}),
|
||||
);
|
||||
const getFileSpy = vi.fn(async () => ({ file_path: "photos/p1.jpg" }));
|
||||
|
||||
try {
|
||||
createTelegramBot({ token: "tok" });
|
||||
const handler = getOnHandler("message") as (ctx: Record<string, unknown>) => Promise<void>;
|
||||
|
||||
await handler({
|
||||
message: {
|
||||
chat: { id: 1234, type: "private" },
|
||||
message_id: 410,
|
||||
date: 1736380800,
|
||||
photo: [{ file_id: "p1" }],
|
||||
from: { id: 999, username: "random" },
|
||||
},
|
||||
me: { username: "openclaw_bot" },
|
||||
getFile: getFileSpy,
|
||||
});
|
||||
|
||||
expect(getFileSpy).not.toHaveBeenCalled();
|
||||
expect(fetchSpy).not.toHaveBeenCalled();
|
||||
expect(sendMessageSpy).toHaveBeenCalledTimes(1);
|
||||
expect(String(sendMessageSpy.mock.calls[0]?.[1])).toContain("Pairing code:");
|
||||
expect(replySpy).not.toHaveBeenCalled();
|
||||
} finally {
|
||||
fetchSpy.mockRestore();
|
||||
}
|
||||
});
|
||||
it("blocks DM media downloads completely when dmPolicy is disabled", async () => {
|
||||
loadConfig.mockReturnValue({
|
||||
@@ -342,48 +359,51 @@ describe("createTelegramBot", () => {
|
||||
}
|
||||
});
|
||||
it("blocks unauthorized DM media groups before any photo download", async () => {
|
||||
loadConfig.mockReturnValue({
|
||||
channels: { telegram: { dmPolicy: "pairing" } },
|
||||
});
|
||||
readChannelAllowFromStore.mockResolvedValue([]);
|
||||
upsertChannelPairingRequest.mockResolvedValue({ code: "PAIRME12", created: true });
|
||||
sendMessageSpy.mockClear();
|
||||
replySpy.mockClear();
|
||||
|
||||
const fetchSpy = vi.spyOn(globalThis, "fetch").mockImplementation(
|
||||
async () =>
|
||||
new Response(new Uint8Array([0xff, 0xd8, 0xff, 0x00]), {
|
||||
status: 200,
|
||||
headers: { "content-type": "image/jpeg" },
|
||||
}),
|
||||
);
|
||||
const getFileSpy = vi.fn(async () => ({ file_path: "photos/p1.jpg" }));
|
||||
|
||||
try {
|
||||
createTelegramBot({ token: "tok", testTimings: TELEGRAM_TEST_TIMINGS });
|
||||
const handler = getOnHandler("message") as (ctx: Record<string, unknown>) => Promise<void>;
|
||||
|
||||
await handler({
|
||||
message: {
|
||||
chat: { id: 1234, type: "private" },
|
||||
message_id: 412,
|
||||
media_group_id: "dm-album-1",
|
||||
date: 1736380800,
|
||||
photo: [{ file_id: "p1" }],
|
||||
from: { id: 999, username: "random" },
|
||||
},
|
||||
me: { username: "openclaw_bot" },
|
||||
getFile: getFileSpy,
|
||||
await withIsolatedStateDirAsync(async () => {
|
||||
loadConfig.mockReturnValue({
|
||||
channels: { telegram: { dmPolicy: "pairing" } },
|
||||
});
|
||||
readChannelAllowFromStore.mockResolvedValue([]);
|
||||
upsertChannelPairingRequest.mockResolvedValue({ code: "PAIRME12", created: true });
|
||||
sendMessageSpy.mockClear();
|
||||
replySpy.mockClear();
|
||||
const senderId = Number(`${Date.now()}02`.slice(-9));
|
||||
|
||||
expect(getFileSpy).not.toHaveBeenCalled();
|
||||
expect(fetchSpy).not.toHaveBeenCalled();
|
||||
expect(sendMessageSpy).toHaveBeenCalledTimes(1);
|
||||
expect(String(sendMessageSpy.mock.calls[0]?.[1])).toContain("Pairing code:");
|
||||
expect(replySpy).not.toHaveBeenCalled();
|
||||
} finally {
|
||||
fetchSpy.mockRestore();
|
||||
}
|
||||
const fetchSpy = vi.spyOn(globalThis, "fetch").mockImplementation(
|
||||
async () =>
|
||||
new Response(new Uint8Array([0xff, 0xd8, 0xff, 0x00]), {
|
||||
status: 200,
|
||||
headers: { "content-type": "image/jpeg" },
|
||||
}),
|
||||
);
|
||||
const getFileSpy = vi.fn(async () => ({ file_path: "photos/p1.jpg" }));
|
||||
|
||||
try {
|
||||
createTelegramBot({ token: "tok", testTimings: TELEGRAM_TEST_TIMINGS });
|
||||
const handler = getOnHandler("message") as (ctx: Record<string, unknown>) => Promise<void>;
|
||||
|
||||
await handler({
|
||||
message: {
|
||||
chat: { id: 1234, type: "private" },
|
||||
message_id: 412,
|
||||
media_group_id: "dm-album-1",
|
||||
date: 1736380800,
|
||||
photo: [{ file_id: "p1" }],
|
||||
from: { id: senderId, username: "random" },
|
||||
},
|
||||
me: { username: "openclaw_bot" },
|
||||
getFile: getFileSpy,
|
||||
});
|
||||
|
||||
expect(getFileSpy).not.toHaveBeenCalled();
|
||||
expect(fetchSpy).not.toHaveBeenCalled();
|
||||
expect(sendMessageSpy).toHaveBeenCalledTimes(1);
|
||||
expect(String(sendMessageSpy.mock.calls[0]?.[1])).toContain("Pairing code:");
|
||||
expect(replySpy).not.toHaveBeenCalled();
|
||||
} finally {
|
||||
fetchSpy.mockRestore();
|
||||
}
|
||||
});
|
||||
});
|
||||
it("triggers typing cue via onReplyStart", async () => {
|
||||
dispatchReplyWithBufferedBlockDispatcher.mockImplementationOnce(
|
||||
@@ -800,13 +820,15 @@ describe("createTelegramBot", () => {
|
||||
});
|
||||
|
||||
it("routes DMs by telegram accountId binding", async () => {
|
||||
loadConfig.mockReturnValue({
|
||||
const config = {
|
||||
channels: {
|
||||
telegram: {
|
||||
allowFrom: ["*"],
|
||||
accounts: {
|
||||
opie: {
|
||||
botToken: "tok-opie",
|
||||
dmPolicy: "open",
|
||||
allowFrom: ["*"],
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -817,27 +839,30 @@ describe("createTelegramBot", () => {
|
||||
match: { channel: "telegram", accountId: "opie" },
|
||||
},
|
||||
],
|
||||
};
|
||||
loadConfig.mockReturnValue(config);
|
||||
|
||||
await withConfigPathAsync(config, async () => {
|
||||
createTelegramBot({ token: "tok", accountId: "opie" });
|
||||
const handler = getOnHandler("message") as (ctx: Record<string, unknown>) => Promise<void>;
|
||||
|
||||
await handler({
|
||||
message: {
|
||||
chat: { id: 123, type: "private" },
|
||||
from: { id: 999, username: "testuser" },
|
||||
text: "hello",
|
||||
date: 1736380800,
|
||||
message_id: 42,
|
||||
},
|
||||
me: { username: "openclaw_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");
|
||||
});
|
||||
|
||||
createTelegramBot({ token: "tok", accountId: "opie" });
|
||||
const handler = getOnHandler("message") as (ctx: Record<string, unknown>) => Promise<void>;
|
||||
|
||||
await handler({
|
||||
message: {
|
||||
chat: { id: 123, type: "private" },
|
||||
from: { id: 999, username: "testuser" },
|
||||
text: "hello",
|
||||
date: 1736380800,
|
||||
message_id: 42,
|
||||
},
|
||||
me: { username: "openclaw_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("routes non-default account DMs to the per-account fallback session without explicit bindings", async () => {
|
||||
@@ -1036,26 +1061,28 @@ describe("createTelegramBot", () => {
|
||||
];
|
||||
|
||||
for (const testCase of cases) {
|
||||
resetHarnessSpies();
|
||||
loadConfig.mockReturnValue(testCase.config);
|
||||
await dispatchMessage({
|
||||
message: {
|
||||
chat: {
|
||||
id: -1001234567890,
|
||||
type: "supergroup",
|
||||
title: "Forum Group",
|
||||
is_forum: true,
|
||||
await withConfigPathAsync(testCase.config, async () => {
|
||||
resetHarnessSpies();
|
||||
loadConfig.mockReturnValue(testCase.config);
|
||||
await dispatchMessage({
|
||||
message: {
|
||||
chat: {
|
||||
id: -1001234567890,
|
||||
type: "supergroup",
|
||||
title: "Forum Group",
|
||||
is_forum: true,
|
||||
},
|
||||
from: { id: 999, username: "testuser" },
|
||||
text: testCase.text,
|
||||
date: 1736380800,
|
||||
message_id: 42,
|
||||
message_thread_id: 99,
|
||||
},
|
||||
from: { id: 999, username: "testuser" },
|
||||
text: testCase.text,
|
||||
date: 1736380800,
|
||||
message_id: 42,
|
||||
message_thread_id: 99,
|
||||
},
|
||||
});
|
||||
expect(replySpy).toHaveBeenCalledTimes(1);
|
||||
const payload = replySpy.mock.calls[0][0];
|
||||
expect(payload.SessionKey).toContain(testCase.expectedSessionKeyFragment);
|
||||
});
|
||||
expect(replySpy).toHaveBeenCalledTimes(1);
|
||||
const payload = replySpy.mock.calls[0][0];
|
||||
expect(payload.SessionKey).toContain(testCase.expectedSessionKeyFragment);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1064,35 +1091,38 @@ describe("createTelegramBot", () => {
|
||||
text: "caption",
|
||||
mediaUrl: "https://example.com/fun",
|
||||
});
|
||||
const fetchSpy = vi.spyOn(globalThis, "fetch").mockResolvedValue(
|
||||
new Response(Buffer.from("GIF89a"), {
|
||||
status: 200,
|
||||
headers: { "content-type": "image/gif" },
|
||||
}),
|
||||
);
|
||||
try {
|
||||
createTelegramBot({ token: "tok" });
|
||||
const handler = getOnHandler("message") as (ctx: Record<string, unknown>) => Promise<void>;
|
||||
|
||||
loadWebMedia.mockResolvedValueOnce({
|
||||
buffer: Buffer.from("GIF89a"),
|
||||
contentType: "image/gif",
|
||||
fileName: "fun.gif",
|
||||
});
|
||||
await handler({
|
||||
message: {
|
||||
chat: { id: 1234, type: "private" },
|
||||
text: "hello world",
|
||||
date: 1736380800,
|
||||
message_id: 5,
|
||||
from: { first_name: "Ada" },
|
||||
},
|
||||
me: { username: "openclaw_bot" },
|
||||
getFile: async () => ({ download: async () => new Uint8Array() }),
|
||||
});
|
||||
|
||||
createTelegramBot({ token: "tok" });
|
||||
const handler = getOnHandler("message") as (ctx: Record<string, unknown>) => Promise<void>;
|
||||
|
||||
await handler({
|
||||
message: {
|
||||
chat: { id: 1234, type: "private" },
|
||||
text: "hello world",
|
||||
date: 1736380800,
|
||||
message_id: 5,
|
||||
from: { first_name: "Ada" },
|
||||
},
|
||||
me: { username: "openclaw_bot" },
|
||||
getFile: async () => ({ download: async () => new Uint8Array() }),
|
||||
});
|
||||
|
||||
expect(sendAnimationSpy).toHaveBeenCalledTimes(1);
|
||||
expect(sendAnimationSpy).toHaveBeenCalledWith("1234", expect.anything(), {
|
||||
caption: "caption",
|
||||
parse_mode: "HTML",
|
||||
reply_to_message_id: undefined,
|
||||
});
|
||||
expect(sendPhotoSpy).not.toHaveBeenCalled();
|
||||
expect(sendAnimationSpy).toHaveBeenCalledTimes(1);
|
||||
expect(sendAnimationSpy).toHaveBeenCalledWith("1234", expect.anything(), {
|
||||
caption: "caption",
|
||||
parse_mode: "HTML",
|
||||
reply_to_message_id: undefined,
|
||||
});
|
||||
expect(sendPhotoSpy).not.toHaveBeenCalled();
|
||||
} finally {
|
||||
fetchSpy.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
function resetHarnessSpies() {
|
||||
@@ -1746,7 +1776,7 @@ describe("createTelegramBot", () => {
|
||||
}),
|
||||
"utf-8",
|
||||
);
|
||||
loadConfig.mockReturnValue({
|
||||
const config = {
|
||||
channels: {
|
||||
telegram: {
|
||||
groupPolicy: "open",
|
||||
@@ -1763,23 +1793,26 @@ describe("createTelegramBot", () => {
|
||||
},
|
||||
],
|
||||
session: { store: storePath },
|
||||
};
|
||||
loadConfig.mockReturnValue(config);
|
||||
|
||||
await withConfigPathAsync(config, async () => {
|
||||
createTelegramBot({ token: "tok" });
|
||||
const handler = getOnHandler("message") as (ctx: Record<string, unknown>) => Promise<void>;
|
||||
|
||||
await handler({
|
||||
message: {
|
||||
chat: { id: 123, type: "group", title: "Routing" },
|
||||
from: { id: 999, username: "ops" },
|
||||
text: "hello",
|
||||
date: 1736380800,
|
||||
},
|
||||
me: { username: "openclaw_bot" },
|
||||
getFile: async () => ({ download: async () => new Uint8Array() }),
|
||||
});
|
||||
|
||||
expect(replySpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
createTelegramBot({ token: "tok" });
|
||||
const handler = getOnHandler("message") as (ctx: Record<string, unknown>) => Promise<void>;
|
||||
|
||||
await handler({
|
||||
message: {
|
||||
chat: { id: 123, type: "group", title: "Routing" },
|
||||
from: { id: 999, username: "ops" },
|
||||
text: "hello",
|
||||
date: 1736380800,
|
||||
},
|
||||
me: { username: "openclaw_bot" },
|
||||
getFile: async () => ({ download: async () => new Uint8Array() }),
|
||||
});
|
||||
|
||||
expect(replySpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("applies topic skill filters and system prompts", async () => {
|
||||
|
||||
Reference in New Issue
Block a user