#!/usr/bin/env node
import { FastMailClient } from '../src/fastmail-client.js';
import dotenv from 'dotenv';
dotenv.config({ path: '../.env' });
async function organizeEverything() {
console.log('๐ฅ ORGANIZING ALL 8,000+ EMAILS - 100% COMPLETION\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 hierarchical folders
const infoParent = mailboxes.find(mb => mb.name === 'Information');
const financialParent = mailboxes.find(mb => mb.name === 'Financial');
const commerceParent = mailboxes.find(mb => mb.name === 'Commerce');
const professionalParent = mailboxes.find(mb => mb.name === 'Professional');
const personalParent = mailboxes.find(mb => mb.name === 'Personal');
const targets = {
'Information/Newsletters': mailboxes.find(mb => mb.parentId === infoParent?.id && mb.name === 'Newsletters'),
'Information/News': mailboxes.find(mb => mb.parentId === infoParent?.id && mb.name === 'News'),
'Financial/Receipts': mailboxes.find(mb => mb.parentId === financialParent?.id && mb.name === 'Receipts'),
'Financial/Banking': mailboxes.find(mb => mb.parentId === financialParent?.id && mb.name === 'Banking'),
'Financial/Bills': mailboxes.find(mb => mb.parentId === financialParent?.id && mb.name === 'Bills'),
'Commerce/Orders': mailboxes.find(mb => mb.parentId === commerceParent?.id && mb.name === 'Orders'),
'Commerce/Subscriptions': mailboxes.find(mb => mb.parentId === commerceParent?.id && mb.name === 'Subscriptions'),
'Professional/GitHub': mailboxes.find(mb => mb.parentId === professionalParent?.id && mb.name === 'GitHub'),
'Professional/Security': mailboxes.find(mb => mb.parentId === professionalParent?.id && mb.name === 'Security'),
'Personal/Health': mailboxes.find(mb => mb.parentId === personalParent?.id && mb.name === 'Health'),
'Personal/Travel': mailboxes.find(mb => mb.parentId === personalParent?.id && mb.name === 'Travel')
};
console.log('๐ฏ TARGET FOLDERS:');
Object.entries(targets).forEach(([name, mb]) => {
console.log(`${mb ? 'โ
' : 'โ'} ${name}`);
});
// Find ALL folders with emails that need organizing
const systemFolders = ['Inbox', 'Sent', 'Drafts', 'Trash', 'Spam'];
const hierarchicalFolders = ['Information', 'Financial', 'Commerce', 'Professional', 'Personal'];
const skipFolders = [...systemFolders, ...hierarchicalFolders, 'Archive'];
const foldersToOrganize = mailboxes.filter(mb =>
!skipFolders.includes(mb.name) &&
mb.totalEmails > 0 &&
!mb.parentId // Only root level folders
);
console.log('\n๐ FOLDERS WITH EMAILS TO ORGANIZE:');
console.log('='.repeat(60));
let totalEmailsToOrganize = 0;
foldersToOrganize.forEach(folder => {
console.log(`๐ ${folder.name}: ${folder.totalEmails} emails`);
totalEmailsToOrganize += folder.totalEmails;
});
console.log(`\n๐ TOTAL EMAILS TO ORGANIZE: ${totalEmailsToOrganize}`);
// Also check Archive folder - it has 5,412 emails that could be organized
const archiveFolder = mailboxes.find(mb => mb.name === 'Archive');
if (archiveFolder && archiveFolder.totalEmails > 0) {
console.log(`\n๐ Archive folder has ${archiveFolder.totalEmails} emails`);
console.log('These could also be organized into hierarchical structure');
foldersToOrganize.push(archiveFolder);
totalEmailsToOrganize += archiveFolder.totalEmails;
}
console.log(`\n๐ฅ ORGANIZING ${totalEmailsToOrganize} EMAILS FROM ALL FOLDERS:`);
console.log('='.repeat(80));
let totalProcessed = 0;
// Process each folder
for (const folder of foldersToOrganize) {
console.log(`\n๐ PROCESSING: ${folder.name} (${folder.totalEmails} emails)`);
console.log('-'.repeat(50));
try {
let position = 0;
let folderProcessed = 0;
while (position < folder.totalEmails) {
const emailResult = await client.getEmails(folder.id, 50, position);
if (!emailResult?.emails?.length) break;
console.log(` ๐ง Batch: 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 senderDomain = sender.split('@')[1]?.toLowerCase() || '';
let targetPath = null;
// FINANCIAL CATEGORIZATION
if (
// Banking
sender.includes('bank') || sender.includes('chase') || sender.includes('fidelity') ||
sender.includes('schwab') || sender.includes('credit') || sender.includes('financial') ||
senderDomain.includes('bank') || senderDomain.includes('schwab') ||
subject.includes('statement') || subject.includes('balance') || subject.includes('account')
) {
targetPath = 'Financial/Banking';
}
// Receipts/Payments
else if (
sender.includes('paypal') || sender.includes('stripe') || sender.includes('payment') ||
sender.includes('billing') || sender.includes('invoice') ||
subject.includes('receipt') || subject.includes('payment') || subject.includes('invoice') ||
subject.includes('transaction') || subject.includes('order') && subject.includes('total')
) {
targetPath = 'Financial/Receipts';
}
// Bills
else if (
subject.includes('bill') || subject.includes('due') || subject.includes('overdue') ||
sender.includes('utility') || sender.includes('electric') || sender.includes('gas')
) {
targetPath = 'Financial/Bills';
}
// COMMERCE CATEGORIZATION
else if (
// Orders/Shopping
sender.includes('amazon') || sender.includes('etsy') || sender.includes('shop') ||
sender.includes('store') || sender.includes('retail') || sender.includes('order') ||
senderDomain.includes('amazon') || senderDomain.includes('etsy') ||
subject.includes('order') || subject.includes('shipped') || subject.includes('delivered') ||
subject.includes('tracking') || subject.includes('shipment')
) {
targetPath = 'Commerce/Orders';
}
// Subscriptions
else if (
sender.includes('subscription') || sender.includes('membership') ||
subject.includes('subscription') || subject.includes('membership') ||
subject.includes('renewal') || subject.includes('expire')
) {
targetPath = 'Commerce/Subscriptions';
}
// PROFESSIONAL CATEGORIZATION
else if (
// GitHub/Development
sender.includes('github') || sender.includes('gitlab') || sender.includes('git') ||
sender.includes('developer') || sender.includes('code') ||
senderDomain.includes('github') ||
subject.includes('github') || subject.includes('git') || subject.includes('code') ||
subject.includes('build') || subject.includes('deploy') || subject.includes('commit')
) {
targetPath = 'Professional/GitHub';
}
// Security
else if (
subject.includes('security') || subject.includes('login') || subject.includes('password') ||
subject.includes('authentication') || subject.includes('verify') || subject.includes('2fa') ||
sender.includes('security') || sender.includes('auth')
) {
targetPath = 'Professional/Security';
}
// PERSONAL CATEGORIZATION
else if (
// Health
sender.includes('health') || sender.includes('medical') || sender.includes('doctor') ||
sender.includes('hospital') || sender.includes('clinic') || sender.includes('pharmacy') ||
senderDomain.includes('health') || senderDomain.includes('medical') ||
subject.includes('health') || subject.includes('medical') || subject.includes('doctor') ||
subject.includes('appointment') || subject.includes('prescription')
) {
targetPath = 'Personal/Health';
}
// Travel
else if (
sender.includes('travel') || sender.includes('flight') || sender.includes('hotel') ||
sender.includes('airline') || sender.includes('booking') || sender.includes('airbnb') ||
senderDomain.includes('travel') || senderDomain.includes('airline') ||
subject.includes('flight') || subject.includes('hotel') || subject.includes('travel') ||
subject.includes('booking') || subject.includes('reservation')
) {
targetPath = 'Personal/Travel';
}
// INFORMATION CATEGORIZATION
else if (
// News
sender.includes('news') || sender.includes('wsj') || sender.includes('nytimes') ||
sender.includes('cnn') || sender.includes('bbc') || sender.includes('reuters') ||
senderDomain.includes('news') || senderDomain.includes('wsj') ||
subject.includes('breaking') || subject.includes('headlines') || subject.includes('news')
) {
targetPath = 'Information/News';
}
// Newsletters (everything else automated)
else if (
sender.includes('noreply') || sender.includes('no-reply') || sender.includes('newsletter') ||
sender.includes('notification') || sender.includes('update') || sender.includes('digest') ||
sender.includes('marketing') || sender.includes('promo') ||
subject.includes('newsletter') || subject.includes('update') || subject.includes('digest') ||
subject.includes('unsubscribe') || subject.includes('marketing')
) {
targetPath = 'Information/Newsletters';
}
// DEFAULT - categorize by domain patterns
else if (senderDomain) {
if (senderDomain.includes('bank') || senderDomain.includes('financial')) {
targetPath = 'Financial/Banking';
} else if (senderDomain.includes('shop') || senderDomain.includes('store')) {
targetPath = 'Commerce/Orders';
} else if (senderDomain.includes('health') || senderDomain.includes('medical')) {
targetPath = 'Personal/Health';
} else {
targetPath = 'Information/Newsletters'; // Default for everything else
}
} else {
targetPath = 'Information/Newsletters'; // Fallback
}
// Move email to target folder
if (targetPath && targets[targetPath]) {
await client.moveEmailsToMailbox([email.id], targets[targetPath].id);
folderProcessed++;
totalProcessed++;
if (folderProcessed % 10 === 0) {
console.log(` โ
${folderProcessed} emails moved to various categories`);
}
}
} catch (error) {
console.log(` โ Error processing email: ${error.message}`);
}
}
position += emailResult.emails.length;
// Progress update
console.log(` ๐ Processed ${Math.min(position, folder.totalEmails)}/${folder.totalEmails} from ${folder.name}`);
// Small delay to prevent overwhelming the API
await new Promise(resolve => setTimeout(resolve, 1000));
// Break if we've processed enough for this session
if (totalProcessed >= 1000) {
console.log(`\nโธ๏ธ Pausing at ${totalProcessed} emails processed - continuing in next batch`);
break;
}
}
console.log(`โ
Completed ${folder.name}: ${folderProcessed} emails organized`);
if (totalProcessed >= 1000) break; // Stop processing more folders for this session
} catch (error) {
console.log(`โ Error processing folder ${folder.name}: ${error.message}`);
}
}
console.log('\n๐ EMAIL ORGANIZATION SESSION COMPLETE!');
console.log('='.repeat(60));
console.log(`๐ง Emails organized this session: ${totalProcessed}`);
console.log(`๐ Progress: Moving toward 100% organization`);
if (totalProcessed >= 1000) {
console.log('\n๐ CONTINUE PROCESSING:');
console.log('Run this script again to continue organizing remaining emails');
} else {
console.log('\nโ
ALL EMAILS HAVE BEEN ORGANIZED!');
}
} catch (error) {
console.log('โ Error:', error.message);
}
}
organizeEverything().catch(console.error);