{"version":3,"file":"forum-commands.mjs","names":[],"sources":["../../../src/commands/forum-commands.ts"],"sourcesContent":["/**\n * Forum Topic commands — manage Telegram threaded mode topics.\n *\n * /topics         — List registered topics and their bindings\n * /topics_setup   — Set up suggested topic structure\n * /topic_bind     — Bind a topic to a persona/mode configuration\n * /topic_unbind   — Remove a topic binding\n */\n\nimport { getForumTopics, type TopicPurpose } from '../services/forum-topics.js';\nimport { getThreadBindings } from '../services/thread-bindings.js';\n\nfunction getSenderId(ctx: any): string {\n  return ctx?.senderId ?? ctx?.from ?? ctx?.metadata?.senderId ?? 'unknown';\n}\n\nfunction getChatId(ctx: any): string {\n  return ctx?.chatId ?? ctx?.metadata?.chatId ?? getSenderId(ctx);\n}\n\nexport const topicsCommand = {\n  name: 'topics',\n  description: 'List forum topics and their bindings',\n  acceptsArgs: false,\n  requireAuth: true,\n  handler: async (ctx: any) => {\n    const chatId = getChatId(ctx);\n    const topics = getForumTopics();\n    const bindings = getThreadBindings();\n\n    if (!topics.isForumEnabled(chatId)) {\n      return {\n        text: `**Forum Topics: Not Enabled**\n\nTo use forum topics:\n1. Enable Topics mode in BotFather for this group\n2. Create topics in the group (Trading, Portfolio, Research, Alerts, etc.)\n3. Run **/topics_setup** in the group to register topics\n\nForum topics give each conversation thread its own isolated LLM context, persona, and safety mode.`,\n      };\n    }\n\n    const registered = topics.listTopics(chatId);\n    if (registered.length === 0) {\n      return {\n        text: 'Forum mode is enabled but no topics are registered.\\n\\nRun **/topics_setup** to set up the suggested topic structure.',\n      };\n    }\n\n    const lines = ['**Forum Topics**', ''];\n\n    for (const topic of registered) {\n      const binding = bindings.getEffectiveBinding(chatId, topic.threadId, topic.purpose);\n      const parts = [\n        `**${topic.name}** (${topic.purpose})`,\n        `  Thread ID: ${topic.threadId}`,\n      ];\n      if (binding.persona) parts.push(`  Persona: ${binding.persona}`);\n      if (binding.safetyMode) parts.push(`  Safety: ${binding.safetyMode}`);\n      if (topic.receivesNotifications) parts.push('  Receives notifications');\n      lines.push(parts.join('\\n'));\n    }\n\n    return { text: lines.join('\\n') };\n  },\n};\n\nexport const topicsSetupCommand = {\n  name: 'topics_setup',\n  description: 'Set up suggested forum topic structure with default bindings',\n  acceptsArgs: false,\n  requireAuth: true,\n  handler: async (ctx: any) => {\n    const chatId = getChatId(ctx);\n    const topics = getForumTopics();\n    const bindings = getThreadBindings();\n\n    topics.setForumEnabled(chatId, true);\n\n    const suggested = topics.getSuggestedTopics();\n    const lines = [\n      '**Forum Topics Setup**',\n      '',\n      'Create these topics in your Telegram group, then register each one with **/topic_bind**:',\n      '',\n    ];\n\n    for (let i = 0; i < suggested.length; i++) {\n      const s = suggested[i]!;\n      lines.push(`${i + 1}. **${s.emoji} ${s.name}** — ${s.purpose}`);\n    }\n\n    lines.push(\n      '',\n      'After creating topics in Telegram, register each one:',\n      '```',\n      '/topic_bind <thread_id> <purpose>',\n      '/topic_bind 42 trading',\n      '/topic_bind 43 portfolio',\n      '```',\n      '',\n      'Thread IDs are visible in the Telegram API response when you create topics.',\n      '',\n      'Forum mode is now **enabled** for this chat.',\n    );\n\n    return { text: lines.join('\\n') };\n  },\n};\n\nexport const topicBindCommand = {\n  name: 'topic_bind',\n  description: 'Bind a topic to a purpose and configuration (e.g. /topic_bind 42 trading)',\n  acceptsArgs: true,\n  requireAuth: true,\n  handler: async (ctx: any) => {\n    const chatId = getChatId(ctx);\n    const args = (ctx?.args ?? ctx?.text ?? '').trim();\n\n    // Parse: <thread_id> <purpose> [persona]\n    const parts = args.split(/\\s+/);\n    if (parts.length < 2) {\n      return {\n        text: 'Usage: /topic_bind <thread_id> <purpose> [persona]\\n\\nPurposes: trading, portfolio, research, alerts, governance, social, admin, general\\nPersonas: professional, degen, chill, technical, mentor',\n      };\n    }\n\n    const threadId = parseInt(parts[0]!, 10);\n    if (isNaN(threadId)) {\n      return { text: 'Invalid thread_id. Must be a number.' };\n    }\n\n    const purpose = parts[1]!.toLowerCase() as TopicPurpose;\n    const validPurposes: TopicPurpose[] = ['general', 'trading', 'portfolio', 'research', 'alerts', 'governance', 'social', 'admin'];\n    if (!validPurposes.includes(purpose)) {\n      return { text: `Invalid purpose: \"${purpose}\". Valid: ${validPurposes.join(', ')}` };\n    }\n\n    const persona = parts[2] ?? undefined;\n\n    const topics = getForumTopics();\n    const bindings = getThreadBindings();\n\n    // Register topic\n    topics.registerTopic(chatId, threadId, purpose, purpose);\n\n    // Apply binding\n    const binding = bindings.applyDefaults(chatId, threadId, purpose);\n    if (persona) {\n      bindings.bind(chatId, threadId, { persona });\n    }\n\n    return {\n      text: `Topic bound: thread ${threadId} → **${purpose}**${persona ? ` (persona: ${persona})` : ''}\\n\\nSafety mode: ${binding.safetyMode ?? 'default'}\\nNotifications: ${topics.listTopics(chatId).find(t => t.threadId === threadId)?.receivesNotifications ? 'yes' : 'no'}`,\n    };\n  },\n};\n\nexport const topicUnbindCommand = {\n  name: 'topic_unbind',\n  description: 'Remove a topic binding (e.g. /topic_unbind 42)',\n  acceptsArgs: true,\n  requireAuth: true,\n  handler: async (ctx: any) => {\n    const chatId = getChatId(ctx);\n    const args = (ctx?.args ?? ctx?.text ?? '').trim();\n\n    const threadId = parseInt(args, 10);\n    if (isNaN(threadId)) {\n      return { text: 'Usage: /topic_unbind <thread_id>' };\n    }\n\n    const topics = getForumTopics();\n    const bindings = getThreadBindings();\n\n    const removed = topics.unregisterTopic(chatId, threadId);\n    bindings.unbind(chatId, threadId);\n\n    return {\n      text: removed\n        ? `Topic ${threadId} unbound and removed.`\n        : `No topic found with thread ID ${threadId}.`,\n    };\n  },\n};\n"],"mappings":"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"}