199 lines
		
	
	
	
		
			6.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			199 lines
		
	
	
	
		
			6.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
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;
 | 
						|
  }
 | 
						|
}
 |