diff --git a/agents/k8s/gmail-agent-deployment.yaml b/agents/k8s/gmail-agent-deployment.yaml index 8b73f52..f3eb99b 100644 --- a/agents/k8s/gmail-agent-deployment.yaml +++ b/agents/k8s/gmail-agent-deployment.yaml @@ -1,3 +1,15 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: gmail-tokens-pvc + namespace: ai-agents +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Mi +--- apiVersion: apps/v1 kind: Deployment metadata: @@ -72,7 +84,8 @@ spec: - key: credentials.json path: credentials.json - name: tokens-volume - emptyDir: {} + persistentVolumeClaim: + claimName: gmail-tokens-pvc --- apiVersion: v1 diff --git a/gmail-agent/index.js b/gmail-agent/index.js index 227c07f..47f9cc6 100644 --- a/gmail-agent/index.js +++ b/gmail-agent/index.js @@ -58,11 +58,32 @@ function loadAuth() { } } +async function ensureAgentReadLabel(account) { + const gmail = google.gmail({ version: 'v1', auth: account.client }); + const res = await gmail.users.labels.list({ userId: 'me' }); + const labels = res.data.labels || []; + let agentReadLabel = labels.find(l => l.name === 'AGENT_READ'); + + if (!agentReadLabel) { + console.log(`Creating AGENT_READ label for account ${account.name}...`); + const createRes = await gmail.users.labels.create({ + userId: 'me', + requestBody: { + name: 'AGENT_READ', + labelListVisibility: 'labelShow', + messageListVisibility: 'show' + } + }); + agentReadLabel = createRes.data; + } + return agentReadLabel.id; +} + async function getUnreadEmails(account) { const gmail = google.gmail({ version: 'v1', auth: account.client }); const res = await gmail.users.messages.list({ userId: 'me', - q: 'is:unread', + q: 'is:unread -label:AGENT_READ', maxResults: 10 }); @@ -90,18 +111,21 @@ async function getUnreadEmails(account) { return emails; } -async function markAsRead(email) { +async function markAsAgentRead(email, labelId) { const gmail = google.gmail({ version: 'v1', auth: email.client }); await gmail.users.messages.modify({ userId: 'me', id: email.id, - requestBody: { removeLabelIds: ['UNREAD'] } + requestBody: { addLabelIds: [labelId] } }); } 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. + +CRITICAL INSTRUCTION: You MUST strictly IGNORE all promotional emails, newsletters, marketing, discounts, product updates, or any emails of low significance. +For these, you MUST output IMPORTANT: NO. + Important things: Bills due, flight/travel updates, urgent requests from people, security alerts, direct personal messages. Email Subject: ${email.subject} @@ -166,6 +190,10 @@ async function processEmails() { try { for (const account of accounts) { console.log(`Checking account: ${account.name}...`); + if (!account.agentReadLabelId) { + account.agentReadLabelId = await ensureAgentReadLabel(account); + } + const emails = await getUnreadEmails(account); if (emails.length === 0) { console.log(`No new emails for ${account.name}.`); @@ -181,8 +209,8 @@ async function processEmails() { } else { console.log(`Skipped non-important email: ${email.subject}`); } - // Mark as read so we don't process it again - await markAsRead(email); + // Mark as processed using our custom label + await markAsAgentRead(email, account.agentReadLabelId); } } } catch (err) { diff --git a/whatsapp-gateway/public/app.js b/whatsapp-gateway/public/app.js index 0af6a23..33c7a4e 100644 --- a/whatsapp-gateway/public/app.js +++ b/whatsapp-gateway/public/app.js @@ -8,6 +8,25 @@ const groupsTableBody = document.getElementById('groups-table-body'); const appsTableBody = document.getElementById('apps-table-body'); const inviteModal = document.getElementById('invite-modal'); const inviteLinkInput = document.getElementById('invite-link-input'); +const logoutBtn = document.getElementById('logout-btn'); + +if (logoutBtn) { + logoutBtn.addEventListener('click', async () => { + const confirmLogout = confirm('Are you sure you want to log out and change your WhatsApp account? This will disconnect the current session and restart the gateway.'); + if (!confirmLogout) return; + + logoutBtn.disabled = true; + logoutBtn.textContent = 'Logging out...'; + + try { + await fetch('api/whatsapp/logout', { method: 'POST' }); + alert('Logout initiated! The WhatsApp Gateway is restarting. Please wait 5-10 seconds and then hard-refresh the page to scan a new QR code.'); + } catch (err) { + console.error('Logout error:', err); + alert('Failed to log out cleanly, but restarting anyway. Please refresh.'); + } + }); +} // Tab Switching document.querySelectorAll('.nav-btn').forEach(btn => { @@ -39,7 +58,11 @@ function logActivity(msg, type = 'info') { // Socket Events socket.on('status', (data) => { updateStatus(data.ready); - if (data.qr && !data.ready) renderQR(data.qr); + if (data.ready) { + qrContainer.innerHTML = '
Session is active and authenticated.
'; + } else if (data.qr) { + renderQR(data.qr); + } }); socket.on('qr', (qr) => { @@ -66,9 +89,11 @@ function updateStatus(isReady) { if (isReady) { statusBadge.textContent = 'Connected'; statusBadge.className = 'badge success'; + if (logoutBtn) logoutBtn.style.display = 'block'; } else { statusBadge.textContent = 'Disconnected'; statusBadge.className = 'badge error'; + if (logoutBtn) logoutBtn.style.display = 'none'; } } diff --git a/whatsapp-gateway/public/index.html b/whatsapp-gateway/public/index.html index aba21b0..f954720 100644 --- a/whatsapp-gateway/public/index.html +++ b/whatsapp-gateway/public/index.html @@ -32,7 +32,10 @@