Add AGENT_READ label functionality and WhatsApp logout endpoint

This commit is contained in:
Antigravity 2026-06-14 10:25:06 -04:00
parent eee7189867
commit fed234e448
5 changed files with 97 additions and 9 deletions

View File

@ -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

View File

@ -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) {

View File

@ -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 = '<h3>✅ Connected</h3><p>Session is active and authenticated.</p>';
} 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';
}
}

View File

@ -32,7 +32,10 @@
<section id="status" class="tab-pane active">
<header>
<h2>Connection Status</h2>
<div id="status-badge" class="badge error">Disconnected</div>
<div style="display:flex; gap:10px; align-items:center;">
<button id="logout-btn" class="btn" style="background-color: #ef4444; color: white; display: none; padding: 0.25rem 0.75rem; font-size: 0.875rem;">Logout & Change WhatsApp</button>
<div id="status-badge" class="badge error">Disconnected</div>
</div>
</header>
<div class="card status-card">

View File

@ -107,6 +107,24 @@ app.get('/api/status', (req, res) => {
});
});
app.post('/api/whatsapp/logout', async (req, res) => {
try {
console.log('[WhatsApp Bot] Logging out...');
if (client.info) {
await client.logout();
}
res.json({ success: true, message: 'Logged out successfully. Pod restarting...' });
// Wait briefly to allow the response to be sent, then exit so Kubernetes restarts the pod cleanly
setTimeout(() => process.exit(0), 1000);
} catch (err) {
console.error('Logout error:', err);
res.status(500).json({ success: false, error: err.message });
// Still exit to force a clean slate
setTimeout(() => process.exit(1), 1000);
}
});
// ─── App Tracking ───────────────────────────────────────────────────────────
const connectedApps = {};
@ -235,6 +253,7 @@ app.post('/api/groups/add-by-link', async (req, res) => {
res.json({ success: true, group: newGroup });
} catch (err) {
console.error('[Add Group Error]:', err);
res.status(500).json({ error: err.message || 'Failed to process invite link' });
}
});