fix: honor telegram pairing allowlists for native commands
This commit is contained in:
@@ -24,6 +24,17 @@ export const normalizeAllowFrom = (list?: Array<string | number>): NormalizedAll
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const normalizeAllowFromWithStore = (params: {
|
||||||
|
allowFrom?: Array<string | number>;
|
||||||
|
storeAllowFrom?: string[];
|
||||||
|
}): NormalizedAllowFrom => {
|
||||||
|
const combined = [
|
||||||
|
...(params.allowFrom ?? []),
|
||||||
|
...(params.storeAllowFrom ?? []),
|
||||||
|
].map((value) => String(value).trim()).filter(Boolean);
|
||||||
|
return normalizeAllowFrom(combined);
|
||||||
|
};
|
||||||
|
|
||||||
export const firstDefined = <T>(...values: Array<T | undefined>) => {
|
export const firstDefined = <T>(...values: Array<T | undefined>) => {
|
||||||
for (const value of values) {
|
for (const value of values) {
|
||||||
if (typeof value !== "undefined") return value;
|
if (typeof value !== "undefined") return value;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { danger, logVerbose, warn } from "../globals.js";
|
|||||||
import { resolveMedia } from "./bot/delivery.js";
|
import { resolveMedia } from "./bot/delivery.js";
|
||||||
import { resolveTelegramForumThreadId } from "./bot/helpers.js";
|
import { resolveTelegramForumThreadId } from "./bot/helpers.js";
|
||||||
import type { TelegramMessage } from "./bot/types.js";
|
import type { TelegramMessage } from "./bot/types.js";
|
||||||
import { firstDefined, isSenderAllowed, normalizeAllowFrom } from "./bot-access.js";
|
import { firstDefined, isSenderAllowed, normalizeAllowFromWithStore } from "./bot-access.js";
|
||||||
import { MEDIA_GROUP_TIMEOUT_MS, type MediaGroupEntry } from "./bot-updates.js";
|
import { MEDIA_GROUP_TIMEOUT_MS, type MediaGroupEntry } from "./bot-updates.js";
|
||||||
import { migrateTelegramGroupConfig } from "./group-migration.js";
|
import { migrateTelegramGroupConfig } from "./group-migration.js";
|
||||||
import { resolveTelegramInlineButtonsScope } from "./inline-buttons.js";
|
import { resolveTelegramInlineButtonsScope } from "./inline-buttons.js";
|
||||||
@@ -205,14 +205,14 @@ export const registerTelegramHandlers = ({
|
|||||||
const { groupConfig, topicConfig } = resolveTelegramGroupConfig(chatId, resolvedThreadId);
|
const { groupConfig, topicConfig } = resolveTelegramGroupConfig(chatId, resolvedThreadId);
|
||||||
const storeAllowFrom = await readTelegramAllowFromStore().catch(() => []);
|
const storeAllowFrom = await readTelegramAllowFromStore().catch(() => []);
|
||||||
const groupAllowOverride = firstDefined(topicConfig?.allowFrom, groupConfig?.allowFrom);
|
const groupAllowOverride = firstDefined(topicConfig?.allowFrom, groupConfig?.allowFrom);
|
||||||
const effectiveGroupAllow = normalizeAllowFrom([
|
const effectiveGroupAllow = normalizeAllowFromWithStore({
|
||||||
...(groupAllowOverride ?? groupAllowFrom ?? []),
|
allowFrom: groupAllowOverride ?? groupAllowFrom,
|
||||||
...storeAllowFrom,
|
storeAllowFrom,
|
||||||
]);
|
});
|
||||||
const effectiveDmAllow = normalizeAllowFrom([
|
const effectiveDmAllow = normalizeAllowFromWithStore({
|
||||||
...(telegramCfg.allowFrom ?? []),
|
allowFrom: telegramCfg.allowFrom,
|
||||||
...storeAllowFrom,
|
storeAllowFrom,
|
||||||
]);
|
});
|
||||||
const dmPolicy = telegramCfg.dmPolicy ?? "pairing";
|
const dmPolicy = telegramCfg.dmPolicy ?? "pairing";
|
||||||
const senderId = callback.from?.id ? String(callback.from.id) : "";
|
const senderId = callback.from?.id ? String(callback.from.id) : "";
|
||||||
const senderUsername = callback.from?.username ?? "";
|
const senderUsername = callback.from?.username ?? "";
|
||||||
@@ -393,10 +393,10 @@ export const registerTelegramHandlers = ({
|
|||||||
const storeAllowFrom = await readTelegramAllowFromStore().catch(() => []);
|
const storeAllowFrom = await readTelegramAllowFromStore().catch(() => []);
|
||||||
const { groupConfig, topicConfig } = resolveTelegramGroupConfig(chatId, resolvedThreadId);
|
const { groupConfig, topicConfig } = resolveTelegramGroupConfig(chatId, resolvedThreadId);
|
||||||
const groupAllowOverride = firstDefined(topicConfig?.allowFrom, groupConfig?.allowFrom);
|
const groupAllowOverride = firstDefined(topicConfig?.allowFrom, groupConfig?.allowFrom);
|
||||||
const effectiveGroupAllow = normalizeAllowFrom([
|
const effectiveGroupAllow = normalizeAllowFromWithStore({
|
||||||
...(groupAllowOverride ?? groupAllowFrom ?? []),
|
allowFrom: groupAllowOverride ?? groupAllowFrom,
|
||||||
...storeAllowFrom,
|
storeAllowFrom,
|
||||||
]);
|
});
|
||||||
const hasGroupAllowOverride = typeof groupAllowOverride !== "undefined";
|
const hasGroupAllowOverride = typeof groupAllowOverride !== "undefined";
|
||||||
|
|
||||||
if (isGroup) {
|
if (isGroup) {
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
firstDefined,
|
firstDefined,
|
||||||
isSenderAllowed,
|
isSenderAllowed,
|
||||||
normalizeAllowFrom,
|
normalizeAllowFromWithStore,
|
||||||
resolveSenderAllowMatch,
|
resolveSenderAllowMatch,
|
||||||
} from "./bot-access.js";
|
} from "./bot-access.js";
|
||||||
import { upsertTelegramPairingRequest } from "./pairing-store.js";
|
import { upsertTelegramPairingRequest } from "./pairing-store.js";
|
||||||
@@ -138,12 +138,12 @@ export const buildTelegramMessageContext = async ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
const mentionRegexes = buildMentionRegexes(cfg, route.agentId);
|
const mentionRegexes = buildMentionRegexes(cfg, route.agentId);
|
||||||
const effectiveDmAllow = normalizeAllowFrom([...(allowFrom ?? []), ...storeAllowFrom]);
|
const effectiveDmAllow = normalizeAllowFromWithStore({ allowFrom, storeAllowFrom });
|
||||||
const groupAllowOverride = firstDefined(topicConfig?.allowFrom, groupConfig?.allowFrom);
|
const groupAllowOverride = firstDefined(topicConfig?.allowFrom, groupConfig?.allowFrom);
|
||||||
const effectiveGroupAllow = normalizeAllowFrom([
|
const effectiveGroupAllow = normalizeAllowFromWithStore({
|
||||||
...(groupAllowOverride ?? groupAllowFrom ?? []),
|
allowFrom: groupAllowOverride ?? groupAllowFrom,
|
||||||
...storeAllowFrom,
|
storeAllowFrom,
|
||||||
]);
|
});
|
||||||
const hasGroupAllowOverride = typeof groupAllowOverride !== "undefined";
|
const hasGroupAllowOverride = typeof groupAllowOverride !== "undefined";
|
||||||
|
|
||||||
if (isGroup && groupConfig?.enabled === false) {
|
if (isGroup && groupConfig?.enabled === false) {
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ import {
|
|||||||
buildTelegramGroupPeerId,
|
buildTelegramGroupPeerId,
|
||||||
resolveTelegramForumThreadId,
|
resolveTelegramForumThreadId,
|
||||||
} from "./bot/helpers.js";
|
} from "./bot/helpers.js";
|
||||||
import { firstDefined, isSenderAllowed, normalizeAllowFrom } from "./bot-access.js";
|
import { firstDefined, isSenderAllowed, normalizeAllowFromWithStore } from "./bot-access.js";
|
||||||
import { readTelegramAllowFromStore } from "./pairing-store.js";
|
import { readTelegramAllowFromStore } from "./pairing-store.js";
|
||||||
|
|
||||||
type TelegramNativeCommandContext = Context & { match?: string };
|
type TelegramNativeCommandContext = Context & { match?: string };
|
||||||
@@ -132,10 +132,10 @@ export const registerTelegramNativeCommands = ({
|
|||||||
const storeAllowFrom = await readTelegramAllowFromStore().catch(() => []);
|
const storeAllowFrom = await readTelegramAllowFromStore().catch(() => []);
|
||||||
const { groupConfig, topicConfig } = resolveTelegramGroupConfig(chatId, resolvedThreadId);
|
const { groupConfig, topicConfig } = resolveTelegramGroupConfig(chatId, resolvedThreadId);
|
||||||
const groupAllowOverride = firstDefined(topicConfig?.allowFrom, groupConfig?.allowFrom);
|
const groupAllowOverride = firstDefined(topicConfig?.allowFrom, groupConfig?.allowFrom);
|
||||||
const effectiveGroupAllow = normalizeAllowFrom([
|
const effectiveGroupAllow = normalizeAllowFromWithStore({
|
||||||
...(groupAllowOverride ?? groupAllowFrom ?? []),
|
allowFrom: groupAllowOverride ?? groupAllowFrom,
|
||||||
...storeAllowFrom,
|
storeAllowFrom,
|
||||||
]);
|
});
|
||||||
const hasGroupAllowOverride = typeof groupAllowOverride !== "undefined";
|
const hasGroupAllowOverride = typeof groupAllowOverride !== "undefined";
|
||||||
|
|
||||||
if (isGroup && groupConfig?.enabled === false) {
|
if (isGroup && groupConfig?.enabled === false) {
|
||||||
@@ -194,12 +194,12 @@ export const registerTelegramNativeCommands = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const allowFromList = Array.isArray(allowFrom)
|
|
||||||
? allowFrom.map((entry) => String(entry).trim()).filter(Boolean)
|
|
||||||
: [];
|
|
||||||
const senderId = msg.from?.id ? String(msg.from.id) : "";
|
const senderId = msg.from?.id ? String(msg.from.id) : "";
|
||||||
const senderUsername = msg.from?.username ?? "";
|
const senderUsername = msg.from?.username ?? "";
|
||||||
const dmAllow = normalizeAllowFrom(allowFromList);
|
const dmAllow = normalizeAllowFromWithStore({
|
||||||
|
allowFrom: allowFrom,
|
||||||
|
storeAllowFrom,
|
||||||
|
});
|
||||||
const senderAllowed = isSenderAllowed({
|
const senderAllowed = isSenderAllowed({
|
||||||
allow: dmAllow,
|
allow: dmAllow,
|
||||||
senderId,
|
senderId,
|
||||||
|
|||||||
@@ -2138,6 +2138,49 @@ describe("createTelegramBot", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("allows native DM commands for paired users", async () => {
|
||||||
|
onSpy.mockReset();
|
||||||
|
sendMessageSpy.mockReset();
|
||||||
|
commandSpy.mockReset();
|
||||||
|
const replySpy = replyModule.__replySpy as unknown as ReturnType<typeof vi.fn>;
|
||||||
|
replySpy.mockReset();
|
||||||
|
replySpy.mockResolvedValue({ text: "response" });
|
||||||
|
|
||||||
|
loadConfig.mockReturnValue({
|
||||||
|
commands: { native: true },
|
||||||
|
channels: {
|
||||||
|
telegram: {
|
||||||
|
dmPolicy: "pairing",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
readTelegramAllowFromStore.mockResolvedValueOnce(["12345"]);
|
||||||
|
|
||||||
|
createTelegramBot({ token: "tok" });
|
||||||
|
const handler = commandSpy.mock.calls.find((call) => call[0] === "status")?.[1] as
|
||||||
|
| ((ctx: Record<string, unknown>) => Promise<void>)
|
||||||
|
| undefined;
|
||||||
|
if (!handler) throw new Error("status command handler missing");
|
||||||
|
|
||||||
|
await handler({
|
||||||
|
message: {
|
||||||
|
chat: { id: 12345, type: "private" },
|
||||||
|
from: { id: 12345, username: "testuser" },
|
||||||
|
text: "/status",
|
||||||
|
date: 1736380800,
|
||||||
|
message_id: 42,
|
||||||
|
},
|
||||||
|
match: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(replySpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(
|
||||||
|
sendMessageSpy.mock.calls.some(
|
||||||
|
(call) => call[1] === "You are not authorized to use this command.",
|
||||||
|
),
|
||||||
|
).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
it("streams tool summaries for native slash commands", async () => {
|
it("streams tool summaries for native slash commands", async () => {
|
||||||
onSpy.mockReset();
|
onSpy.mockReset();
|
||||||
sendMessageSpy.mockReset();
|
sendMessageSpy.mockReset();
|
||||||
|
|||||||
Reference in New Issue
Block a user