Skip to main content
Glama
complete-inbox-zero.js12 kB
#!/usr/bin/env node import { FastMailClient } from './src/fastmail-client.js'; import dotenv from 'dotenv'; dotenv.config({ path: '../.env' }); async function completeInboxZero() { console.log('📥 COMPLETING INBOX ZERO - CATEGORIZING ALL REMAINING EMAILS\n'); const client = new FastMailClient( process.env.FASTMAIL_API_TOKEN, 'clark@clarkeverson.com', 'clark@clarkeverson.com', 'clarkeverson.com', 'https://api.fastmail.com/jmap/session' ); try { await client.authenticate(); const mailboxes = await client.getMailboxes(); // Get target mailboxes for categorization const targets = { 'Information/Newsletters': mailboxes.find(mb => { const parent = mailboxes.find(p => p.name === 'Information'); return parent && mb.parentId === parent.id && mb.name === 'Newsletters'; }), 'Information/News': mailboxes.find(mb => { const parent = mailboxes.find(p => p.name === 'Information'); return parent && mb.parentId === parent.id && mb.name === 'News'; }), 'Financial/Receipts': mailboxes.find(mb => { const parent = mailboxes.find(p => p.name === 'Financial'); return parent && mb.parentId === parent.id && mb.name === 'Receipts'; }), 'Financial/Banking': mailboxes.find(mb => { const parent = mailboxes.find(p => p.name === 'Financial'); return parent && mb.parentId === parent.id && mb.name === 'Banking'; }), 'Commerce/Orders': mailboxes.find(mb => { const parent = mailboxes.find(p => p.name === 'Commerce'); return parent && mb.parentId === parent.id && mb.name === 'Orders'; }), 'Professional/GitHub': mailboxes.find(mb => { const parent = mailboxes.find(p => p.name === 'Professional'); return parent && mb.parentId === parent.id && mb.name === 'GitHub'; }), 'Professional/Security': mailboxes.find(mb => { const parent = mailboxes.find(p => p.name === 'Professional'); return parent && mb.parentId === parent.id && mb.name === 'Security'; }), 'Personal/Health': mailboxes.find(mb => { const parent = mailboxes.find(p => p.name === 'Personal'); return parent && mb.parentId === parent.id && mb.name === 'Health'; }), 'Personal/Travel': mailboxes.find(mb => { const parent = mailboxes.find(p => p.name === 'Personal'); return parent && mb.parentId === parent.id && mb.name === 'Travel'; }) }; console.log('📧 TARGET MAILBOXES FOUND:'); Object.entries(targets).forEach(([name, mb]) => { console.log(`${mb ? '✅' : '❌'} ${name} ${mb ? `(ID: ${mb.id})` : '(NOT FOUND)'}`); }); // Get inbox const inboxMailbox = mailboxes.find(mb => mb.name === 'Inbox'); if (!inboxMailbox) { console.log('❌ Inbox not found'); return; } console.log(`\n📥 PROCESSING ${inboxMailbox.totalEmails} INBOX EMAILS FOR COMPLETE ORGANIZATION:`); console.log('='.repeat(80)); let categorized = 0; let actionable = 0; let position = 0; const batchSize = 25; while (position < inboxMailbox.totalEmails) { try { const emailResult = await client.getEmails(inboxMailbox.id, batchSize, position); if (!emailResult?.emails?.length) break; console.log(`\n📧 Processing batch ${Math.floor(position/batchSize) + 1}: emails ${position + 1}-${position + emailResult.emails.length}`); for (const email of emailResult.emails) { try { const sender = email.from?.[0]?.email?.toLowerCase() || ''; const subject = email.subject?.toLowerCase() || ''; const preview = email.preview?.toLowerCase() || ''; let targetPath = null; let reason = ''; // Aggressive categorization patterns // 1. FINANCIAL - Banking, receipts, payments if (sender.includes('paypal') || sender.includes('stripe') || sender.includes('apple') || sender.includes('amazon') && (subject.includes('order') || subject.includes('payment')) || sender.includes('chase') || sender.includes('fidelity') || sender.includes('schwab') || sender.includes('monarchmoney') || sender.includes('flourish') || subject.includes('receipt') || subject.includes('payment') || subject.includes('invoice') || subject.includes('transaction') || subject.includes('statement') || subject.includes('balance') || subject.includes('transfer') || subject.includes('bill')) { targetPath = 'Financial/Receipts'; reason = 'Financial transaction/receipt'; } // Banking specific else if (sender.includes('bank') || subject.includes('account') || subject.includes('deposit') || subject.includes('withdrawal') || sender.includes('banking')) { targetPath = 'Financial/Banking'; reason = 'Banking notification'; } // 2. NEWSLETTERS/INFORMATION - No-reply, updates, newsletters else if (sender.includes('noreply') || sender.includes('no-reply') || sender.includes('donotreply') || sender.includes('newsletter') || sender.includes('notification') || sender.includes('update') || sender.includes('digest') || sender.includes('alert') || sender.includes('team@') || sender.includes('support@') || sender.includes('info@') || subject.includes('newsletter') || subject.includes('update') || subject.includes('digest') || subject.includes('weekly') || subject.includes('daily') || subject.includes('summary') || sender.includes('sololearn') || sender.includes('datacamp') || sender.includes('thekrazycouponlady') || sender.includes('strava') || sender.includes('lyft') || sender.includes('uber')) { targetPath = 'Information/Newsletters'; reason = 'Newsletter/automated notification'; } // 3. NEWS - News sources else if (sender.includes('wsj') || sender.includes('nytimes') || sender.includes('news') || subject.includes('breaking') || subject.includes('headlines')) { targetPath = 'Information/News'; reason = 'News content'; } // 4. COMMERCE - Shopping, orders, shipping else if (sender.includes('amazon') || sender.includes('etsy') || sender.includes('samsung') || sender.includes('backerkit') || sender.includes('satechi') || sender.includes('netflix') || sender.includes('spotify') || sender.includes('subscription') || subject.includes('order') || subject.includes('shipped') || subject.includes('delivered') || subject.includes('tracking') || subject.includes('shipment') || subject.includes('delivery') || subject.includes('subscription') || subject.includes('renewal')) { targetPath = 'Commerce/Orders'; reason = 'Shopping/subscription order'; } // 5. PROFESSIONAL - GitHub, work, security else if (sender.includes('github') || sender.includes('slack') || sender.includes('google') || sender.includes('microsoft') || sender.includes('claude') || sender.includes('anthropic') || subject.includes('github') || subject.includes('build') || subject.includes('deploy') || subject.includes('failed') || subject.includes('security') || subject.includes('login') || subject.includes('password') || subject.includes('authentication')) { if (subject.includes('security') || subject.includes('login') || subject.includes('password')) { targetPath = 'Professional/Security'; reason = 'Security notification'; } else { targetPath = 'Professional/GitHub'; reason = 'Professional/technical content'; } } // 6. PERSONAL - Health, travel, personal services else if (sender.includes('kaiser') || sender.includes('airbnb') || sender.includes('southwest') || sender.includes('health') || sender.includes('medical') || sender.includes('doctor') || sender.includes('flight') || sender.includes('hotel') || sender.includes('booking') || subject.includes('appointment') || subject.includes('health') || subject.includes('medical') || subject.includes('flight') || subject.includes('hotel') || subject.includes('booking')) { if (subject.includes('health') || subject.includes('medical') || subject.includes('doctor') || subject.includes('appointment')) { targetPath = 'Personal/Health'; reason = 'Health/medical'; } else { targetPath = 'Personal/Travel'; reason = 'Travel/booking'; } } // Apply categorization if (targetPath && targets[targetPath]) { await client.moveEmailsToMailbox([email.id], targets[targetPath].id); categorized++; console.log(` ✅ [${categorized}] ${email.subject?.substring(0, 40)}... → ${targetPath} (${reason})`); } else { actionable++; console.log(` 📥 [ACTIONABLE] ${email.subject?.substring(0, 50)}... (FROM: ${sender})`); } } catch (error) { console.log(` ❌ Error processing email: ${error.message}`); } } position += emailResult.emails.length; // Small delay between batches await new Promise(resolve => setTimeout(resolve, 2000)); } catch (error) { console.log(`❌ Batch error: ${error.message}`); break; } } // Also handle remaining old folders console.log('\n📧 FINISHING OLD FOLDER CLEANUP:'); console.log('='.repeat(50)); const newsFolder = mailboxes.find(mb => mb.name === 'News' && !mb.parentId); if (newsFolder && newsFolder.totalEmails > 0 && targets['Information/News']) { console.log(`📰 Moving ${newsFolder.totalEmails} emails from old News folder...`); try { const emailResult = await client.getEmails(newsFolder.id, 100, 0); if (emailResult?.emails?.length) { const emailIds = emailResult.emails.map(e => e.id); await client.moveEmailsToMailbox(emailIds, targets['Information/News'].id); console.log(`✅ Moved ${emailIds.length} news emails to Information/News`); categorized += emailIds.length; } } catch (error) { console.log(`❌ Error moving news: ${error.message}`); } } console.log('\n🎉 INBOX ZERO ACHIEVED!'); console.log('='.repeat(50)); console.log(`📧 Total emails categorized: ${categorized}`); console.log(`📥 Emails remaining in inbox (actionable): ${actionable}`); console.log(`🎯 Inbox zero goal: ${actionable <= 10 ? '✅ ACHIEVED' : '⚠️ NEEDS MORE WORK'}`); if (actionable <= 10) { console.log('\n✅ PERFECT! Inbox now contains only actionable emails!'); console.log('🎯 Target achieved: ~1 email per day in inbox'); } else { console.log(`\n⚠️ ${actionable} emails still in inbox - may need manual review`); } } catch (error) { console.log('❌ Error:', error.message); } } completeInboxZero().catch(console.error);

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/gr3enarr0w/fastmail-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server