diff --git a/extensions/telegram-user/src/channel.ts b/extensions/telegram-user/src/channel.ts index 2199a98cd..0aa2094ac 100644 --- a/extensions/telegram-user/src/channel.ts +++ b/extensions/telegram-user/src/channel.ts @@ -8,9 +8,11 @@ import { formatPairingApproveHint, normalizeAccountId, setAccountEnabledInConfigSection, + type ChannelGroupContext, type ChannelPlugin, type ChannelSetupInput, type ClawdbotConfig, + type GroupToolPolicyConfig, } from "clawdbot/plugin-sdk"; import { @@ -51,6 +53,36 @@ type TelegramUserSetupInput = ChannelSetupInput & { apiHash?: string; }; +function normalizeTelegramUserGroupKey(raw?: string | null): string | undefined { + if (!raw) return undefined; + const trimmed = raw.trim(); + if (!trimmed) return undefined; + const withoutPrefix = trimmed.replace(/^telegram-user:group:/i, ""); + const [base] = withoutPrefix.split(/:topic:/i); + const normalized = base?.trim(); + return normalized ? normalized : undefined; +} + +function resolveTelegramUserGroupToolPolicy( + params: ChannelGroupContext, +): GroupToolPolicyConfig | undefined { + const account = resolveTelegramUserAccount({ + cfg: params.cfg as CoreConfig, + accountId: params.accountId, + }); + const groups = account.config.groups ?? {}; + const groupId = normalizeTelegramUserGroupKey(params.groupId); + const groupChannel = normalizeTelegramUserGroupKey(params.groupChannel); + const candidates = [groupId, groupChannel, "*"].filter( + (value): value is string => Boolean(value), + ); + for (const key of candidates) { + const entry = groups[key]; + if (entry?.tools) return entry.tools; + } + return undefined; +} + const isSessionLinked = async (accountId: string): Promise => { const sessionPath = resolveTelegramUserSessionPath(accountId); return fs.existsSync(sessionPath); @@ -142,6 +174,21 @@ export const telegramUserPlugin: ChannelPlugin = { raw.replace(/^(telegram-user|telegram|tg):/i, "").toLowerCase(), }; }, + collectWarnings: ({ account, cfg }) => { + const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy; + const groupPolicy = account.config.groupPolicy ?? defaultGroupPolicy ?? "allowlist"; + if (groupPolicy !== "open") return []; + const groupAllowlistConfigured = + account.config.groups && Object.keys(account.config.groups).length > 0; + if (groupAllowlistConfigured) { + return [ + `- Telegram user groups: groupPolicy="open" allows any member in allowed groups to trigger (mention-gated). Set channels.telegram-user.groupPolicy="allowlist" + channels.telegram-user.groupAllowFrom to restrict senders.`, + ]; + } + return [ + `- Telegram user groups: groupPolicy="open" with no channels.telegram-user.groups allowlist; any group can add + ping (mention-gated). Set channels.telegram-user.groupPolicy="allowlist" + channels.telegram-user.groupAllowFrom or configure channels.telegram-user.groups.`, + ]; + }, }, groups: { resolveRequireMention: ({ cfg, groupId, accountId }) => @@ -151,6 +198,7 @@ export const telegramUserPlugin: ChannelPlugin = { groupId, accountId, }), + resolveToolPolicy: resolveTelegramUserGroupToolPolicy, }, threading: { resolveReplyToMode: ({ cfg }) => cfg.channels?.["telegram-user"]?.replyToMode ?? "first", diff --git a/extensions/telegram-user/src/config-schema.ts b/extensions/telegram-user/src/config-schema.ts index 134f97050..0f42c0762 100644 --- a/extensions/telegram-user/src/config-schema.ts +++ b/extensions/telegram-user/src/config-schema.ts @@ -1,5 +1,12 @@ import { z } from "zod"; +import { + DmPolicySchema, + GroupPolicySchema, + ToolPolicySchema, + requireOpenAllowFrom, +} from "clawdbot/plugin-sdk"; + const allowFromEntry = z.union([z.string(), z.number()]); const TelegramUserTopicSchema = z @@ -16,6 +23,7 @@ const TelegramUserGroupSchema = z .object({ requireMention: z.boolean().optional(), skills: z.array(z.string()).optional(), + tools: ToolPolicySchema, topics: z.record(z.string(), TelegramUserTopicSchema.optional()).optional(), enabled: z.boolean().optional(), allowFrom: z.array(allowFromEntry).optional(), @@ -23,23 +31,43 @@ const TelegramUserGroupSchema = z }) .strict(); -const TelegramUserAccountSchema = z +const TelegramUserAccountSchemaBase = z .object({ name: z.string().optional(), enabled: z.boolean().optional(), apiId: z.number().int().positive().optional(), apiHash: z.string().optional(), - dmPolicy: z.enum(["pairing", "allowlist", "open", "disabled"]).optional(), + dmPolicy: DmPolicySchema.optional().default("pairing"), allowFrom: z.array(allowFromEntry).optional(), replyToMode: z.enum(["off", "first", "all"]).optional(), textChunkLimit: z.number().int().positive().optional(), mediaMaxMb: z.number().positive().optional(), groupAllowFrom: z.array(allowFromEntry).optional(), - groupPolicy: z.enum(["open", "allowlist", "disabled"]).optional(), + groupPolicy: GroupPolicySchema.optional().default("allowlist"), groups: z.record(z.string(), TelegramUserGroupSchema.optional()).optional(), }) .strict(); -export const TelegramUserConfigSchema = TelegramUserAccountSchema.extend({ - accounts: z.record(z.string(), TelegramUserAccountSchema.optional()).optional(), +const TelegramUserAccountSchema = TelegramUserAccountSchemaBase.superRefine((value, ctx) => { + requireOpenAllowFrom({ + policy: value.dmPolicy, + allowFrom: value.allowFrom, + ctx, + path: ["allowFrom"], + message: + 'channels.telegram-user.dmPolicy="open" requires channels.telegram-user.allowFrom to include "*"', + }); +}); + +export const TelegramUserConfigSchema = TelegramUserAccountSchemaBase.extend({ + accounts: z.record(z.string(), TelegramUserAccountSchema.optional()).optional(), +}).superRefine((value, ctx) => { + requireOpenAllowFrom({ + policy: value.dmPolicy, + allowFrom: value.allowFrom, + ctx, + path: ["allowFrom"], + message: + 'channels.telegram-user.dmPolicy="open" requires channels.telegram-user.allowFrom to include "*"', + }); }); diff --git a/extensions/telegram-user/src/types.ts b/extensions/telegram-user/src/types.ts index 26649e011..59a6e32a9 100644 --- a/extensions/telegram-user/src/types.ts +++ b/extensions/telegram-user/src/types.ts @@ -1,4 +1,4 @@ -import type { DmPolicy, GroupPolicy } from "clawdbot/plugin-sdk"; +import type { DmPolicy, GroupPolicy, GroupToolPolicyConfig } from "clawdbot/plugin-sdk"; export type TelegramUserTopicConfig = { requireMention?: boolean; @@ -11,6 +11,7 @@ export type TelegramUserTopicConfig = { export type TelegramUserGroupConfig = { requireMention?: boolean; skills?: string[]; + tools?: GroupToolPolicyConfig; topics?: Record; enabled?: boolean; allowFrom?: Array;