(deps: StartBotsDeps, opts: StartBotsOptions = {})
| 26 | export interface StartBotsOptions { telegram?: boolean; discord?: boolean; slack?: boolean } |
| 27 | |
| 28 | export async function startBots(deps: StartBotsDeps, opts: StartBotsOptions = {}): Promise<void> { |
| 29 | const cwd = deps.cwd ?? process.cwd(); |
| 30 | const botCfg: any = (deps.config as any).bot ?? {}; |
| 31 | const allow: AllowConfig = { |
| 32 | telegram: { allowedUsers: botCfg.telegram?.allowedUsers ?? [] }, |
| 33 | discord: { allowedUsers: botCfg.discord?.allowedUsers ?? [] }, |
| 34 | slack: { allowedUsers: botCfg.slack?.allowedUsers ?? [] }, |
| 35 | }; |
| 36 | // No platform flag ⇒ start every platform that's enabled in config. |
| 37 | const want = (p: 'telegram' | 'discord' | 'slack') => (!opts.telegram && !opts.discord && !opts.slack) || !!opts[p]; |
| 38 | |
| 39 | const transports: Transport[] = []; |
| 40 | const notes: string[] = []; |
| 41 | |
| 42 | if (want('telegram') && (botCfg.telegram?.enabled ?? false)) { |
| 43 | const token = process.env.TELEGRAM_BOT_TOKEN; |
| 44 | if (!token) notes.push('telegram is enabled but TELEGRAM_BOT_TOKEN is missing — add it to ~/.qodex/.env'); |
| 45 | else if (!allow.telegram!.allowedUsers!.length) notes.push('telegram allowlist is empty — set bot.telegram.allowedUsers (deny-by-default means nobody can use it)'); |
| 46 | else transports.push(new TelegramTransport(token)); |
| 47 | } |
| 48 | |
| 49 | if (want('discord') && (botCfg.discord?.enabled ?? false)) { |
| 50 | const token = process.env.DISCORD_TOKEN; |
| 51 | if (!token) notes.push('discord is enabled but DISCORD_TOKEN is missing — add it to ~/.qodex/.env'); |
| 52 | else if (!allow.discord!.allowedUsers!.length) notes.push('discord allowlist is empty — set bot.discord.allowedUsers'); |
| 53 | else { |
| 54 | try { |
| 55 | const { createDiscordTransport } = await import('./adapters/discord.js'); |
| 56 | transports.push(await createDiscordTransport(token)); |
| 57 | } catch (e: any) { notes.push(e?.message ?? 'discord transport failed'); } |
| 58 | } |
| 59 | } |
| 60 | |
| 61 | if (want('slack') && (botCfg.slack?.enabled ?? false)) { |
| 62 | const appToken = process.env.SLACK_APP_TOKEN; |
| 63 | const botToken = process.env.SLACK_BOT_TOKEN; |
| 64 | if (!appToken || !botToken) notes.push('slack is enabled but SLACK_APP_TOKEN / SLACK_BOT_TOKEN is missing — add both to ~/.qodex/.env'); |
| 65 | else if (!allow.slack!.allowedUsers!.length) notes.push('slack allowlist is empty — set bot.slack.allowedUsers'); |
| 66 | else { |
| 67 | try { |
| 68 | const { createSlackTransport } = await import('./adapters/slack.js'); |
| 69 | transports.push(await createSlackTransport(appToken, botToken)); |
| 70 | } catch (e: any) { notes.push(e?.message ?? 'slack transport failed'); } |
| 71 | } |
| 72 | } |
| 73 | |
| 74 | for (const n of notes) console.error('⚠️ ' + n); |
| 75 | if (!transports.length) { |
| 76 | console.error('No bot transports started. Enable bot.telegram/discord/slack in config, put the token(s) in ~/.qodex/.env, and add allowed user ids.'); |
| 77 | return; |
| 78 | } |
| 79 | |
| 80 | const runner = new QodexAgentRunner({ config: deps.config, router: deps.router, registry: deps.registry, permissions: deps.permissions, cwd }); |
| 81 | const gateway = new BotGateway({ transports, agent: runner, allow }); |
| 82 | await gateway.start(); |
| 83 | console.error(`🤖 QodeX bot running on: ${transports.map(t => t.platform).join(', ')} · project=${cwd} · Ctrl-C to stop`); |
| 84 | |
| 85 | const shutdown = () => void gateway.stop().finally(() => process.exit(0)); |
no test coverage detected