From 24fbafa9a7f7d61322d782705851623fbbf76bd9 Mon Sep 17 00:00:00 2001 From: Ayaan Zaidi Date: Sun, 1 Feb 2026 14:55:41 +0530 Subject: [PATCH] refactor: use shared pairing store for telegram --- ...-back-legacy-sandbox-image-missing.test.ts | 4 - ...owfrom-channels-whatsapp-allowfrom.test.ts | 4 - ...-state-migrations-yes-mode-without.test.ts | 4 - ...agent-sandbox-docker-browser-prune.test.ts | 4 - ...r.warns-state-directory-is-missing.test.ts | 4 - src/telegram/bot-handlers.ts | 10 +- src/telegram/bot-message-context.ts | 15 ++- .../bot-native-commands.plugin-auth.test.ts | 4 +- src/telegram/bot-native-commands.ts | 4 +- ...patterns-match-without-botusername.test.ts | 12 +- ...topic-skill-filters-system-prompts.test.ts | 12 +- ...-all-group-messages-grouppolicy-is.test.ts | 12 +- ...e-callback-query-updates-by-update.test.ts | 12 +- ...gram-bot.installs-grammy-throttler.test.ts | 20 +-- ...lowfrom-entries-case-insensitively.test.ts | 12 +- ...-case-insensitively-grouppolicy-is.test.ts | 12 +- ...-dms-by-telegram-accountid-binding.test.ts | 12 +- ...ies-without-native-reply-threading.test.ts | 12 +- ...s-media-file-path-no-file-download.test.ts | 6 +- ...udes-location-text-ctx-fields-pins.test.ts | 6 +- src/telegram/bot.test.ts | 26 ++-- src/telegram/pairing-store.test.ts | 52 -------- src/telegram/pairing-store.ts | 124 ------------------ 23 files changed, 95 insertions(+), 288 deletions(-) delete mode 100644 src/telegram/pairing-store.test.ts delete mode 100644 src/telegram/pairing-store.ts diff --git a/src/commands/doctor.falls-back-legacy-sandbox-image-missing.test.ts b/src/commands/doctor.falls-back-legacy-sandbox-image-missing.test.ts index 0a4f02c6d0..9abac46364 100644 --- a/src/commands/doctor.falls-back-legacy-sandbox-image-missing.test.ts +++ b/src/commands/doctor.falls-back-legacy-sandbox-image-missing.test.ts @@ -247,10 +247,6 @@ vi.mock("../daemon/service.js", () => ({ }), })); -vi.mock("../telegram/pairing-store.js", () => ({ - readTelegramAllowFromStore: vi.fn().mockResolvedValue([]), -})); - vi.mock("../pairing/pairing-store.js", () => ({ readChannelAllowFromStore: vi.fn().mockResolvedValue([]), upsertChannelPairingRequest: vi.fn().mockResolvedValue({ code: "000000", created: false }), diff --git a/src/commands/doctor.migrates-routing-allowfrom-channels-whatsapp-allowfrom.test.ts b/src/commands/doctor.migrates-routing-allowfrom-channels-whatsapp-allowfrom.test.ts index 27690ad4b1..493bdd9726 100644 --- a/src/commands/doctor.migrates-routing-allowfrom-channels-whatsapp-allowfrom.test.ts +++ b/src/commands/doctor.migrates-routing-allowfrom-channels-whatsapp-allowfrom.test.ts @@ -246,10 +246,6 @@ vi.mock("../daemon/service.js", () => ({ }), })); -vi.mock("../telegram/pairing-store.js", () => ({ - readTelegramAllowFromStore: vi.fn().mockResolvedValue([]), -})); - vi.mock("../pairing/pairing-store.js", () => ({ readChannelAllowFromStore: vi.fn().mockResolvedValue([]), upsertChannelPairingRequest: vi.fn().mockResolvedValue({ code: "000000", created: false }), diff --git a/src/commands/doctor.runs-legacy-state-migrations-yes-mode-without.test.ts b/src/commands/doctor.runs-legacy-state-migrations-yes-mode-without.test.ts index 84422ee494..5ecaaf2cd1 100644 --- a/src/commands/doctor.runs-legacy-state-migrations-yes-mode-without.test.ts +++ b/src/commands/doctor.runs-legacy-state-migrations-yes-mode-without.test.ts @@ -246,10 +246,6 @@ vi.mock("../daemon/service.js", () => ({ }), })); -vi.mock("../telegram/pairing-store.js", () => ({ - readTelegramAllowFromStore: vi.fn().mockResolvedValue([]), -})); - vi.mock("../pairing/pairing-store.js", () => ({ readChannelAllowFromStore: vi.fn().mockResolvedValue([]), upsertChannelPairingRequest: vi.fn().mockResolvedValue({ code: "000000", created: false }), diff --git a/src/commands/doctor.warns-per-agent-sandbox-docker-browser-prune.test.ts b/src/commands/doctor.warns-per-agent-sandbox-docker-browser-prune.test.ts index d39a548851..3846b71d9b 100644 --- a/src/commands/doctor.warns-per-agent-sandbox-docker-browser-prune.test.ts +++ b/src/commands/doctor.warns-per-agent-sandbox-docker-browser-prune.test.ts @@ -246,10 +246,6 @@ vi.mock("../daemon/service.js", () => ({ }), })); -vi.mock("../telegram/pairing-store.js", () => ({ - readTelegramAllowFromStore: vi.fn().mockResolvedValue([]), -})); - vi.mock("../pairing/pairing-store.js", () => ({ readChannelAllowFromStore: vi.fn().mockResolvedValue([]), upsertChannelPairingRequest: vi.fn().mockResolvedValue({ code: "000000", created: false }), diff --git a/src/commands/doctor.warns-state-directory-is-missing.test.ts b/src/commands/doctor.warns-state-directory-is-missing.test.ts index 6f90d67aa5..fa3577ba18 100644 --- a/src/commands/doctor.warns-state-directory-is-missing.test.ts +++ b/src/commands/doctor.warns-state-directory-is-missing.test.ts @@ -246,10 +246,6 @@ vi.mock("../daemon/service.js", () => ({ }), })); -vi.mock("../telegram/pairing-store.js", () => ({ - readTelegramAllowFromStore: vi.fn().mockResolvedValue([]), -})); - vi.mock("../pairing/pairing-store.js", () => ({ readChannelAllowFromStore: vi.fn().mockResolvedValue([]), upsertChannelPairingRequest: vi.fn().mockResolvedValue({ code: "000000", created: false }), diff --git a/src/telegram/bot-handlers.ts b/src/telegram/bot-handlers.ts index a5fcd2d212..1daac5690b 100644 --- a/src/telegram/bot-handlers.ts +++ b/src/telegram/bot-handlers.ts @@ -13,6 +13,7 @@ import { resolveChannelConfigWrites } from "../channels/plugins/config-writes.js import { loadConfig } from "../config/config.js"; import { writeConfigFile } from "../config/io.js"; import { danger, logVerbose, warn } from "../globals.js"; +import { readChannelAllowFromStore } from "../pairing/pairing-store.js"; import { withTelegramApiErrorLogging } from "./api-logging.js"; import { firstDefined, isSenderAllowed, normalizeAllowFromWithStore } from "./bot-access.js"; import { RegisterTelegramHandlerParams } from "./bot-native-commands.js"; @@ -21,7 +22,6 @@ import { resolveMedia } from "./bot/delivery.js"; import { resolveTelegramForumThreadId } from "./bot/helpers.js"; import { migrateTelegramGroupConfig } from "./group-migration.js"; import { resolveTelegramInlineButtonsScope } from "./inline-buttons.js"; -import { readTelegramAllowFromStore } from "./pairing-store.js"; import { buildInlineKeyboard } from "./send.js"; export const registerTelegramHandlers = ({ @@ -142,7 +142,7 @@ export const registerTelegramHandlers = ({ } } - const storeAllowFrom = await readTelegramAllowFromStore().catch(() => []); + const storeAllowFrom = await readChannelAllowFromStore("telegram").catch(() => []); await processMessage(primaryEntry.ctx, allMedia, storeAllowFrom); } catch (err) { runtime.error?.(danger(`media group handler failed: ${String(err)}`)); @@ -173,7 +173,7 @@ export const registerTelegramHandlers = ({ date: last.msg.date ?? first.msg.date, }; - const storeAllowFrom = await readTelegramAllowFromStore().catch(() => []); + const storeAllowFrom = await readChannelAllowFromStore("telegram").catch(() => []); const baseCtx = first.ctx as { me?: unknown; getFile?: unknown } & Record; const getFile = typeof baseCtx.getFile === "function" ? baseCtx.getFile.bind(baseCtx) : async () => ({}); @@ -248,7 +248,7 @@ export const registerTelegramHandlers = ({ messageThreadId, }); const { groupConfig, topicConfig } = resolveTelegramGroupConfig(chatId, resolvedThreadId); - const storeAllowFrom = await readTelegramAllowFromStore().catch(() => []); + const storeAllowFrom = await readChannelAllowFromStore("telegram").catch(() => []); const groupAllowOverride = firstDefined(topicConfig?.allowFrom, groupConfig?.allowFrom); const effectiveGroupAllow = normalizeAllowFromWithStore({ allowFrom: groupAllowOverride ?? groupAllowFrom, @@ -492,7 +492,7 @@ export const registerTelegramHandlers = ({ isForum, messageThreadId, }); - const storeAllowFrom = await readTelegramAllowFromStore().catch(() => []); + const storeAllowFrom = await readChannelAllowFromStore("telegram").catch(() => []); const { groupConfig, topicConfig } = resolveTelegramGroupConfig(chatId, resolvedThreadId); const groupAllowOverride = firstDefined(topicConfig?.allowFrom, groupConfig?.allowFrom); const effectiveGroupAllow = normalizeAllowFromWithStore({ diff --git a/src/telegram/bot-message-context.ts b/src/telegram/bot-message-context.ts index 1427e6ec50..0017420708 100644 --- a/src/telegram/bot-message-context.ts +++ b/src/telegram/bot-message-context.ts @@ -29,6 +29,7 @@ import { formatCliCommand } from "../cli/command-format.js"; import { readSessionUpdatedAt, resolveStorePath } from "../config/sessions.js"; import { logVerbose, shouldLogVerbose } from "../globals.js"; import { recordChannelActivity } from "../infra/channel-activity.js"; +import { upsertChannelPairingRequest } from "../pairing/pairing-store.js"; import { resolveAgentRoute } from "../routing/resolve-route.js"; import { resolveThreadSessionKeys } from "../routing/session-key.js"; import { withTelegramApiErrorLogging } from "./api-logging.js"; @@ -52,7 +53,6 @@ import { hasBotMention, resolveTelegramForumThreadId, } from "./bot/helpers.js"; -import { upsertTelegramPairingRequest } from "./pairing-store.js"; type TelegramMediaRef = { path: string; @@ -252,11 +252,14 @@ export const buildTelegramMessageContext = async ({ } | undefined; const telegramUserId = from?.id ? String(from.id) : candidate; - const { code, created } = await upsertTelegramPairingRequest({ - chatId: candidate, - username: from?.username, - firstName: from?.first_name, - lastName: from?.last_name, + const { code, created } = await upsertChannelPairingRequest({ + channel: "telegram", + id: String(candidate), + meta: { + username: from?.username, + firstName: from?.first_name, + lastName: from?.last_name, + }, }); if (created) { logger.info( diff --git a/src/telegram/bot-native-commands.plugin-auth.test.ts b/src/telegram/bot-native-commands.plugin-auth.test.ts index 60e315e8db..7572279b5c 100644 --- a/src/telegram/bot-native-commands.plugin-auth.test.ts +++ b/src/telegram/bot-native-commands.plugin-auth.test.ts @@ -18,8 +18,8 @@ vi.mock("../plugins/commands.js", () => ({ const deliverReplies = vi.hoisted(() => vi.fn(async () => {})); vi.mock("./bot/delivery.js", () => ({ deliverReplies })); -vi.mock("./pairing-store.js", () => ({ - readTelegramAllowFromStore: vi.fn(async () => []), +vi.mock("../pairing/pairing-store.js", () => ({ + readChannelAllowFromStore: vi.fn(async () => []), })); describe("registerTelegramNativeCommands (plugin auth)", () => { diff --git a/src/telegram/bot-native-commands.ts b/src/telegram/bot-native-commands.ts index c34d594361..a8c53808fa 100644 --- a/src/telegram/bot-native-commands.ts +++ b/src/telegram/bot-native-commands.ts @@ -31,6 +31,7 @@ import { } from "../config/telegram-custom-commands.js"; import { danger, logVerbose } from "../globals.js"; import { getChildLogger } from "../logging.js"; +import { readChannelAllowFromStore } from "../pairing/pairing-store.js"; import { executePluginCommand, getPluginCommandSpecs, @@ -49,7 +50,6 @@ import { buildTelegramGroupPeerId, resolveTelegramForumThreadId, } from "./bot/helpers.js"; -import { readTelegramAllowFromStore } from "./pairing-store.js"; import { buildInlineKeyboard } from "./send.js"; const EMPTY_RESPONSE_FALLBACK = "No response generated. Please try again."; @@ -153,7 +153,7 @@ async function resolveTelegramCommandAuth(params: { isForum, messageThreadId, }); - const storeAllowFrom = await readTelegramAllowFromStore().catch(() => []); + const storeAllowFrom = await readChannelAllowFromStore("telegram").catch(() => []); const { groupConfig, topicConfig } = resolveTelegramGroupConfig(chatId, resolvedThreadId); const groupAllowOverride = firstDefined(topicConfig?.allowFrom, groupConfig?.allowFrom); const effectiveGroupAllow = normalizeAllowFromWithStore({ diff --git a/src/telegram/bot.create-telegram-bot.accepts-group-messages-mentionpatterns-match-without-botusername.test.ts b/src/telegram/bot.create-telegram-bot.accepts-group-messages-mentionpatterns-match-without-botusername.test.ts index 765d0d2f87..62fa9eeca5 100644 --- a/src/telegram/bot.create-telegram-bot.accepts-group-messages-mentionpatterns-match-without-botusername.test.ts +++ b/src/telegram/bot.create-telegram-bot.accepts-group-messages-mentionpatterns-match-without-botusername.test.ts @@ -35,17 +35,17 @@ vi.mock("../config/sessions.js", async (importOriginal) => { }; }); -const { readTelegramAllowFromStore, upsertTelegramPairingRequest } = vi.hoisted(() => ({ - readTelegramAllowFromStore: vi.fn(async () => [] as string[]), - upsertTelegramPairingRequest: vi.fn(async () => ({ +const { readChannelAllowFromStore, upsertChannelPairingRequest } = vi.hoisted(() => ({ + readChannelAllowFromStore: vi.fn(async () => [] as string[]), + upsertChannelPairingRequest: vi.fn(async () => ({ code: "PAIRCODE", created: true, })), })); -vi.mock("./pairing-store.js", () => ({ - readTelegramAllowFromStore, - upsertTelegramPairingRequest, +vi.mock("../pairing/pairing-store.js", () => ({ + readChannelAllowFromStore, + upsertChannelPairingRequest, })); const useSpy = vi.fn(); diff --git a/src/telegram/bot.create-telegram-bot.applies-topic-skill-filters-system-prompts.test.ts b/src/telegram/bot.create-telegram-bot.applies-topic-skill-filters-system-prompts.test.ts index 4b0c852d97..06a924e84e 100644 --- a/src/telegram/bot.create-telegram-bot.applies-topic-skill-filters-system-prompts.test.ts +++ b/src/telegram/bot.create-telegram-bot.applies-topic-skill-filters-system-prompts.test.ts @@ -34,17 +34,17 @@ vi.mock("../config/sessions.js", async (importOriginal) => { }; }); -const { readTelegramAllowFromStore, upsertTelegramPairingRequest } = vi.hoisted(() => ({ - readTelegramAllowFromStore: vi.fn(async () => [] as string[]), - upsertTelegramPairingRequest: vi.fn(async () => ({ +const { readChannelAllowFromStore, upsertChannelPairingRequest } = vi.hoisted(() => ({ + readChannelAllowFromStore: vi.fn(async () => [] as string[]), + upsertChannelPairingRequest: vi.fn(async () => ({ code: "PAIRCODE", created: true, })), })); -vi.mock("./pairing-store.js", () => ({ - readTelegramAllowFromStore, - upsertTelegramPairingRequest, +vi.mock("../pairing/pairing-store.js", () => ({ + readChannelAllowFromStore, + upsertChannelPairingRequest, })); const useSpy = vi.fn(); diff --git a/src/telegram/bot.create-telegram-bot.blocks-all-group-messages-grouppolicy-is.test.ts b/src/telegram/bot.create-telegram-bot.blocks-all-group-messages-grouppolicy-is.test.ts index 7cc27f7f78..b52e934060 100644 --- a/src/telegram/bot.create-telegram-bot.blocks-all-group-messages-grouppolicy-is.test.ts +++ b/src/telegram/bot.create-telegram-bot.blocks-all-group-messages-grouppolicy-is.test.ts @@ -34,17 +34,17 @@ vi.mock("../config/sessions.js", async (importOriginal) => { }; }); -const { readTelegramAllowFromStore, upsertTelegramPairingRequest } = vi.hoisted(() => ({ - readTelegramAllowFromStore: vi.fn(async () => [] as string[]), - upsertTelegramPairingRequest: vi.fn(async () => ({ +const { readChannelAllowFromStore, upsertChannelPairingRequest } = vi.hoisted(() => ({ + readChannelAllowFromStore: vi.fn(async () => [] as string[]), + upsertChannelPairingRequest: vi.fn(async () => ({ code: "PAIRCODE", created: true, })), })); -vi.mock("./pairing-store.js", () => ({ - readTelegramAllowFromStore, - upsertTelegramPairingRequest, +vi.mock("../pairing/pairing-store.js", () => ({ + readChannelAllowFromStore, + upsertChannelPairingRequest, })); const useSpy = vi.fn(); diff --git a/src/telegram/bot.create-telegram-bot.dedupes-duplicate-callback-query-updates-by-update.test.ts b/src/telegram/bot.create-telegram-bot.dedupes-duplicate-callback-query-updates-by-update.test.ts index 7feecf57d3..4c0828c446 100644 --- a/src/telegram/bot.create-telegram-bot.dedupes-duplicate-callback-query-updates-by-update.test.ts +++ b/src/telegram/bot.create-telegram-bot.dedupes-duplicate-callback-query-updates-by-update.test.ts @@ -34,17 +34,17 @@ vi.mock("../config/sessions.js", async (importOriginal) => { }; }); -const { readTelegramAllowFromStore, upsertTelegramPairingRequest } = vi.hoisted(() => ({ - readTelegramAllowFromStore: vi.fn(async () => [] as string[]), - upsertTelegramPairingRequest: vi.fn(async () => ({ +const { readChannelAllowFromStore, upsertChannelPairingRequest } = vi.hoisted(() => ({ + readChannelAllowFromStore: vi.fn(async () => [] as string[]), + upsertChannelPairingRequest: vi.fn(async () => ({ code: "PAIRCODE", created: true, })), })); -vi.mock("./pairing-store.js", () => ({ - readTelegramAllowFromStore, - upsertTelegramPairingRequest, +vi.mock("../pairing/pairing-store.js", () => ({ + readChannelAllowFromStore, + upsertChannelPairingRequest, })); const useSpy = vi.fn(); diff --git a/src/telegram/bot.create-telegram-bot.installs-grammy-throttler.test.ts b/src/telegram/bot.create-telegram-bot.installs-grammy-throttler.test.ts index c1b78ea243..3f08a45f60 100644 --- a/src/telegram/bot.create-telegram-bot.installs-grammy-throttler.test.ts +++ b/src/telegram/bot.create-telegram-bot.installs-grammy-throttler.test.ts @@ -36,17 +36,17 @@ vi.mock("../config/sessions.js", async (importOriginal) => { }; }); -const { readTelegramAllowFromStore, upsertTelegramPairingRequest } = vi.hoisted(() => ({ - readTelegramAllowFromStore: vi.fn(async () => [] as string[]), - upsertTelegramPairingRequest: vi.fn(async () => ({ +const { readChannelAllowFromStore, upsertChannelPairingRequest } = vi.hoisted(() => ({ + readChannelAllowFromStore: vi.fn(async () => [] as string[]), + upsertChannelPairingRequest: vi.fn(async () => ({ code: "PAIRCODE", created: true, })), })); -vi.mock("./pairing-store.js", () => ({ - readTelegramAllowFromStore, - upsertTelegramPairingRequest, +vi.mock("../pairing/pairing-store.js", () => ({ + readChannelAllowFromStore, + upsertChannelPairingRequest, })); const useSpy = vi.fn(); @@ -357,8 +357,8 @@ describe("createTelegramBot", () => { loadConfig.mockReturnValue({ channels: { telegram: { dmPolicy: "pairing" } }, }); - readTelegramAllowFromStore.mockResolvedValue([]); - upsertTelegramPairingRequest.mockResolvedValue({ + readChannelAllowFromStore.mockResolvedValue([]); + upsertChannelPairingRequest.mockResolvedValue({ code: "PAIRME12", created: true, }); @@ -393,8 +393,8 @@ describe("createTelegramBot", () => { loadConfig.mockReturnValue({ channels: { telegram: { dmPolicy: "pairing" } }, }); - readTelegramAllowFromStore.mockResolvedValue([]); - upsertTelegramPairingRequest + readChannelAllowFromStore.mockResolvedValue([]); + upsertChannelPairingRequest .mockResolvedValueOnce({ code: "PAIRME12", created: true }) .mockResolvedValueOnce({ code: "PAIRME12", created: false }); diff --git a/src/telegram/bot.create-telegram-bot.matches-tg-prefixed-allowfrom-entries-case-insensitively.test.ts b/src/telegram/bot.create-telegram-bot.matches-tg-prefixed-allowfrom-entries-case-insensitively.test.ts index 365e9e1a2c..dea2babb47 100644 --- a/src/telegram/bot.create-telegram-bot.matches-tg-prefixed-allowfrom-entries-case-insensitively.test.ts +++ b/src/telegram/bot.create-telegram-bot.matches-tg-prefixed-allowfrom-entries-case-insensitively.test.ts @@ -34,17 +34,17 @@ vi.mock("../config/sessions.js", async (importOriginal) => { }; }); -const { readTelegramAllowFromStore, upsertTelegramPairingRequest } = vi.hoisted(() => ({ - readTelegramAllowFromStore: vi.fn(async () => [] as string[]), - upsertTelegramPairingRequest: vi.fn(async () => ({ +const { readChannelAllowFromStore, upsertChannelPairingRequest } = vi.hoisted(() => ({ + readChannelAllowFromStore: vi.fn(async () => [] as string[]), + upsertChannelPairingRequest: vi.fn(async () => ({ code: "PAIRCODE", created: true, })), })); -vi.mock("./pairing-store.js", () => ({ - readTelegramAllowFromStore, - upsertTelegramPairingRequest, +vi.mock("../pairing/pairing-store.js", () => ({ + readChannelAllowFromStore, + upsertChannelPairingRequest, })); const useSpy = vi.fn(); diff --git a/src/telegram/bot.create-telegram-bot.matches-usernames-case-insensitively-grouppolicy-is.test.ts b/src/telegram/bot.create-telegram-bot.matches-usernames-case-insensitively-grouppolicy-is.test.ts index d32496cdcb..d99126eedd 100644 --- a/src/telegram/bot.create-telegram-bot.matches-usernames-case-insensitively-grouppolicy-is.test.ts +++ b/src/telegram/bot.create-telegram-bot.matches-usernames-case-insensitively-grouppolicy-is.test.ts @@ -34,17 +34,17 @@ vi.mock("../config/sessions.js", async (importOriginal) => { }; }); -const { readTelegramAllowFromStore, upsertTelegramPairingRequest } = vi.hoisted(() => ({ - readTelegramAllowFromStore: vi.fn(async () => [] as string[]), - upsertTelegramPairingRequest: vi.fn(async () => ({ +const { readChannelAllowFromStore, upsertChannelPairingRequest } = vi.hoisted(() => ({ + readChannelAllowFromStore: vi.fn(async () => [] as string[]), + upsertChannelPairingRequest: vi.fn(async () => ({ code: "PAIRCODE", created: true, })), })); -vi.mock("./pairing-store.js", () => ({ - readTelegramAllowFromStore, - upsertTelegramPairingRequest, +vi.mock("../pairing/pairing-store.js", () => ({ + readChannelAllowFromStore, + upsertChannelPairingRequest, })); const useSpy = vi.fn(); diff --git a/src/telegram/bot.create-telegram-bot.routes-dms-by-telegram-accountid-binding.test.ts b/src/telegram/bot.create-telegram-bot.routes-dms-by-telegram-accountid-binding.test.ts index 929395fe6b..b7e87debf4 100644 --- a/src/telegram/bot.create-telegram-bot.routes-dms-by-telegram-accountid-binding.test.ts +++ b/src/telegram/bot.create-telegram-bot.routes-dms-by-telegram-accountid-binding.test.ts @@ -34,17 +34,17 @@ vi.mock("../config/sessions.js", async (importOriginal) => { }; }); -const { readTelegramAllowFromStore, upsertTelegramPairingRequest } = vi.hoisted(() => ({ - readTelegramAllowFromStore: vi.fn(async () => [] as string[]), - upsertTelegramPairingRequest: vi.fn(async () => ({ +const { readChannelAllowFromStore, upsertChannelPairingRequest } = vi.hoisted(() => ({ + readChannelAllowFromStore: vi.fn(async () => [] as string[]), + upsertChannelPairingRequest: vi.fn(async () => ({ code: "PAIRCODE", created: true, })), })); -vi.mock("./pairing-store.js", () => ({ - readTelegramAllowFromStore, - upsertTelegramPairingRequest, +vi.mock("../pairing/pairing-store.js", () => ({ + readChannelAllowFromStore, + upsertChannelPairingRequest, })); const useSpy = vi.fn(); diff --git a/src/telegram/bot.create-telegram-bot.sends-replies-without-native-reply-threading.test.ts b/src/telegram/bot.create-telegram-bot.sends-replies-without-native-reply-threading.test.ts index 1d482c0a78..d8a5c91c4a 100644 --- a/src/telegram/bot.create-telegram-bot.sends-replies-without-native-reply-threading.test.ts +++ b/src/telegram/bot.create-telegram-bot.sends-replies-without-native-reply-threading.test.ts @@ -39,17 +39,17 @@ vi.mock("../config/sessions.js", async (importOriginal) => { }; }); -const { readTelegramAllowFromStore, upsertTelegramPairingRequest } = vi.hoisted(() => ({ - readTelegramAllowFromStore: vi.fn(async () => [] as string[]), - upsertTelegramPairingRequest: vi.fn(async () => ({ +const { readChannelAllowFromStore, upsertChannelPairingRequest } = vi.hoisted(() => ({ + readChannelAllowFromStore: vi.fn(async () => [] as string[]), + upsertChannelPairingRequest: vi.fn(async () => ({ code: "PAIRCODE", created: true, })), })); -vi.mock("./pairing-store.js", () => ({ - readTelegramAllowFromStore, - upsertTelegramPairingRequest, +vi.mock("../pairing/pairing-store.js", () => ({ + readChannelAllowFromStore, + upsertChannelPairingRequest, })); const useSpy = vi.fn(); diff --git a/src/telegram/bot.media.downloads-media-file-path-no-file-download.test.ts b/src/telegram/bot.media.downloads-media-file-path-no-file-download.test.ts index bd3b73499f..c3e154e997 100644 --- a/src/telegram/bot.media.downloads-media-file-path-no-file-download.test.ts +++ b/src/telegram/bot.media.downloads-media-file-path-no-file-download.test.ts @@ -88,9 +88,9 @@ vi.mock("./sticker-cache.js", () => ({ describeStickerImage: (...args: unknown[]) => describeStickerImageSpy(...args), })); -vi.mock("./pairing-store.js", () => ({ - readTelegramAllowFromStore: vi.fn(async () => [] as string[]), - upsertTelegramPairingRequest: vi.fn(async () => ({ +vi.mock("../pairing/pairing-store.js", () => ({ + readChannelAllowFromStore: vi.fn(async () => [] as string[]), + upsertChannelPairingRequest: vi.fn(async () => ({ code: "PAIRCODE", created: true, })), diff --git a/src/telegram/bot.media.includes-location-text-ctx-fields-pins.test.ts b/src/telegram/bot.media.includes-location-text-ctx-fields-pins.test.ts index 5a4f1b3625..c4a44156b7 100644 --- a/src/telegram/bot.media.includes-location-text-ctx-fields-pins.test.ts +++ b/src/telegram/bot.media.includes-location-text-ctx-fields-pins.test.ts @@ -77,9 +77,9 @@ vi.mock("../config/sessions.js", async (importOriginal) => { }; }); -vi.mock("./pairing-store.js", () => ({ - readTelegramAllowFromStore: vi.fn(async () => [] as string[]), - upsertTelegramPairingRequest: vi.fn(async () => ({ +vi.mock("../pairing/pairing-store.js", () => ({ + readChannelAllowFromStore: vi.fn(async () => [] as string[]), + upsertChannelPairingRequest: vi.fn(async () => ({ code: "PAIRCODE", created: true, })), diff --git a/src/telegram/bot.test.ts b/src/telegram/bot.test.ts index ab79c7adab..a11803125a 100644 --- a/src/telegram/bot.test.ts +++ b/src/telegram/bot.test.ts @@ -56,17 +56,17 @@ vi.mock("../config/sessions.js", async (importOriginal) => { }; }); -const { readTelegramAllowFromStore, upsertTelegramPairingRequest } = vi.hoisted(() => ({ - readTelegramAllowFromStore: vi.fn(async () => [] as string[]), - upsertTelegramPairingRequest: vi.fn(async () => ({ +const { readChannelAllowFromStore, upsertChannelPairingRequest } = vi.hoisted(() => ({ + readChannelAllowFromStore: vi.fn(async () => [] as string[]), + upsertChannelPairingRequest: vi.fn(async () => ({ code: "PAIRCODE", created: true, })), })); -vi.mock("./pairing-store.js", () => ({ - readTelegramAllowFromStore, - upsertTelegramPairingRequest, +vi.mock("../pairing/pairing-store.js", () => ({ + readChannelAllowFromStore, + upsertChannelPairingRequest, })); const { enqueueSystemEvent } = vi.hoisted(() => ({ @@ -569,8 +569,8 @@ describe("createTelegramBot", () => { loadConfig.mockReturnValue({ channels: { telegram: { dmPolicy: "pairing" } }, }); - readTelegramAllowFromStore.mockResolvedValue([]); - upsertTelegramPairingRequest.mockResolvedValue({ + readChannelAllowFromStore.mockResolvedValue([]); + upsertChannelPairingRequest.mockResolvedValue({ code: "PAIRME12", created: true, }); @@ -606,8 +606,8 @@ describe("createTelegramBot", () => { loadConfig.mockReturnValue({ channels: { telegram: { dmPolicy: "pairing" } }, }); - readTelegramAllowFromStore.mockResolvedValue([]); - upsertTelegramPairingRequest + readChannelAllowFromStore.mockResolvedValue([]); + upsertChannelPairingRequest .mockResolvedValueOnce({ code: "PAIRME12", created: true }) .mockResolvedValueOnce({ code: "PAIRME12", created: false }); @@ -2335,7 +2335,7 @@ describe("createTelegramBot", () => { }, }, }); - readTelegramAllowFromStore.mockResolvedValueOnce(["12345"]); + readChannelAllowFromStore.mockResolvedValueOnce(["12345"]); createTelegramBot({ token: "tok" }); const handler = commandSpy.mock.calls.find((call) => call[0] === "status")?.[1] as @@ -2378,7 +2378,7 @@ describe("createTelegramBot", () => { }, }, }); - readTelegramAllowFromStore.mockResolvedValueOnce(["12345"]); + readChannelAllowFromStore.mockResolvedValueOnce(["12345"]); createTelegramBot({ token: "tok" }); const handler = commandSpy.mock.calls.find((call) => call[0] === "status")?.[1] as @@ -2422,7 +2422,7 @@ describe("createTelegramBot", () => { }, }, }); - readTelegramAllowFromStore.mockResolvedValueOnce([]); + readChannelAllowFromStore.mockResolvedValueOnce([]); createTelegramBot({ token: "tok" }); const handler = commandSpy.mock.calls.find((call) => call[0] === "status")?.[1] as diff --git a/src/telegram/pairing-store.test.ts b/src/telegram/pairing-store.test.ts deleted file mode 100644 index 08ef7bdb26..0000000000 --- a/src/telegram/pairing-store.test.ts +++ /dev/null @@ -1,52 +0,0 @@ -import fs from "node:fs/promises"; -import os from "node:os"; -import path from "node:path"; -import { describe, expect, it } from "vitest"; -import { - approveTelegramPairingCode, - listTelegramPairingRequests, - readTelegramAllowFromStore, - upsertTelegramPairingRequest, -} from "./pairing-store.js"; - -async function withTempStateDir(fn: (stateDir: string) => Promise) { - const previous = process.env.OPENCLAW_STATE_DIR; - const dir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-pairing-")); - process.env.OPENCLAW_STATE_DIR = dir; - try { - return await fn(dir); - } finally { - if (previous === undefined) { - delete process.env.OPENCLAW_STATE_DIR; - } else { - process.env.OPENCLAW_STATE_DIR = previous; - } - await fs.rm(dir, { recursive: true, force: true }); - } -} - -describe("telegram pairing store", () => { - it("creates pairing request and approves it into allow store", async () => { - await withTempStateDir(async () => { - const created = await upsertTelegramPairingRequest({ - chatId: "123456789", - username: "ada", - }); - expect(created.code).toBeTruthy(); - - const list = await listTelegramPairingRequests(); - expect(list).toHaveLength(1); - expect(list[0]?.chatId).toBe("123456789"); - expect(list[0]?.code).toBe(created.code); - - const approved = await approveTelegramPairingCode({ code: created.code }); - expect(approved?.chatId).toBe("123456789"); - - const listAfter = await listTelegramPairingRequests(); - expect(listAfter).toHaveLength(0); - - const allow = await readTelegramAllowFromStore(); - expect(allow).toContain("123456789"); - }); - }); -}); diff --git a/src/telegram/pairing-store.ts b/src/telegram/pairing-store.ts deleted file mode 100644 index 74223fb578..0000000000 --- a/src/telegram/pairing-store.ts +++ /dev/null @@ -1,124 +0,0 @@ -import type { OpenClawConfig } from "../config/config.js"; -import { - addChannelAllowFromStoreEntry, - approveChannelPairingCode, - listChannelPairingRequests, - readChannelAllowFromStore, - upsertChannelPairingRequest, -} from "../pairing/pairing-store.js"; - -export type TelegramPairingListEntry = { - chatId: string; - username?: string; - firstName?: string; - lastName?: string; - code: string; - createdAt: string; - lastSeenAt: string; -}; - -const PROVIDER = "telegram" as const; - -export async function readTelegramAllowFromStore( - env: NodeJS.ProcessEnv = process.env, -): Promise { - return readChannelAllowFromStore(PROVIDER, env); -} - -export async function addTelegramAllowFromStoreEntry(params: { - entry: string | number; - env?: NodeJS.ProcessEnv; -}): Promise<{ changed: boolean; allowFrom: string[] }> { - return addChannelAllowFromStoreEntry({ - channel: PROVIDER, - entry: params.entry, - env: params.env, - }); -} - -export async function listTelegramPairingRequests( - env: NodeJS.ProcessEnv = process.env, -): Promise { - const list = await listChannelPairingRequests(PROVIDER, env); - return list.map((r) => ({ - chatId: r.id, - code: r.code, - createdAt: r.createdAt, - lastSeenAt: r.lastSeenAt, - username: r.meta?.username, - firstName: r.meta?.firstName, - lastName: r.meta?.lastName, - })); -} - -export async function upsertTelegramPairingRequest(params: { - chatId: string | number; - username?: string; - firstName?: string; - lastName?: string; - env?: NodeJS.ProcessEnv; -}): Promise<{ code: string; created: boolean }> { - return upsertChannelPairingRequest({ - channel: PROVIDER, - id: String(params.chatId), - env: params.env, - meta: { - username: params.username, - firstName: params.firstName, - lastName: params.lastName, - }, - }); -} - -export async function approveTelegramPairingCode(params: { - code: string; - env?: NodeJS.ProcessEnv; -}): Promise<{ chatId: string; entry?: TelegramPairingListEntry } | null> { - const res = await approveChannelPairingCode({ - channel: PROVIDER, - code: params.code, - env: params.env, - }); - if (!res) { - return null; - } - const entry = res.entry - ? { - chatId: res.entry.id, - code: res.entry.code, - createdAt: res.entry.createdAt, - lastSeenAt: res.entry.lastSeenAt, - username: res.entry.meta?.username, - firstName: res.entry.meta?.firstName, - lastName: res.entry.meta?.lastName, - } - : undefined; - return { chatId: res.id, entry }; -} - -export async function resolveTelegramEffectiveAllowFrom(params: { - cfg: OpenClawConfig; - env?: NodeJS.ProcessEnv; -}): Promise<{ dm: string[]; group: string[] }> { - const env = params.env ?? process.env; - const cfgAllowFrom = (params.cfg.channels?.telegram?.allowFrom ?? []) - .map((v) => String(v).trim()) - .filter(Boolean) - .map((v) => v.replace(/^(telegram|tg):/i, "")) - .filter((v) => v !== "*"); - const cfgGroupAllowFrom = (params.cfg.channels?.telegram?.groupAllowFrom ?? []) - .map((v) => String(v).trim()) - .filter(Boolean) - .map((v) => v.replace(/^(telegram|tg):/i, "")) - .filter((v) => v !== "*"); - const storeAllowFrom = await readTelegramAllowFromStore(env); - - const dm = Array.from(new Set([...cfgAllowFrom, ...storeAllowFrom])); - const group = Array.from( - new Set([ - ...(cfgGroupAllowFrom.length > 0 ? cfgGroupAllowFrom : cfgAllowFrom), - ...storeAllowFrom, - ]), - ); - return { dm, group }; -}