#!/usr/bin/env node
import { FastMailClient } from './src/fastmail-client.js';
import dotenv from 'dotenv';
dotenv.config({ path: '../.env' });
async function finishMigration() {
console.log('π§ FINISHING MIGRATION - ORGANIZING 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();
// Continue with remaining migrations
const remainingMigrations = [
{ from: 'Shopping', to: 'Commerce/Orders', reason: 'Shopping orders' },
{ from: 'Technical', to: 'Professional/GitHub', reason: 'Technical content' },
{ from: 'Transactions', to: 'Financial/Receipts', reason: 'Transaction receipts' },
{ from: 'Unsubscribed', to: 'Archive', reason: 'Archive unsubscribed' },
{ from: 'NeedsAction', to: 'Personal', reason: 'Personal action items' },
{ from: 'News', to: 'Information/News', reason: 'News content' }
];
console.log('π§ CONTINUING REMAINING MIGRATIONS:');
console.log('='.repeat(60));
let totalMigrated = 0;
for (const migration of remainingMigrations) {
try {
const sourceMailbox = mailboxes.find(mb => mb.name === migration.from);
if (!sourceMailbox || sourceMailbox.totalEmails === 0) {
console.log(`β
${migration.from} β ${migration.to} (empty or not found)`);
continue;
}
// Find target
let targetMailbox;
if (migration.to.includes('/')) {
const [parentName, childName] = migration.to.split('/');
const parent = mailboxes.find(mb => mb.name === parentName);
if (parent) {
targetMailbox = mailboxes.find(mb =>
mb.parentId === parent.id && mb.name === childName
);
}
} else {
targetMailbox = mailboxes.find(mb => mb.name === migration.to);
}
if (!targetMailbox) {
console.log(`β Target ${migration.to} not found for ${migration.from}`);
continue;
}
console.log(`\nπ§ ${migration.from} β ${migration.to} (${sourceMailbox.totalEmails} emails)`);
// Get all emails in batches
let allEmailIds = [];
let position = 0;
while (true) {
const emailResult = await client.getEmails(sourceMailbox.id, 100, position);
if (!emailResult?.emails?.length) break;
allEmailIds.push(...emailResult.emails.map(e => e.id));
console.log(` π₯ Loaded ${allEmailIds.length} email IDs...`);
if (emailResult.emails.length < 100) break;
position += 100;
}
// Move in batches
if (allEmailIds.length > 0) {
let moved = 0;
for (let i = 0; i < allEmailIds.length; i += 25) {
const batch = allEmailIds.slice(i, i + 25);
await client.moveEmailsToMailbox(batch, targetMailbox.id);
moved += batch.length;
console.log(` β
Moved ${moved}/${allEmailIds.length}`);
await new Promise(resolve => setTimeout(resolve, 1500));
}
totalMigrated += moved;
}
} catch (error) {
console.log(` β Error: ${error.message}`);
}
}
// Handle Transactions sub-folders that still have emails
console.log('\nπ¦ Handling remaining Transactions sub-folders...');
const transactionsParent = mailboxes.find(mb => mb.name === 'Transactions');
if (transactionsParent) {
const transactionChildren = mailboxes.filter(mb => mb.parentId === transactionsParent.id);
for (const child of transactionChildren) {
if (child.totalEmails > 0) {
console.log(`\nπ§ Transactions/${child.name} (${child.totalEmails} emails)`);
const targetPath = child.name === 'Orders' ? 'Commerce/Orders' : 'Financial/Receipts';
const [parentName, childName] = targetPath.split('/');
const parent = mailboxes.find(mb => mb.name === parentName);
const target = parent ? mailboxes.find(mb =>
mb.parentId === parent.id && mb.name === childName
) : null;
if (target) {
try {
let allEmailIds = [];
let position = 0;
while (true) {
const emailResult = await client.getEmails(child.id, 100, position);
if (!emailResult?.emails?.length) break;
allEmailIds.push(...emailResult.emails.map(e => e.id));
if (emailResult.emails.length < 100) break;
position += 100;
}
if (allEmailIds.length > 0) {
let moved = 0;
for (let i = 0; i < allEmailIds.length; i += 25) {
const batch = allEmailIds.slice(i, i + 25);
await client.moveEmailsToMailbox(batch, target.id);
moved += batch.length;
console.log(` β
Moved ${moved}/${allEmailIds.length} to ${targetPath}`);
await new Promise(resolve => setTimeout(resolve, 1500));
}
totalMigrated += moved;
}
} catch (error) {
console.log(` β Error: ${error.message}`);
}
}
}
}
}
console.log(`\nπ MIGRATION PHASE COMPLETE!`);
console.log(`π Additional emails migrated: ${totalMigrated}`);
// Now categorize inbox emails
console.log('\nπ₯ CATEGORIZING INBOX EMAILS...');
console.log('='.repeat(50));
const inboxMailbox = mailboxes.find(mb => mb.name === 'Inbox');
if (inboxMailbox && inboxMailbox.totalEmails > 0) {
console.log(`π§ Processing ${inboxMailbox.totalEmails} inbox emails...`);
try {
// Get inbox emails in batches
let position = 0;
let categorized = 0;
while (position < inboxMailbox.totalEmails) {
const emailResult = await client.getEmails(inboxMailbox.id, 50, position);
if (!emailResult?.emails?.length) break;
console.log(`π₯ Processing emails ${position + 1}-${position + emailResult.emails.length}...`);
for (const email of emailResult.emails) {
try {
// Simple categorization based on sender/subject
let targetPath = null;
const sender = email.from?.[0]?.email?.toLowerCase() || '';
const subject = email.subject?.toLowerCase() || '';
// Financial patterns
if (sender.includes('bank') || sender.includes('paypal') || sender.includes('stripe') ||
subject.includes('payment') || subject.includes('transaction') || subject.includes('receipt')) {
targetPath = 'Financial/Receipts';
}
// Newsletter patterns
else if (sender.includes('newsletter') || sender.includes('noreply') || sender.includes('no-reply') ||
subject.includes('newsletter') || subject.includes('update') || subject.includes('digest')) {
targetPath = 'Information/Newsletters';
}
// Commerce patterns
else if (sender.includes('amazon') || sender.includes('order') || sender.includes('shipping') ||
subject.includes('order') || subject.includes('shipped') || subject.includes('delivered')) {
targetPath = 'Commerce/Orders';
}
// Technical patterns
else if (sender.includes('github') || sender.includes('security') || sender.includes('technical') ||
subject.includes('github') || subject.includes('build') || subject.includes('deploy')) {
targetPath = 'Professional/GitHub';
}
if (targetPath) {
const [parentName, childName] = targetPath.split('/');
const parent = mailboxes.find(mb => mb.name === parentName);
const target = parent ? mailboxes.find(mb =>
mb.parentId === parent.id && mb.name === childName
) : null;
if (target) {
await client.moveEmailsToMailbox([email.id], target.id);
categorized++;
console.log(` β
${email.subject?.substring(0, 50)}... β ${targetPath}`);
}
}
} catch (error) {
console.log(` β οΈ Categorization error: ${error.message}`);
}
}
position += emailResult.emails.length;
await new Promise(resolve => setTimeout(resolve, 2000));
}
console.log(`β
Categorized ${categorized} inbox emails`);
} catch (error) {
console.log(`β Inbox categorization error: ${error.message}`);
}
}
console.log('\nπ ALL EMAIL ORGANIZATION COMPLETE!');
console.log('Ready for final cleanup and sieve rules!');
} catch (error) {
console.log('β Error:', error.message);
}
}
finishMigration().catch(console.error);