#!/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);