Skip to main content
Glama
finish-migration.jsβ€’9.59 kB
#!/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);

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