import Imap from 'node-imap'; import { simpleParser } from 'mailparser'; import { GoogleGenAI } from '@google/genai'; import prisma from '../database.js'; // --- Environment Variables --- const { GOOGLE_API_KEY } = process.env; // Added // --- AI Setup --- const ai = GOOGLE_API_KEY ? new GoogleGenAI({ apiKey: GOOGLE_API_KEY, }) : null; // Added export async function fetchAndFormatEmails() { return new Promise((resolve, reject) => { const imapConfig = { user: process.env.OUTLOOK_EMAIL_ADDRESS, password: process.env.OUTLOOK_APP_PASSWORD, host: 'outlook.office365.com', port: 993, tls: true, tlsOptions: { rejectUnauthorized: false } // Adjust as needed for your environment }; const imap = new Imap(imapConfig); const emailsJson = []; function openInbox(cb) { // Note: IMAP uses '/' as hierarchy separator, adjust if your server uses something else imap.openBox('SLSNotifications/Reports/Backups', false, cb); } imap.once('ready', () => { openInbox((err, box) => { if (err) { imap.end(); return reject(new Error(`Error opening mailbox: ${err.message}`)); } const yesterday = new Date(); yesterday.setDate(yesterday.getDate() - 1); const searchCriteria = [['SINCE', yesterday.toISOString().split('T')[0]]]; // Search since midnight yesterday const fetchOptions = { bodies: ['HEADER.FIELDS (SUBJECT DATE)', 'TEXT'], struct: true }; imap.search(searchCriteria, (searchErr, results) => { if (searchErr) { imap.end(); return reject(new Error(`Error searching emails: ${searchErr.message}`)); } if (results.length === 0) { console.log('No emails found from the last 24 hours.'); imap.end(); return resolve([]); } const f = imap.fetch(results, fetchOptions); let processedCount = 0; f.on('message', (msg, seqno) => { let header = ''; let body = ''; msg.on('body', (stream, info) => { let buffer = ''; stream.on('data', (chunk) => { buffer += chunk.toString('utf8'); }); stream.once('end', () => { if (info.which === 'TEXT') { body = buffer; } else { // Assuming HEADER.FIELDS (SUBJECT DATE) comes as one chunk header = buffer; } }); }); msg.once('attributes', (attrs) => { // Attributes might contain date if not fetched via header }); msg.once('end', async () => { try { // Use mailparser to handle potential encoding issues and structure const mail = await simpleParser(`Subject: ${header.match(/Subject: (.*)/i)?.[1] || ''}\nDate: ${header.match(/Date: (.*)/i)?.[1] || ''}\n\n${body}`); emailsJson.push({ title: mail.subject || 'No Subject', time: mail.date ? mail.date.toISOString() : 'No Date', body: mail.text || mail.html || 'No Body Content' // Prefer text, fallback to html, then empty }); } catch (parseErr) { console.error(`Error parsing email seqno ${seqno}:`, parseErr); // Decide if you want to reject or just skip this email } processedCount++; if (processedCount === results.length) { // This check might be slightly inaccurate if errors occur, // but it's a common pattern. Consider refining with promises. } }); }); f.once('error', (fetchErr) => { console.error('Fetch error: ' + fetchErr); // Don't reject here immediately, might still get some emails }); f.once('end', () => { console.log('Done fetching all messages!'); imap.end(); }); }); }); }); imap.once('error', (err) => { reject(new Error(`IMAP Connection Error: ${err.message}`)); }); imap.once('end', () => { console.log('IMAP Connection ended.'); resolve(emailsJson); // Resolve with the collected emails }); imap.connect(); }); } // --- Email Summary Logic (New Function) --- export async function generateAndStoreEmailSummary() { console.log('Attempting to generate and store Email summary...'); if (!ai) { console.error('Google AI API key not configured. Skipping email summary generation.'); return; } try { // Get the prompt from the database settings using Prisma const setting = await prisma.setting.findUnique({ where: { key: 'emailPrompt' }, // Use 'emailPrompt' as the key select: { value: true } }); const promptTemplate = setting?.value; if (!promptTemplate) { console.error('Email prompt not found in database settings (key: emailPrompt). Skipping summary generation.'); return; } const emails = await fetchAndFormatEmails(); let summaryText; if (emails.length === 0) { summaryText = "No relevant emails found in the last 24 hours."; console.log('No recent emails found for summary.'); } else { console.log(`Found ${emails.length} recent emails. Generating summary...`); // Replace placeholder in the prompt template // Ensure your prompt template uses $EMAIL_DATA let prompt = promptTemplate.replaceAll("$EMAIL_DATA", JSON.stringify(emails, null, 2)); // Call the AI model (adjust model name and config as needed) const response = await ai.models.generateContent({ "model": "gemini-2.5-preview-04-17", "contents": prompt, config: { temperature: 0 // Adjust temperature as needed } }); summaryText = response.text; console.log('Email summary generated successfully by AI.'); } // Store the summary in the database using Prisma upsert const today = new Date(); today.setUTCHours(0, 0, 0, 0); // Use UTC start of day for consistency await prisma.emailSummary.upsert({ where: { summaryDate: today }, update: { summaryText: summaryText, // generatedAt is updated automatically by @default(now()) }, create: { summaryDate: today, summaryText: summaryText, }, }); console.log(`Email summary for ${today.toISOString().split('T')[0]} stored/updated in the database.`); } catch (error) { console.error("Error during Email summary generation/storage:", error); // Re-throw or handle as appropriate for your application throw error; } }