refactor: use shared pairing store for telegram

This commit is contained in:
Ayaan Zaidi
2026-02-01 14:55:41 +05:30
committed by Ayaan Zaidi
parent ca92597e1f
commit 24fbafa9a7
23 changed files with 95 additions and 288 deletions

View File

@@ -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 }),

View File

@@ -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 }),

View File

@@ -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 }),

View File

@@ -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 }),

View File

@@ -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 }),

View File

@@ -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<string, unknown>;
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({

View File

@@ -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(

View File

@@ -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)", () => {

View File

@@ -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({

View File

@@ -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();

View File

@@ -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();

View File

@@ -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();

View File

@@ -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();

View File

@@ -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 });

View File

@@ -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();

View File

@@ -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();

View File

@@ -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();

View File

@@ -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();

View File

@@ -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,
})),

View File

@@ -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,
})),

View File

@@ -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

View File

@@ -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<T>(fn: (stateDir: string) => Promise<T>) {
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");
});
});
});

View File

@@ -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<string[]> {
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<TelegramPairingListEntry[]> {
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 };
}