import type { Config } from '../config/index.js'; import type { HookEngine } from '../hooks/index.js'; import { ChannelRegistry, TelegramAdapter, WebChatAdapter, DiscordAdapter, SlackAdapter, WhatsAppAdapter, MatrixAdapter, SignalAdapter, MattermostAdapter, TeamsAdapter, GoogleChatAdapter, BlueBubblesAdapter, LineAdapter, FeishuAdapter, ZaloAdapter, PairingManager } from '../channels/index.js'; import { CronScheduler, WebhookHandler, GmailWatcher, buildPresetCronJobs } from '../automation/index.js'; import type { GatewayServer } from '../gateway/index.js'; export interface ChannelsDeps { config: Config; channelRegistry: ChannelRegistry; hookEngine: HookEngine; pairingManager?: PairingManager; gateway: GatewayServer; } export interface ChannelsResult { cronScheduler?: CronScheduler; webhookHandler?: WebhookHandler; gmailWatcher?: GmailWatcher; } function resolveChannelMinioShare(config: Config): { enabled: boolean; endpoint: string; accessKey: string; secretKey: string; bucket: string; prefix: string; secure: boolean; expires?: string; mcPath?: string; } | undefined { const minio = config.backup.minio; if (!minio.enabled || !minio.endpoint || !minio.access_key || !minio.secret_key || !minio.bucket) { return undefined; } return { enabled: true, endpoint: minio.endpoint, accessKey: minio.access_key, secretKey: minio.secret_key, bucket: minio.bucket, prefix: minio.prefix, secure: minio.secure, }; } function resolveChannelMinioShareWithOverrides( base: ReturnType, override: { enabled: boolean; endpoint?: string; access_key?: string; secret_key?: string; bucket?: string; prefix: string; secure: boolean; expires: string; mc_path?: string; } | undefined, ): { enabled: boolean; endpoint: string; accessKey: string; secretKey: string; bucket: string; prefix: string; secure: boolean; expires?: string; mcPath?: string; } | undefined { if (!override?.enabled) { return base; } const endpoint = override.endpoint ?? base?.endpoint; const accessKey = override.access_key ?? base?.accessKey; const secretKey = override.secret_key ?? base?.secretKey; const bucket = override.bucket ?? base?.bucket; if (!endpoint || !accessKey || !secretKey || !bucket) { return undefined; } return { enabled: true, endpoint, accessKey, secretKey, bucket, prefix: override.prefix, secure: override.secure, expires: override.expires, mcPath: override.mc_path, }; } export function registerChannels(deps: ChannelsDeps): ChannelsResult { const { config, channelRegistry, hookEngine, pairingManager, gateway } = deps; const channelMinioShare = resolveChannelMinioShare(config); // Register Telegram adapter (if configured) if (config.telegram) { const telegramAdapter = new TelegramAdapter({ botToken: config.telegram.bot_token, allowedChatIds: config.telegram.allowed_chat_ids, requireMention: config.telegram.require_mention, hookEngine, pairingManager, }); channelRegistry.register(telegramAdapter); } // Register Discord adapter (if configured) if (config.discord) { const discordAdapter = new DiscordAdapter({ botToken: config.discord.bot_token, allowedGuildIds: config.discord.allowed_guild_ids.length > 0 ? config.discord.allowed_guild_ids : undefined, allowedChannelIds: config.discord.allowed_channel_ids.length > 0 ? config.discord.allowed_channel_ids : undefined, requireMention: config.discord.require_mention, pairingManager, }); channelRegistry.register(discordAdapter); } // Register Slack adapter (if configured) if (config.slack) { const slackAdapter = new SlackAdapter({ botToken: config.slack.bot_token, appToken: config.slack.app_token, signingSecret: config.slack.signing_secret, allowedChannelIds: config.slack.allowed_channel_ids.length > 0 ? config.slack.allowed_channel_ids : undefined, requireMention: config.slack.require_mention, pairingManager, }); channelRegistry.register(slackAdapter); } // Register WhatsApp adapter (if configured) if (config.whatsapp) { const whatsappAdapter = new WhatsAppAdapter({ allowedNumbers: config.whatsapp.allowed_numbers.length > 0 ? config.whatsapp.allowed_numbers : undefined, allowedGroupIds: config.whatsapp.allowed_group_ids.length > 0 ? config.whatsapp.allowed_group_ids : undefined, requireMention: config.whatsapp.require_mention, dataDir: config.whatsapp.data_dir, pairingManager, allowNoSandbox: config.whatsapp.no_sandbox, }); channelRegistry.register(whatsappAdapter); } // Register Matrix adapter (if configured) if (config.matrix) { const matrixAdapter = new MatrixAdapter({ homeserverUrl: config.matrix.homeserver_url, accessToken: config.matrix.access_token, allowedRoomIds: config.matrix.allowed_room_ids.length > 0 ? config.matrix.allowed_room_ids : undefined, requireMention: config.matrix.require_mention, syncTimeoutMs: config.matrix.sync_timeout_ms, displayName: config.matrix.display_name, pairingManager, }); channelRegistry.register(matrixAdapter); } // Register Signal adapter (if configured) if (config.signal) { const signalAdapter = new SignalAdapter({ account: config.signal.account, signalCliPath: config.signal.signal_cli_path, allowedNumbers: config.signal.allowed_numbers.length > 0 ? config.signal.allowed_numbers : undefined, allowedGroupIds: config.signal.allowed_group_ids.length > 0 ? config.signal.allowed_group_ids : undefined, requireMention: config.signal.require_mention, mentionName: config.signal.mention_name, pollIntervalMs: config.signal.poll_interval_ms, sendTimeoutMs: config.signal.send_timeout_ms, pairingManager, }); channelRegistry.register(signalAdapter); } // Register Mattermost adapter (if configured) if (config.mattermost) { const mattermostAdapter = new MattermostAdapter({ serverUrl: config.mattermost.server_url, botToken: config.mattermost.bot_token, allowedChannelIds: config.mattermost.allowed_channel_ids.length > 0 ? config.mattermost.allowed_channel_ids : undefined, requireMention: config.mattermost.require_mention, mentionName: config.mattermost.mention_name, pollIntervalMs: config.mattermost.poll_interval_ms, pairingManager, }); channelRegistry.register(mattermostAdapter); } // Register Microsoft Teams adapter (if configured) if (config.teams) { const teamsAdapter = new TeamsAdapter({ appId: config.teams.app_id, appPassword: config.teams.app_password, allowedConversationIds: config.teams.allowed_conversation_ids.length > 0 ? config.teams.allowed_conversation_ids : undefined, requireMention: config.teams.require_mention, }); channelRegistry.register(teamsAdapter); gateway.setTeamsHandler(teamsAdapter); } // Register Google Chat adapter (if configured) if (config.google_chat) { const googleChatAdapter = new GoogleChatAdapter({ serviceAccountKeyFile: config.google_chat.service_account_key_file, serviceAccountJson: config.google_chat.service_account_json, webhookToken: config.google_chat.webhook_token, allowedSpaceNames: config.google_chat.allowed_space_names.length > 0 ? config.google_chat.allowed_space_names : undefined, requireMention: config.google_chat.require_mention, }); channelRegistry.register(googleChatAdapter); gateway.setGoogleChatHandler(googleChatAdapter); } // Register BlueBubbles adapter (if configured) if (config.bluebubbles) { const blueBubblesAdapter = new BlueBubblesAdapter({ endpoint: config.bluebubbles.endpoint, apiKey: config.bluebubbles.api_key, webhookToken: config.bluebubbles.webhook_token, allowedChatGuids: config.bluebubbles.allowed_chat_guids.length > 0 ? config.bluebubbles.allowed_chat_guids : undefined, requireMention: config.bluebubbles.require_mention, mentionName: config.bluebubbles.mention_name, }); channelRegistry.register(blueBubblesAdapter); gateway.setBlueBubblesHandler(blueBubblesAdapter); } // Register LINE adapter (if configured) if (config.line) { const lineMinioShare = resolveChannelMinioShareWithOverrides(channelMinioShare, config.line.minio); const effectiveLineMinioShare = lineMinioShare && !config.line.minio.enabled ? { ...lineMinioShare, prefix: `${lineMinioShare.prefix.replace(/\/+$/, '')}/channels/line` } : lineMinioShare; const lineAdapter = new LineAdapter({ channelAccessToken: config.line.channel_access_token, channelSecret: config.line.channel_secret, allowedSourceIds: config.line.allowed_source_ids.length > 0 ? config.line.allowed_source_ids : undefined, requireMention: config.line.require_mention, mentionName: config.line.mention_name, minio: effectiveLineMinioShare, }); channelRegistry.register(lineAdapter); gateway.setLineHandler(lineAdapter); } // Register Feishu adapter (if configured) if (config.feishu) { const feishuAdapter = new FeishuAdapter({ appId: config.feishu.app_id, appSecret: config.feishu.app_secret, webhookToken: config.feishu.webhook_token, allowedChatIds: config.feishu.allowed_chat_ids.length > 0 ? config.feishu.allowed_chat_ids : undefined, requireMention: config.feishu.require_mention, mentionName: config.feishu.mention_name, endpoint: config.feishu.endpoint, }); channelRegistry.register(feishuAdapter); gateway.setFeishuHandler(feishuAdapter); } // Register Zalo adapter (if configured) if (config.zalo) { const zaloMinioShare = resolveChannelMinioShareWithOverrides(channelMinioShare, config.zalo.minio); const effectiveZaloMinioShare = zaloMinioShare && !config.zalo.minio.enabled ? { ...zaloMinioShare, prefix: `${zaloMinioShare.prefix.replace(/\/+$/, '')}/channels/zalo` } : zaloMinioShare; const zaloAdapter = new ZaloAdapter({ oaAccessToken: config.zalo.oa_access_token, endpoint: config.zalo.endpoint, webhookToken: config.zalo.webhook_token, allowedUserIds: config.zalo.allowed_user_ids.length > 0 ? config.zalo.allowed_user_ids : undefined, requireMention: config.zalo.require_mention, mentionName: config.zalo.mention_name, minio: effectiveZaloMinioShare, }); channelRegistry.register(zaloAdapter); gateway.setZaloHandler(zaloAdapter); } // Register WebChat adapter (wraps the gateway) const webChatAdapter = new WebChatAdapter({ gateway }); channelRegistry.register(webChatAdapter); // Register cron scheduler adapter (if any cron jobs configured) let cronScheduler: CronScheduler | undefined; const presetCronJobs = buildPresetCronJobs(config); const cronJobs = [...config.automation.cron, ...presetCronJobs]; if (cronJobs.length > 0) { cronScheduler = new CronScheduler(cronJobs, channelRegistry, config.automation.delivery_mode); channelRegistry.register(cronScheduler); console.log(`Registered ${cronJobs.length} cron job(s)`); } // Register webhook handler adapter (if any webhooks configured) let webhookHandler: WebhookHandler | undefined; if (config.automation.webhooks.length > 0) { webhookHandler = new WebhookHandler( config.automation.webhooks, channelRegistry, config.automation.delivery_mode, config.server.max_request_body_bytes, ); channelRegistry.register(webhookHandler); gateway.setWebhookHandler(webhookHandler); console.log(`Registered ${config.automation.webhooks.length} webhook(s)`); } // Register Gmail watcher adapter (if configured and enabled) let gmailWatcher: GmailWatcher | undefined; if (config.automation.gmail?.enabled) { gmailWatcher = new GmailWatcher(config.automation.gmail, channelRegistry); channelRegistry.register(gmailWatcher); gateway.setGmailHandler(gmailWatcher); console.log('Registered Gmail watcher'); } return { cronScheduler, webhookHandler, gmailWatcher }; }