diff --git a/src/auto-reply/templating.ts b/src/auto-reply/templating.ts index 1e07f6a32..47aed376e 100644 --- a/src/auto-reply/templating.ts +++ b/src/auto-reply/templating.ts @@ -103,6 +103,8 @@ export type MsgContext = { CommandTargetSessionKey?: string; /** Thread identifier (Telegram topic id or Matrix thread event id). */ MessageThreadId?: string | number; + /** Topic name from config (Telegram forum topics). */ + TopicName?: string; /** Telegram forum supergroup marker. */ IsForum?: boolean; /** diff --git a/src/config/types.telegram.ts b/src/config/types.telegram.ts index 9a96bce45..820b61473 100644 --- a/src/config/types.telegram.ts +++ b/src/config/types.telegram.ts @@ -133,6 +133,8 @@ export type TelegramAccountConfig = { }; export type TelegramTopicConfig = { + /** Human-readable name for this topic (displayed in conversation context). */ + name?: string; requireMention?: boolean; /** If specified, only load these skills for this topic. Omit = all skills; empty = no skills. */ skills?: string[]; diff --git a/src/config/zod-schema.providers-core.ts b/src/config/zod-schema.providers-core.ts index ed7dda22a..6029d11e0 100644 --- a/src/config/zod-schema.providers-core.ts +++ b/src/config/zod-schema.providers-core.ts @@ -37,6 +37,7 @@ const TelegramCapabilitiesSchema = z.union([ export const TelegramTopicSchema = z .object({ + name: z.string().optional(), requireMention: z.boolean().optional(), skills: z.array(z.string()).optional(), enabled: z.boolean().optional(), diff --git a/src/telegram/bot-message-context.ts b/src/telegram/bot-message-context.ts index 9696e4f1b..7f6cc9ebc 100644 --- a/src/telegram/bot-message-context.ts +++ b/src/telegram/bot-message-context.ts @@ -495,7 +495,9 @@ export const buildTelegramMessageContext = async ({ forwardOrigin.date ? ` at ${new Date(forwardOrigin.date * 1000).toISOString()}` : "" }]\n` : ""; - const groupLabel = isGroup ? buildGroupLabel(msg, chatId, resolvedThreadId) : undefined; + const groupLabel = isGroup + ? buildGroupLabel(msg, chatId, resolvedThreadId, topicConfig?.name) + : undefined; const senderName = buildSenderName(msg); const conversationLabel = isGroup ? (groupLabel ?? `group:${chatId}`) @@ -605,6 +607,8 @@ export const buildTelegramMessageContext = async ({ CommandAuthorized: commandAuthorized, // For groups: use resolvedThreadId (forum topics only); for DMs: use raw messageThreadId MessageThreadId: isGroup ? resolvedThreadId : messageThreadId, + // Topic name from config (if configured) + TopicName: isGroup && resolvedThreadId != null ? topicConfig?.name : undefined, IsForum: isForum, // Originating channel for reply routing. OriginatingChannel: "telegram" as const, diff --git a/src/telegram/bot/helpers.ts b/src/telegram/bot/helpers.ts index cd57392c0..771a585fc 100644 --- a/src/telegram/bot/helpers.ts +++ b/src/telegram/bot/helpers.ts @@ -106,9 +106,16 @@ export function buildGroupLabel( msg: TelegramMessage, chatId: number | string, messageThreadId?: number, + topicName?: string, ) { const title = msg.chat?.title; - const topicSuffix = messageThreadId != null ? ` topic:${messageThreadId}` : ""; + // Include topic name if available, otherwise just show topic ID + const topicSuffix = + messageThreadId != null + ? topicName + ? ` topic:${topicName}` + : ` topic:${messageThreadId}` + : ""; if (title) return `${title} id:${chatId}${topicSuffix}`; return `group:${chatId}${topicSuffix}`; }