import { ipcMain, BrowserWindow } from 'electron' import { IPC } from '@shared/ipc-channels' import { accountsRepo, telegramRepo, telegramUserRepo, whatsappRepo, messagesRepo, rulesRepo, contactsRepo, aiProvidersRepo } from './db/repositories' import { settingsRepo, SETTING_KEYS } from './db/settings' import { imapService } from './services/imap' import { smtpService } from './services/smtp' import { telegramService } from './services/telegram' import { telegramMtprotoService } from './services/telegram-mtproto' import { whatsappService } from './services/whatsapp' import { aiService } from './services/ai' import { connectGmail } from './services/gmail-oauth' import { importContactsDialog } from './services/contacts-import' import type { IpcResponse, SendRequest, AiProviderConfig, AppSettings, EmailAccount } from '@shared/types' function ok(data: T): IpcResponse { return { ok: true, data } } function fail(err: unknown): IpcResponse { return { ok: false, error: err instanceof Error ? err.message : String(err) } } function on(channel: string, handler: (...args: any[]) => Promise | T): void { ipcMain.handle(channel, async (_event, ...args) => { try { return ok(await handler(...args)) } catch (err) { console.error(`[ipc:${channel}]`, err) return fail(err) } }) } export function broadcast(channel: string, payload: unknown): void { for (const win of BrowserWindow.getAllWindows()) { win.webContents.send(channel, payload) } } export function registerIpcHandlers(): void { // -------- Accounts -------- on(IPC.accountsList, () => accountsRepo.list()) on(IPC.accountsCreate, (payload) => { const created = accountsRepo.create(payload) if (created.enabled) imapService.startPolling(created) return created }) on(IPC.accountsUpdate, (id, payload) => { const updated = accountsRepo.update(id, payload) imapService.stopPolling(id) smtpService.invalidate(id) if (updated.enabled) imapService.startPolling(updated) return updated }) on(IPC.accountsDelete, (id) => { imapService.stopPolling(id) smtpService.invalidate(id) accountsRepo.delete(id) return true }) on(IPC.accountsTest, async (id) => { const a = accountsRepo.get(id) if (!a) throw new Error('Account non trovato') await imapService.testConnection(a) await smtpService.verify(a) return true }) on(IPC.accountsStartPolling, (id) => { const a = accountsRepo.get(id) if (!a) throw new Error('Account non trovato') imapService.startPolling(a) return true }) on(IPC.accountsStopPolling, (id) => { imapService.stopPolling(id) return true }) // -------- Telegram -------- on(IPC.telegramList, () => telegramRepo.list()) on(IPC.telegramCreate, (payload) => telegramRepo.create(payload)) on(IPC.telegramUpdate, (id, payload) => { const bot = telegramRepo.update(id, payload) telegramService.invalidate(id) return bot }) on(IPC.telegramDelete, (id) => { telegramService.invalidate(id) telegramRepo.delete(id) return true }) on(IPC.telegramTest, async (id) => { const bot = telegramRepo.get(id) if (!bot) throw new Error('Bot non trovato') return await telegramService.verify(bot) }) on(IPC.telegramDiscover, async (id) => { const bot = telegramRepo.get(id) if (!bot) throw new Error('Bot non trovato') return await telegramService.discoverChats(bot) }) // -------- Telegram User (MTProto) -------- on(IPC.tguList, () => telegramUserRepo.list()) on(IPC.tguCreate, (payload) => telegramUserRepo.create(payload)) on(IPC.tguDelete, async (id) => { await telegramMtprotoService.logout(id).catch(() => undefined) telegramUserRepo.delete(id) return true }) on(IPC.tguStartLogin, async (id) => { await telegramMtprotoService.startLogin(id) return true }) on(IPC.tguSubmitCode, async (id, code: string) => { return await telegramMtprotoService.submitCode(id, code) }) on(IPC.tguSubmit2fa, async (id, password: string) => { return await telegramMtprotoService.submit2fa(id, password) }) on(IPC.tguLogout, async (id) => { await telegramMtprotoService.logout(id) return true }) on(IPC.tguResolve, async (id, recipient: string) => { const account = telegramUserRepo.get(id) if (!account) throw new Error('Account non trovato') return await telegramMtprotoService.resolveContact(account, recipient) }) // -------- WhatsApp -------- on(IPC.whatsappList, () => whatsappRepo.list()) on(IPC.whatsappCreate, (payload) => whatsappRepo.create(payload)) on(IPC.whatsappUpdate, (id, payload) => whatsappRepo.update(id, payload)) on(IPC.whatsappDelete, async (id) => { await whatsappService.stop(id) whatsappRepo.delete(id) return true }) on(IPC.whatsappStart, async (id) => { const s = whatsappRepo.get(id) if (!s) throw new Error('Sessione non trovata') await whatsappService.start(s) return true }) on(IPC.whatsappStop, async (id) => { await whatsappService.stop(id) return true }) on(IPC.whatsappStatus, (id) => whatsappService.getStatus(id)) // -------- Messages -------- on(IPC.messagesList, (filter) => messagesRepo.list(filter)) on(IPC.messagesGet, (id) => messagesRepo.get(id)) on(IPC.messagesDelete, (id) => { messagesRepo.delete(id) return true }) on(IPC.messagesSend, async (req: SendRequest) => { let result: { messageId: string | number } if (req.channel === 'smtp') { if (!req.accountId) throw new Error('accountId obbligatorio per SMTP') const account = accountsRepo.get(req.accountId) if (!account) throw new Error('Account non trovato') result = await smtpService.send(account, { to: req.to, cc: req.cc, bcc: req.bcc, subject: req.subject, text: req.body, html: req.html, attachments: req.attachments }) } else if (req.channel === 'telegram') { if (!req.accountId) throw new Error('Bot Telegram id obbligatorio (accountId)') const bot = telegramRepo.get(req.accountId) if (!bot) throw new Error('Bot non trovato') const res = await telegramService.send(bot, req.to, req.body, req.attachments) result = { messageId: res.messageId } } else if (req.channel === 'telegram_user') { if (!req.accountId) throw new Error('Account Telegram (MTProto) id obbligatorio') const account = telegramUserRepo.get(req.accountId) if (!account) throw new Error('Account Telegram non trovato') const res = await telegramMtprotoService.send(account, req.to, req.body, req.attachments) result = { messageId: res.messageId } } else if (req.channel === 'whatsapp') { if (!req.accountId) throw new Error('Sessione WhatsApp id obbligatorio (accountId)') const res = await whatsappService.send(req.accountId, req.to, req.body, req.attachments) result = res } else { throw new Error(`Canale non supportato: ${req.channel}`) } const stored = messagesRepo.create({ accountId: req.channel === 'smtp' ? req.accountId : undefined, direction: 'outbound', channel: req.channel, externalId: String(result.messageId), toAddr: req.to, cc: req.cc, bcc: req.bcc, subject: req.subject, bodyText: req.body, bodyHtml: req.html, status: 'sent', sentAt: new Date().toISOString() } as any) return stored }) // -------- Rules -------- on(IPC.rulesList, () => rulesRepo.list()) on(IPC.rulesCreate, (payload) => rulesRepo.create(payload)) on(IPC.rulesUpdate, (id, payload) => rulesRepo.update(id, payload)) on(IPC.rulesDelete, (id) => { rulesRepo.delete(id) return true }) // -------- Contacts -------- on(IPC.contactsList, () => contactsRepo.list()) on(IPC.contactsCreate, (payload) => contactsRepo.create(payload)) on(IPC.contactsUpdate, (id, payload) => contactsRepo.update(id, payload)) on(IPC.contactsDelete, (id) => { contactsRepo.delete(id) return true }) on(IPC.contactsImport, async () => { const result = await importContactsDialog() return result }) // -------- AI -------- on(IPC.aiList, () => aiProvidersRepo.list()) on(IPC.aiCreate, (payload: Omit) => aiProvidersRepo.create(payload)) on(IPC.aiUpdate, (id, payload) => aiProvidersRepo.update(id, payload)) on(IPC.aiDelete, (id) => { aiProvidersRepo.delete(id) return true }) on(IPC.aiTest, async (id) => { const p = aiProvidersRepo.get(id) if (!p) throw new Error('Provider non trovato') return await aiService.test(p) }) on(IPC.aiSummarize, async (messageId, providerId) => { const message = messagesRepo.get(messageId) if (!message) throw new Error('Messaggio non trovato') const provider = providerId ? aiProvidersRepo.get(providerId) : undefined return await aiService.summarize(message, provider) }) on(IPC.aiClassify, async (messageId, providerId) => { const message = messagesRepo.get(messageId) if (!message) throw new Error('Messaggio non trovato') const provider = providerId ? aiProvidersRepo.get(providerId) : undefined return await aiService.classify(message, provider) }) on(IPC.aiExtract, async (messageId, providerId) => { const message = messagesRepo.get(messageId) if (!message) throw new Error('Messaggio non trovato') const provider = providerId ? aiProvidersRepo.get(providerId) : undefined return await aiService.extract(message, provider) }) on(IPC.aiSuggestReply, async (messageId, providerId) => { const message = messagesRepo.get(messageId) if (!message) throw new Error('Messaggio non trovato') const provider = providerId ? aiProvidersRepo.get(providerId) : undefined return await aiService.suggestReply(message, provider) }) // -------- Settings -------- on(IPC.settingsGet, (): AppSettings => ({ gmailClientId: settingsRepo.get(SETTING_KEYS.gmailClientId), gmailClientSecret: settingsRepo.get(SETTING_KEYS.gmailClientSecret), telegramApiId: settingsRepo.get(SETTING_KEYS.telegramApiId), telegramApiHash: settingsRepo.get(SETTING_KEYS.telegramApiHash) })) on(IPC.settingsUpdate, (payload: AppSettings) => { if ('gmailClientId' in payload) { settingsRepo.set(SETTING_KEYS.gmailClientId, payload.gmailClientId ?? null) } if ('gmailClientSecret' in payload) { settingsRepo.set(SETTING_KEYS.gmailClientSecret, payload.gmailClientSecret ?? null) } if ('telegramApiId' in payload) { settingsRepo.set(SETTING_KEYS.telegramApiId, payload.telegramApiId ?? null) } if ('telegramApiHash' in payload) { settingsRepo.set(SETTING_KEYS.telegramApiHash, payload.telegramApiHash ?? null) } return true }) // -------- Gmail OAuth connect -------- on(IPC.gmailConnect, async (): Promise => { const result = await connectGmail() const account = accountsRepo.create({ name: result.name ? `${result.name} (Gmail)` : `Gmail: ${result.email}`, authType: 'oauth_google', imap: { host: 'imap.gmail.com', port: 993, secure: true, user: result.email, pass: '' }, smtp: { host: 'smtp.gmail.com', port: 465, secure: true, user: result.email, pass: '', fromName: result.name, fromAddress: result.email }, oauth: { provider: 'google', refreshToken: result.refreshToken, accessToken: result.accessToken, expiresAt: result.expiresAt }, enabled: true, pollInterval: 60 }) imapService.startPolling(account) return account }) }