168 lines
6.0 KiB
JavaScript
168 lines
6.0 KiB
JavaScript
require('dotenv').config();
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const cron = require('node-cron');
|
|
const axios = require('axios');
|
|
const { google } = require('googleapis');
|
|
|
|
const TOKEN_PATH = path.join(__dirname, 'token.json');
|
|
const CREDENTIALS_PATH = path.join(__dirname, 'credentials.json');
|
|
|
|
const OLLAMA_URL = process.env.OLLAMA_URL || 'http://ollama.ai-agents.svc.cluster.local:11434/api/generate';
|
|
const WHATSAPP_GATEWAY_URL = process.env.WHATSAPP_GATEWAY_URL || 'http://whatsapp-gateway.ai-agents.svc.cluster.local:5001/api/send-message';
|
|
const WHATSAPP_TO = process.env.WHATSAPP_TO || '+14085505485';
|
|
const OLLAMA_MODEL = process.env.OLLAMA_MODEL || 'qwen2.5:0.5b';
|
|
|
|
let oAuth2Client;
|
|
|
|
function loadAuth() {
|
|
if (!fs.existsSync(CREDENTIALS_PATH)) {
|
|
console.error('Missing credentials.json. Please download it from Google Cloud Console and place it in the project directory.');
|
|
process.exit(1);
|
|
}
|
|
if (!fs.existsSync(TOKEN_PATH)) {
|
|
console.error('Missing token.json. Please run "node auth.js" to generate it.');
|
|
process.exit(1);
|
|
}
|
|
const creds = JSON.parse(fs.readFileSync(CREDENTIALS_PATH));
|
|
const {client_secret, client_id, redirect_uris} = creds.installed || creds.web;
|
|
oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirect_uris[0] || 'urn:ietf:wg:oauth:2.0:oob');
|
|
oAuth2Client.setCredentials(JSON.parse(fs.readFileSync(TOKEN_PATH)));
|
|
}
|
|
|
|
async function getUnreadEmails() {
|
|
const gmail = google.gmail({ version: 'v1', auth: oAuth2Client });
|
|
const res = await gmail.users.messages.list({
|
|
userId: 'me',
|
|
q: 'is:unread',
|
|
maxResults: 10
|
|
});
|
|
|
|
const messages = res.data.messages || [];
|
|
const emails = [];
|
|
|
|
for (const msg of messages) {
|
|
const fullMsg = await gmail.users.messages.get({ userId: 'me', id: msg.id, format: 'full' });
|
|
const headers = fullMsg.data.payload.headers;
|
|
const subject = headers.find(h => h.name === 'Subject')?.value || 'No Subject';
|
|
const from = headers.find(h => h.name === 'From')?.value || 'Unknown';
|
|
|
|
let body = '';
|
|
if (fullMsg.data.payload.parts) {
|
|
const part = fullMsg.data.payload.parts.find(p => p.mimeType === 'text/plain');
|
|
if (part && part.body.data) {
|
|
body = Buffer.from(part.body.data, 'base64').toString('utf8');
|
|
}
|
|
} else if (fullMsg.data.payload.body && fullMsg.data.payload.body.data) {
|
|
body = Buffer.from(fullMsg.data.payload.body.data, 'base64').toString('utf8');
|
|
}
|
|
|
|
emails.push({ id: msg.id, subject, from, body: body.substring(0, 1000) });
|
|
}
|
|
return emails;
|
|
}
|
|
|
|
async function markAsRead(msgId) {
|
|
const gmail = google.gmail({ version: 'v1', auth: oAuth2Client });
|
|
await gmail.users.messages.modify({
|
|
userId: 'me',
|
|
id: msgId,
|
|
requestBody: { removeLabelIds: ['UNREAD'] }
|
|
});
|
|
}
|
|
|
|
async function analyzeWithOllama(email) {
|
|
const prompt = `You are an AI assistant that determines if an email is important enough to notify the user immediately on WhatsApp.
|
|
Newsletters, marketing, promotions, and generic updates are NOT important.
|
|
Important things: Bills due, flight/travel updates, urgent requests from people, security alerts, direct personal messages.
|
|
|
|
Email Subject: ${email.subject}
|
|
Email From: ${email.from}
|
|
Email Body Preview:
|
|
${email.body}
|
|
|
|
First, determine if this is important (YES or NO).
|
|
If YES, provide a 1-sentence summary of what the user needs to know.
|
|
Output format exactly like this:
|
|
IMPORTANT: YES/NO
|
|
SUMMARY: <your summary if yes, otherwise leave blank>
|
|
`;
|
|
|
|
try {
|
|
const res = await axios.post(OLLAMA_URL, {
|
|
model: OLLAMA_MODEL,
|
|
prompt: prompt,
|
|
stream: false
|
|
});
|
|
|
|
const responseText = res.data.response || '';
|
|
const isImportant = responseText.includes('IMPORTANT: YES');
|
|
const summaryMatch = responseText.match(/SUMMARY:\s*(.*)/);
|
|
const summary = summaryMatch ? summaryMatch[1].trim() : '';
|
|
|
|
return { isImportant, summary };
|
|
} catch (err) {
|
|
console.error('Ollama Error:', err.message);
|
|
return { isImportant: false, summary: '' };
|
|
}
|
|
}
|
|
|
|
async function sendWhatsAppNotification(email, summary) {
|
|
const message = `📧 *Important Email*\n*From:* ${email.from}\n*Subject:* ${email.subject}\n\n🤖 *Summary:* ${summary}`;
|
|
|
|
try {
|
|
await axios.post(WHATSAPP_GATEWAY_URL, {
|
|
to: WHATSAPP_TO,
|
|
message: message
|
|
}, {
|
|
headers: { 'x-app-name': 'Gmail Agent' }
|
|
});
|
|
console.log(`Notification sent for: ${email.subject}`);
|
|
} catch (err) {
|
|
console.error('WhatsApp Gateway Error:', err.message);
|
|
}
|
|
}
|
|
|
|
async function processEmails() {
|
|
console.log(`[${new Date().toISOString()}] Checking for new emails...`);
|
|
try {
|
|
const emails = await getUnreadEmails();
|
|
if (emails.length === 0) {
|
|
console.log('No new emails.');
|
|
return;
|
|
}
|
|
|
|
console.log(`Found ${emails.length} unread emails.`);
|
|
|
|
for (const email of emails) {
|
|
const analysis = await analyzeWithOllama(email);
|
|
if (analysis.isImportant) {
|
|
await sendWhatsAppNotification(email, analysis.summary);
|
|
} else {
|
|
console.log(`Skipped non-important email: ${email.subject}`);
|
|
}
|
|
// Mark as read so we don't process it again (or add a label, but reading it is safest to prevent loops)
|
|
await markAsRead(email.id);
|
|
}
|
|
} catch (err) {
|
|
console.error('Error processing emails:', err.message);
|
|
}
|
|
}
|
|
|
|
// Start Application
|
|
try {
|
|
loadAuth();
|
|
console.log('Gmail Agent started successfully.');
|
|
console.log(`Cron job scheduled to check every 15 minutes.`);
|
|
|
|
// Check immediately on startup
|
|
processEmails();
|
|
|
|
// Schedule every 15 minutes
|
|
cron.schedule('*/15 * * * *', () => {
|
|
processEmails();
|
|
});
|
|
} catch (e) {
|
|
console.error('Failed to start:', e.message);
|
|
}
|