// Telegram tests cover bot native commands.group auth plugin behavior. import type { ChannelGroupPolicy, OpenClawConfig, TelegramAccountConfig, TelegramGroupConfig, TelegramTopicConfig, } from "openclaw/plugin-sdk/config-contracts"; import { describe, expect, it, vi } from "vitest"; import { createNativeCommandsHarness, createTelegramDmCommandContext, createTelegramGroupCommandContext, findNotAuthorizedCalls, } from "./bot-native-commands.test-helpers.js"; describe("native command auth in groups", () => { function setup(params: { cfg?: OpenClawConfig; telegramCfg?: TelegramAccountConfig; allowFrom?: string[]; groupAllowFrom?: string[]; storeAllowFrom?: string[]; useAccessGroups?: boolean; groupConfig?: TelegramGroupConfig; topicConfig?: TelegramTopicConfig; resolveGroupPolicy?: () => ChannelGroupPolicy; }) { return createNativeCommandsHarness({ cfg: params.cfg ?? ({} as OpenClawConfig), telegramCfg: params.telegramCfg ?? ({} as TelegramAccountConfig), allowFrom: params.allowFrom ?? [], groupAllowFrom: params.groupAllowFrom ?? [], storeAllowFrom: params.storeAllowFrom, useAccessGroups: params.useAccessGroups ?? false, resolveGroupPolicy: params.resolveGroupPolicy ?? (() => ({ allowlistEnabled: false, allowed: true, }) as ChannelGroupPolicy), groupConfig: params.groupConfig, topicConfig: params.topicConfig, }); } it("authorizes native commands in groups when sender is in groupAllowFrom", async () => { const { handlers, sendMessage } = setup({ groupAllowFrom: ["12345"], useAccessGroups: true, // no allowFrom — sender is NOT in DM allowlist }); const ctx = createTelegramGroupCommandContext(); await handlers.status?.(ctx); const notAuthCalls = findNotAuthorizedCalls(sendMessage); expect(notAuthCalls).toHaveLength(0); }); it("does not authorize group native commands from the DM allowlist store", async () => { const { handlers, sendMessage } = setup({ storeAllowFrom: ["12345"], useAccessGroups: true, }); const ctx = createTelegramGroupCommandContext(); await handlers.status?.(ctx); const notAuthCalls = findNotAuthorizedCalls(sendMessage); expect(notAuthCalls.length).toBeGreaterThan(0); }); it("authorizes native commands in groups from commands.allowFrom.telegram", async () => { const { handlers, sendMessage } = setup({ cfg: { commands: { allowFrom: { telegram: ["12345"], }, }, } as OpenClawConfig, allowFrom: ["99999"], groupAllowFrom: ["99999"], useAccessGroups: true, }); const ctx = createTelegramGroupCommandContext(); await handlers.status?.(ctx); const notAuthCalls = findNotAuthorizedCalls(sendMessage); expect(notAuthCalls).toHaveLength(0); }); it("uses commands.allowFrom.telegram as the sole auth source when configured", async () => { const { handlers, sendMessage } = setup({ cfg: { commands: { allowFrom: { telegram: ["99999"], }, }, } as OpenClawConfig, groupAllowFrom: ["12345"], useAccessGroups: true, }); const ctx = createTelegramGroupCommandContext(); await handlers.status?.(ctx); expect(sendMessage).toHaveBeenCalledWith( -100999, "You are not authorized to use this command.", { message_thread_id: 42 }, ); }); it("silently drops account-disabled native commands", async () => { const { handlers, sendMessage } = setup({ cfg: { channels: { telegram: { groupPolicy: "disabled", }, }, commands: { allowFrom: { telegram: ["12345"], }, }, } as OpenClawConfig, useAccessGroups: true, resolveGroupPolicy: () => ({ allowlistEnabled: false, allowed: false, }) as ChannelGroupPolicy, }); const ctx = createTelegramGroupCommandContext(); await handlers.status?.(ctx); expect(sendMessage).not.toHaveBeenCalled(); }); it("silently drops topic-disabled native commands before dispatch", async () => { const cfg = { commands: { allowFrom: { telegram: ["12345"], }, }, } as OpenClawConfig; const disabled = setup({ cfg, telegramCfg: { groupPolicy: "open" } as TelegramAccountConfig, groupConfig: { groupPolicy: "open" }, topicConfig: { groupPolicy: "disabled" }, useAccessGroups: true, }); await disabled.handlers.status?.(createTelegramGroupCommandContext({ threadId: 42 })); expect(disabled.sendMessage).not.toHaveBeenCalled(); expect(disabled.dispatchReplyWithBufferedBlockDispatcher).not.toHaveBeenCalled(); }); it("silently drops native commands that inherit disabled group policy", async () => { const { handlers, sendMessage, dispatchReplyWithBufferedBlockDispatcher } = setup({ cfg: { commands: { allowFrom: { telegram: ["12345"], }, }, } as OpenClawConfig, telegramCfg: { groupPolicy: "open" } as TelegramAccountConfig, groupConfig: { groupPolicy: "disabled" }, useAccessGroups: true, }); await handlers.status?.(createTelegramGroupCommandContext()); expect(sendMessage).not.toHaveBeenCalled(); expect(dispatchReplyWithBufferedBlockDispatcher).not.toHaveBeenCalled(); }); it("silently drops native commands from groups outside the chat allowlist", async () => { const { handlers, sendMessage } = setup({ cfg: { commands: { allowFrom: { telegram: ["12345"], }, }, } as OpenClawConfig, useAccessGroups: true, resolveGroupPolicy: () => ({ allowlistEnabled: true, allowed: false, }) as ChannelGroupPolicy, }); const ctx = createTelegramGroupCommandContext(); await handlers.status?.(ctx); expect(sendMessage).not.toHaveBeenCalled(); }); it("rejects native commands in groups when sender is in neither allowlist", async () => { const { handlers, sendMessage } = setup({ allowFrom: ["99999"], groupAllowFrom: ["99999"], useAccessGroups: true, }); const ctx = createTelegramGroupCommandContext({ username: "intruder", }); await handlers.status?.(ctx); const notAuthCalls = findNotAuthorizedCalls(sendMessage); expect(notAuthCalls.length).toBeGreaterThan(0); }); it("authorizes a DM native command from commands.allowFrom.telegram when pairing-store read fails transiently", async () => { const readChannelAllowFromStore = vi.fn(async () => { throw new Error("store temporarily unavailable"); }); const { handlers, sendMessage } = createNativeCommandsHarness({ cfg: { commands: { native: true, allowFrom: { telegram: ["12345"] } }, channels: { telegram: { dmPolicy: "pairing" } }, } as OpenClawConfig, telegramCfg: { dmPolicy: "pairing" } as TelegramAccountConfig, readChannelAllowFromStore, }); const ctx = createTelegramDmCommandContext({ senderId: 12345 }); await handlers.status?.(ctx); expect(readChannelAllowFromStore).not.toHaveBeenCalled(); expect(findNotAuthorizedCalls(sendMessage)).toHaveLength(0); }); it("replies in the originating forum topic when auth is rejected", async () => { const { handlers, sendMessage } = setup({ allowFrom: ["99999"], groupAllowFrom: ["99999"], useAccessGroups: true, }); const ctx = createTelegramGroupCommandContext({ username: "intruder", }); await handlers.status?.(ctx); expect(sendMessage).toHaveBeenCalledWith( -100999, "You are not authorized to use this command.", { message_thread_id: 42 }, ); }); });