|
|
|
|
@@ -1608,160 +1608,6 @@ describe("createTelegramBot", () => {
|
|
|
|
|
expect(replySpy).not.toHaveBeenCalled();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("blocks group messages from senders not in allowFrom when groupPolicy is 'allowlist'", async () => {
|
|
|
|
|
onSpy.mockReset();
|
|
|
|
|
const replySpy = replyModule.__replySpy as unknown as ReturnType<typeof vi.fn>;
|
|
|
|
|
replySpy.mockReset();
|
|
|
|
|
loadConfig.mockReturnValue({
|
|
|
|
|
channels: {
|
|
|
|
|
telegram: {
|
|
|
|
|
groupPolicy: "allowlist",
|
|
|
|
|
allowFrom: ["123456789"], // Does not include sender 999999
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
createTelegramBot({ token: "tok" });
|
|
|
|
|
const handler = getOnHandler("message") as (ctx: Record<string, unknown>) => Promise<void>;
|
|
|
|
|
|
|
|
|
|
await handler({
|
|
|
|
|
message: {
|
|
|
|
|
chat: { id: -100123456789, type: "group", title: "Test Group" },
|
|
|
|
|
from: { id: 999999, username: "notallowed" }, // Not in allowFrom
|
|
|
|
|
text: "@openclaw_bot hello",
|
|
|
|
|
date: 1736380800,
|
|
|
|
|
},
|
|
|
|
|
me: { username: "openclaw_bot" },
|
|
|
|
|
getFile: async () => ({ download: async () => new Uint8Array() }),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
expect(replySpy).not.toHaveBeenCalled();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("allows group messages from senders in allowFrom (by ID) when groupPolicy is 'allowlist'", async () => {
|
|
|
|
|
onSpy.mockReset();
|
|
|
|
|
const replySpy = replyModule.__replySpy as unknown as ReturnType<typeof vi.fn>;
|
|
|
|
|
replySpy.mockReset();
|
|
|
|
|
loadConfig.mockReturnValue({
|
|
|
|
|
channels: {
|
|
|
|
|
telegram: {
|
|
|
|
|
groupPolicy: "allowlist",
|
|
|
|
|
allowFrom: ["123456789"],
|
|
|
|
|
groups: { "*": { requireMention: false } }, // Skip mention check
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
createTelegramBot({ token: "tok" });
|
|
|
|
|
const handler = getOnHandler("message") as (ctx: Record<string, unknown>) => Promise<void>;
|
|
|
|
|
|
|
|
|
|
await handler({
|
|
|
|
|
message: {
|
|
|
|
|
chat: { id: -100123456789, type: "group", title: "Test Group" },
|
|
|
|
|
from: { id: 123456789, username: "testuser" }, // In allowFrom
|
|
|
|
|
text: "hello",
|
|
|
|
|
date: 1736380800,
|
|
|
|
|
},
|
|
|
|
|
me: { username: "openclaw_bot" },
|
|
|
|
|
getFile: async () => ({ download: async () => new Uint8Array() }),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
expect(replySpy).toHaveBeenCalledTimes(1);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("allows group messages from senders in allowFrom (by username) when groupPolicy is 'allowlist'", async () => {
|
|
|
|
|
onSpy.mockReset();
|
|
|
|
|
const replySpy = replyModule.__replySpy as unknown as ReturnType<typeof vi.fn>;
|
|
|
|
|
replySpy.mockReset();
|
|
|
|
|
loadConfig.mockReturnValue({
|
|
|
|
|
channels: {
|
|
|
|
|
telegram: {
|
|
|
|
|
groupPolicy: "allowlist",
|
|
|
|
|
allowFrom: ["@testuser"], // By username
|
|
|
|
|
groups: { "*": { requireMention: false } },
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
createTelegramBot({ token: "tok" });
|
|
|
|
|
const handler = getOnHandler("message") as (ctx: Record<string, unknown>) => Promise<void>;
|
|
|
|
|
|
|
|
|
|
await handler({
|
|
|
|
|
message: {
|
|
|
|
|
chat: { id: -100123456789, type: "group", title: "Test Group" },
|
|
|
|
|
from: { id: 12345, username: "testuser" }, // Username matches @testuser
|
|
|
|
|
text: "hello",
|
|
|
|
|
date: 1736380800,
|
|
|
|
|
},
|
|
|
|
|
me: { username: "openclaw_bot" },
|
|
|
|
|
getFile: async () => ({ download: async () => new Uint8Array() }),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
expect(replySpy).toHaveBeenCalledTimes(1);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("allows group messages from telegram:-prefixed allowFrom entries when groupPolicy is 'allowlist'", async () => {
|
|
|
|
|
onSpy.mockReset();
|
|
|
|
|
const replySpy = replyModule.__replySpy as unknown as ReturnType<typeof vi.fn>;
|
|
|
|
|
replySpy.mockReset();
|
|
|
|
|
loadConfig.mockReturnValue({
|
|
|
|
|
channels: {
|
|
|
|
|
telegram: {
|
|
|
|
|
groupPolicy: "allowlist",
|
|
|
|
|
allowFrom: ["telegram:77112533"],
|
|
|
|
|
groups: { "*": { requireMention: false } },
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
createTelegramBot({ token: "tok" });
|
|
|
|
|
const handler = getOnHandler("message") as (ctx: Record<string, unknown>) => Promise<void>;
|
|
|
|
|
|
|
|
|
|
await handler({
|
|
|
|
|
message: {
|
|
|
|
|
chat: { id: -100123456789, type: "group", title: "Test Group" },
|
|
|
|
|
from: { id: 77112533, username: "mneves" },
|
|
|
|
|
text: "hello",
|
|
|
|
|
date: 1736380800,
|
|
|
|
|
},
|
|
|
|
|
me: { username: "openclaw_bot" },
|
|
|
|
|
getFile: async () => ({ download: async () => new Uint8Array() }),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
expect(replySpy).toHaveBeenCalledTimes(1);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("allows group messages from tg:-prefixed allowFrom entries case-insensitively when groupPolicy is 'allowlist'", async () => {
|
|
|
|
|
onSpy.mockReset();
|
|
|
|
|
const replySpy = replyModule.__replySpy as unknown as ReturnType<typeof vi.fn>;
|
|
|
|
|
replySpy.mockReset();
|
|
|
|
|
loadConfig.mockReturnValue({
|
|
|
|
|
channels: {
|
|
|
|
|
telegram: {
|
|
|
|
|
groupPolicy: "allowlist",
|
|
|
|
|
allowFrom: ["TG:77112533"],
|
|
|
|
|
groups: { "*": { requireMention: false } },
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
createTelegramBot({ token: "tok" });
|
|
|
|
|
const handler = getOnHandler("message") as (ctx: Record<string, unknown>) => Promise<void>;
|
|
|
|
|
|
|
|
|
|
await handler({
|
|
|
|
|
message: {
|
|
|
|
|
chat: { id: -100123456789, type: "group", title: "Test Group" },
|
|
|
|
|
from: { id: 77112533, username: "mneves" },
|
|
|
|
|
text: "hello",
|
|
|
|
|
date: 1736380800,
|
|
|
|
|
},
|
|
|
|
|
me: { username: "openclaw_bot" },
|
|
|
|
|
getFile: async () => ({ download: async () => new Uint8Array() }),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
expect(replySpy).toHaveBeenCalledTimes(1);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("allows all group messages when groupPolicy is 'open'", async () => {
|
|
|
|
|
onSpy.mockReset();
|
|
|
|
|
const replySpy = replyModule.__replySpy as unknown as ReturnType<typeof vi.fn>;
|
|
|
|
|
@@ -1792,37 +1638,6 @@ describe("createTelegramBot", () => {
|
|
|
|
|
expect(replySpy).toHaveBeenCalledTimes(1);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("matches usernames case-insensitively when groupPolicy is 'allowlist'", async () => {
|
|
|
|
|
onSpy.mockReset();
|
|
|
|
|
const replySpy = replyModule.__replySpy as unknown as ReturnType<typeof vi.fn>;
|
|
|
|
|
replySpy.mockReset();
|
|
|
|
|
loadConfig.mockReturnValue({
|
|
|
|
|
channels: {
|
|
|
|
|
telegram: {
|
|
|
|
|
groupPolicy: "allowlist",
|
|
|
|
|
allowFrom: ["@TestUser"], // Uppercase in config
|
|
|
|
|
groups: { "*": { requireMention: false } },
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
createTelegramBot({ token: "tok" });
|
|
|
|
|
const handler = getOnHandler("message") as (ctx: Record<string, unknown>) => Promise<void>;
|
|
|
|
|
|
|
|
|
|
await handler({
|
|
|
|
|
message: {
|
|
|
|
|
chat: { id: -100123456789, type: "group", title: "Test Group" },
|
|
|
|
|
from: { id: 12345, username: "testuser" }, // Lowercase in message
|
|
|
|
|
text: "hello",
|
|
|
|
|
date: 1736380800,
|
|
|
|
|
},
|
|
|
|
|
me: { username: "openclaw_bot" },
|
|
|
|
|
getFile: async () => ({ download: async () => new Uint8Array() }),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
expect(replySpy).toHaveBeenCalledTimes(1);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("allows direct messages regardless of groupPolicy", async () => {
|
|
|
|
|
onSpy.mockReset();
|
|
|
|
|
const replySpy = replyModule.__replySpy as unknown as ReturnType<typeof vi.fn>;
|
|
|
|
|
@@ -1882,160 +1697,6 @@ describe("createTelegramBot", () => {
|
|
|
|
|
expect(replySpy).toHaveBeenCalledTimes(1);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("allows direct messages with telegram:-prefixed allowFrom entries", async () => {
|
|
|
|
|
onSpy.mockReset();
|
|
|
|
|
const replySpy = replyModule.__replySpy as unknown as ReturnType<typeof vi.fn>;
|
|
|
|
|
replySpy.mockReset();
|
|
|
|
|
loadConfig.mockReturnValue({
|
|
|
|
|
channels: {
|
|
|
|
|
telegram: {
|
|
|
|
|
allowFrom: ["telegram:123456789"],
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
createTelegramBot({ token: "tok" });
|
|
|
|
|
const handler = getOnHandler("message") as (ctx: Record<string, unknown>) => Promise<void>;
|
|
|
|
|
|
|
|
|
|
await handler({
|
|
|
|
|
message: {
|
|
|
|
|
chat: { id: 123456789, type: "private" },
|
|
|
|
|
from: { id: 123456789, username: "testuser" },
|
|
|
|
|
text: "hello",
|
|
|
|
|
date: 1736380800,
|
|
|
|
|
},
|
|
|
|
|
me: { username: "openclaw_bot" },
|
|
|
|
|
getFile: async () => ({ download: async () => new Uint8Array() }),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
expect(replySpy).toHaveBeenCalledTimes(1);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("allows group messages with wildcard in allowFrom when groupPolicy is 'allowlist'", async () => {
|
|
|
|
|
onSpy.mockReset();
|
|
|
|
|
const replySpy = replyModule.__replySpy as unknown as ReturnType<typeof vi.fn>;
|
|
|
|
|
replySpy.mockReset();
|
|
|
|
|
loadConfig.mockReturnValue({
|
|
|
|
|
channels: {
|
|
|
|
|
telegram: {
|
|
|
|
|
groupPolicy: "allowlist",
|
|
|
|
|
allowFrom: ["*"], // Wildcard allows everyone
|
|
|
|
|
groups: { "*": { requireMention: false } },
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
createTelegramBot({ token: "tok" });
|
|
|
|
|
const handler = getOnHandler("message") as (ctx: Record<string, unknown>) => Promise<void>;
|
|
|
|
|
|
|
|
|
|
await handler({
|
|
|
|
|
message: {
|
|
|
|
|
chat: { id: -100123456789, type: "group", title: "Test Group" },
|
|
|
|
|
from: { id: 999999, username: "random" }, // Random sender, but wildcard allows
|
|
|
|
|
text: "hello",
|
|
|
|
|
date: 1736380800,
|
|
|
|
|
},
|
|
|
|
|
me: { username: "openclaw_bot" },
|
|
|
|
|
getFile: async () => ({ download: async () => new Uint8Array() }),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
expect(replySpy).toHaveBeenCalledTimes(1);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("blocks group messages with no sender ID when groupPolicy is 'allowlist'", async () => {
|
|
|
|
|
onSpy.mockReset();
|
|
|
|
|
const replySpy = replyModule.__replySpy as unknown as ReturnType<typeof vi.fn>;
|
|
|
|
|
replySpy.mockReset();
|
|
|
|
|
loadConfig.mockReturnValue({
|
|
|
|
|
channels: {
|
|
|
|
|
telegram: {
|
|
|
|
|
groupPolicy: "allowlist",
|
|
|
|
|
allowFrom: ["123456789"],
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
createTelegramBot({ token: "tok" });
|
|
|
|
|
const handler = getOnHandler("message") as (ctx: Record<string, unknown>) => Promise<void>;
|
|
|
|
|
|
|
|
|
|
await handler({
|
|
|
|
|
message: {
|
|
|
|
|
chat: { id: -100123456789, type: "group", title: "Test Group" },
|
|
|
|
|
// No `from` field (e.g., channel post or anonymous admin)
|
|
|
|
|
text: "hello",
|
|
|
|
|
date: 1736380800,
|
|
|
|
|
},
|
|
|
|
|
me: { username: "openclaw_bot" },
|
|
|
|
|
getFile: async () => ({ download: async () => new Uint8Array() }),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
expect(replySpy).not.toHaveBeenCalled();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("matches telegram:-prefixed allowFrom entries in group allowlist", async () => {
|
|
|
|
|
onSpy.mockReset();
|
|
|
|
|
const replySpy = replyModule.__replySpy as unknown as ReturnType<typeof vi.fn>;
|
|
|
|
|
replySpy.mockReset();
|
|
|
|
|
loadConfig.mockReturnValue({
|
|
|
|
|
channels: {
|
|
|
|
|
telegram: {
|
|
|
|
|
groupPolicy: "allowlist",
|
|
|
|
|
allowFrom: ["telegram:123456789"], // Prefixed format
|
|
|
|
|
groups: { "*": { requireMention: false } },
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
createTelegramBot({ token: "tok" });
|
|
|
|
|
const handler = getOnHandler("message") as (ctx: Record<string, unknown>) => Promise<void>;
|
|
|
|
|
|
|
|
|
|
await handler({
|
|
|
|
|
message: {
|
|
|
|
|
chat: { id: -100123456789, type: "group", title: "Test Group" },
|
|
|
|
|
from: { id: 123456789, username: "testuser" }, // Matches after stripping prefix
|
|
|
|
|
text: "hello from prefixed user",
|
|
|
|
|
date: 1736380800,
|
|
|
|
|
},
|
|
|
|
|
me: { username: "openclaw_bot" },
|
|
|
|
|
getFile: async () => ({ download: async () => new Uint8Array() }),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Should call reply because sender ID matches after stripping telegram: prefix
|
|
|
|
|
expect(replySpy).toHaveBeenCalled();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("matches tg:-prefixed allowFrom entries case-insensitively in group allowlist", async () => {
|
|
|
|
|
onSpy.mockReset();
|
|
|
|
|
const replySpy = replyModule.__replySpy as unknown as ReturnType<typeof vi.fn>;
|
|
|
|
|
replySpy.mockReset();
|
|
|
|
|
loadConfig.mockReturnValue({
|
|
|
|
|
channels: {
|
|
|
|
|
telegram: {
|
|
|
|
|
groupPolicy: "allowlist",
|
|
|
|
|
allowFrom: ["TG:123456789"], // Prefixed format (case-insensitive)
|
|
|
|
|
groups: { "*": { requireMention: false } },
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
createTelegramBot({ token: "tok" });
|
|
|
|
|
const handler = getOnHandler("message") as (ctx: Record<string, unknown>) => Promise<void>;
|
|
|
|
|
|
|
|
|
|
await handler({
|
|
|
|
|
message: {
|
|
|
|
|
chat: { id: -100123456789, type: "group", title: "Test Group" },
|
|
|
|
|
from: { id: 123456789, username: "testuser" }, // Matches after stripping tg: prefix
|
|
|
|
|
text: "hello from prefixed user",
|
|
|
|
|
date: 1736380800,
|
|
|
|
|
},
|
|
|
|
|
me: { username: "openclaw_bot" },
|
|
|
|
|
getFile: async () => ({ download: async () => new Uint8Array() }),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Should call reply because sender ID matches after stripping tg: prefix
|
|
|
|
|
expect(replySpy).toHaveBeenCalled();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("blocks group messages when groupPolicy allowlist has no groupAllowFrom", async () => {
|
|
|
|
|
onSpy.mockReset();
|
|
|
|
|
const replySpy = replyModule.__replySpy as unknown as ReturnType<typeof vi.fn>;
|
|
|
|
|
|