#!/usr/bin/env node
import { FastMailClient } from './src/fastmail-client.js';
import dotenv from 'dotenv';
dotenv.config({ path: '../.env' });
async function conversationBasedOrganization() {
console.log('💬 CONVERSATION-BASED ORGANIZATION\n');
console.log('Logic: If you replied → move to appropriate folder');
console.log(' If you haven\'t replied → keep in inbox\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();
const sentMailbox = mailboxes.find(mb => mb.name === 'Sent');
const inboxMailbox = mailboxes.find(mb => mb.name === 'Inbox');
if (!sentMailbox || !inboxMailbox) {
console.log('❌ Could not find Sent or Inbox folders');
return;
}
console.log('📤 Analyzing your sent emails to identify completed conversations...');
// Get ALL your sent emails to build comprehensive conversation map
let allSentEmails = [];
let sentPosition = 0;
while (sentPosition < sentMailbox.totalEmails) {
const sentBatch = await client.getEmails(sentMailbox.id, 100, sentPosition);
if (!sentBatch?.emails?.length) break;
allSentEmails.push(...sentBatch.emails);
sentPosition += sentBatch.emails.length;
console.log(` 📤 Loaded ${allSentEmails.length} sent emails...`);
if (allSentEmails.length >= 500) break; // Limit for performance
}
// Build conversation tracking
const repliedToEmails = new Set();
const repliedToSubjects = new Set();
const repliedToDomains = new Set();
allSentEmails.forEach(sentEmail => {
// Track who you replied to
if (sentEmail.to) {
sentEmail.to.forEach(recipient => {
repliedToEmails.add(recipient.email.toLowerCase());
const domain = recipient.email.split('@')[1]?.toLowerCase();
if (domain) repliedToDomains.add(domain);
});
}
// Track subjects you replied to (clean subject lines)
if (sentEmail.subject) {
const cleanSubject = sentEmail.subject
.replace(/^(re:|fwd?:)\s*/i, '')
.toLowerCase()
.trim();
if (cleanSubject.length > 3) {
repliedToSubjects.add(cleanSubject);
}
}
});
console.log(`📧 You've replied to ${repliedToEmails.size} email addresses`);
console.log(`📝 You've replied to ${repliedToSubjects.size} different subjects`);
console.log(`🌐 You've replied to ${repliedToDomains.size} domains`);
// Get target 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'),
'Commerce/Orders': mailboxes.find(mb => mb.parentId === commerceParent?.id && mb.name === 'Orders'),
'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'),
'Personal': personalParent,
'Spam': mailboxes.find(mb => mb.name === 'Spam')
};
console.log(`\n📥 Processing ${inboxMailbox.totalEmails} inbox emails...`);
console.log('='.repeat(80));
let position = 0;
let movedToFolders = 0;
let keptInInbox = 0;
let spamMoved = 0;
while (position < inboxMailbox.totalEmails) {
const emailResult = await client.getEmails(inboxMailbox.id, 25, position);
if (!emailResult?.emails?.length) break;
console.log(`\n📧 Batch ${Math.floor(position/25) + 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 || '';
const cleanSubject = subject.replace(/^(re:|fwd?:)\s*/i, '').toLowerCase().trim();
const senderDomain = sender.split('@')[1]?.toLowerCase() || '';
// Check if you've replied to this conversation
const hasReplied =
repliedToEmails.has(sender) ||
repliedToSubjects.has(cleanSubject) ||
(senderDomain && repliedToDomains.has(senderDomain) &&
!sender.includes('noreply') && !sender.includes('no-reply'));
// Categorize based on whether you've replied
if (hasReplied) {
// You've replied - move to appropriate folder
let targetPath = null;
// Categorize based on content
if (
// Financial
sender.includes('bank') || sender.includes('paypal') || sender.includes('stripe') ||
sender.includes('chase') || sender.includes('fidelity') || sender.includes('schwab') ||
sender.includes('mortgage') || sender.includes('loan') || sender.includes('credit') ||
subject.toLowerCase().includes('payment') || subject.toLowerCase().includes('receipt') ||
subject.toLowerCase().includes('invoice') || subject.toLowerCase().includes('statement') ||
subject.toLowerCase().includes('mortgage') || subject.toLowerCase().includes('refinance')
) {
targetPath = subject.toLowerCase().includes('statement') || subject.toLowerCase().includes('balance') ?
'Financial/Banking' : 'Financial/Receipts';
}
// Commerce
else if (
sender.includes('amazon') || sender.includes('etsy') || sender.includes('shop') ||
sender.includes('order') || sender.includes('commerce') ||
subject.toLowerCase().includes('order') || subject.toLowerCase().includes('shipped') ||
subject.toLowerCase().includes('delivery') || subject.toLowerCase().includes('subscription')
) {
targetPath = 'Commerce/Orders';
}
// Professional
else if (
sender.includes('github') || sender.includes('slack') || sender.includes('google') ||
sender.includes('microsoft') || sender.includes('work') || sender.includes('job') ||
subject.toLowerCase().includes('github') || subject.toLowerCase().includes('work') ||
subject.toLowerCase().includes('job') || subject.toLowerCase().includes('professional')
) {
targetPath = 'Professional/GitHub';
}
// Health
else if (
sender.includes('health') || sender.includes('medical') || sender.includes('doctor') ||
sender.includes('hospital') || sender.includes('clinic') ||
subject.toLowerCase().includes('health') || subject.toLowerCase().includes('medical') ||
subject.toLowerCase().includes('appointment') || subject.toLowerCase().includes('doctor')
) {
targetPath = 'Personal/Health';
}
// Travel
else if (
sender.includes('travel') || sender.includes('flight') || sender.includes('hotel') ||
sender.includes('booking') || sender.includes('airbnb') ||
subject.toLowerCase().includes('travel') || subject.toLowerCase().includes('flight') ||
subject.toLowerCase().includes('hotel') || subject.toLowerCase().includes('booking')
) {
targetPath = 'Personal/Travel';
}
// Personal (general personal communications)
else if (
// Personal email domains
(sender.includes('@gmail.com') || sender.includes('@yahoo.com') ||
sender.includes('@hotmail.com') || sender.includes('@outlook.com') ||
sender.includes('@icloud.com')) &&
!sender.includes('noreply') &&
!subject.toLowerCase().includes('unsubscribe')
) {
targetPath = 'Personal';
}
// Information/Newsletters (default for automated content you've replied to)
else {
targetPath = 'Information/Newsletters';
}
if (targetPath && targets[targetPath]) {
await client.moveEmailsToMailbox([email.id], targets[targetPath].id);
movedToFolders++;
console.log(` ✅ [REPLIED] ${subject.substring(0, 40)}... → ${targetPath}`);
}
} else {
// You haven't replied - check if it's spam or legitimate
// SPAM - Move obvious spam
if (
subject.toLowerCase().includes('tea trick') ||
subject.toLowerCase().includes('blood sugar') ||
subject.toLowerCase().includes('weight loss') ||
subject.toLowerCase().includes('prostate') ||
subject.toLowerCase().includes('vertigo') ||
subject.toLowerCase().includes('memory loss') ||
subject.toLowerCase().includes('milf') ||
subject.toLowerCase().includes('wild night') ||
sender.includes('@cube.dochltrowapp.com') ||
sender.includes('@petsitedeal.com') ||
sender.includes('@blanco17.com')
) {
if (targets['Spam']) {
await client.moveEmailsToMailbox([email.id], targets['Spam'].id);
spamMoved++;
console.log(` 🗑️ [SPAM] ${subject.substring(0, 40)}... → Spam`);
}
} else {
// Not spam and you haven't replied - keep in inbox
keptInInbox++;
console.log(` 📥 [KEEP] ${subject.substring(0, 40)}... (awaiting response)`);
}
}
} catch (error) {
console.log(` ❌ Error processing: ${error.message}`);
}
}
position += emailResult.emails.length;
await new Promise(resolve => setTimeout(resolve, 1500));
}
console.log('\n🎉 CONVERSATION-BASED ORGANIZATION COMPLETE!');
console.log('='.repeat(60));
console.log(`✅ Completed conversations moved to folders: ${movedToFolders}`);
console.log(`📥 Awaiting response (kept in inbox): ${keptInInbox}`);
console.log(`🗑️ Spam moved: ${spamMoved}`);
console.log(`📧 Total processed: ${movedToFolders + keptInInbox + spamMoved}`);
console.log('\n✅ INBOX NOW CONTAINS ONLY:');
console.log('• Emails you haven\'t responded to yet');
console.log('• Legitimate emails awaiting your action');
console.log('• True "actionable" content');
} catch (error) {
console.log('❌ Error:', error.message);
}
}
conversationBasedOrganization().catch(console.error);