From 3c6e1588e7772ed0e030b362f72191402685cac6 Mon Sep 17 00:00:00 2001 From: poprhythm Date: Fri, 6 Mar 2026 16:00:22 +0000 Subject: [PATCH] Add Gmail label cleanup script for inbox-zero migration --- inbox-zero/gmail-label-cleanup.gs | 235 ++++++++++++++++++++++++++++++ 1 file changed, 235 insertions(+) create mode 100644 inbox-zero/gmail-label-cleanup.gs diff --git a/inbox-zero/gmail-label-cleanup.gs b/inbox-zero/gmail-label-cleanup.gs new file mode 100644 index 0000000..52abfcd --- /dev/null +++ b/inbox-zero/gmail-label-cleanup.gs @@ -0,0 +1,235 @@ +/** + * Gmail Label Cleanup Script + * Run from https://script.google.com + * + * What this does: + * 1. Merges old labels into inbox-zero categories (relabels all threads) + * 2. Archives + relabels cvlp threads to Newsletter + * 3. Merges purchases into Receipt, deletes purchases + * 4. Moves donations top-level, selling under business + * 5. Deletes contract label (threads stay under business) + * 6. Deletes empty/unused labels + * + * Safe to run multiple times - skips labels that don't exist. + */ + +// ─── STEP 1: Merge old labels into inbox-zero categories ───────────────────── + +function mergeLabels() { + var merges = [ + { from: 'promo', to: 'Marketing' }, + { from: 'mailing list', to: 'Newsletter' }, + { from: 'twitter', to: 'Notification' }, + { from: 'facebook', to: 'Notification' }, + { from: 'tech', to: 'Newsletter' }, + { from: 'To Reply', to: 'Awaiting Reply' }, + ]; + + merges.forEach(function(m) { + var fromLabel = GmailApp.getUserLabelByName(m.from); + if (!fromLabel) { + Logger.log('Skipping (not found): ' + m.from); + return; + } + + var toLabel = GmailApp.getUserLabelByName(m.to); + if (!toLabel) { + Logger.log('Creating label: ' + m.to); + toLabel = GmailApp.createLabel(m.to); + } + + Logger.log('Merging ' + m.from + ' -> ' + m.to); + var threads = fromLabel.getThreads(0, 500); + var total = 0; + while (threads.length > 0) { + GmailApp.addLabelToThreads(threads, toLabel); + GmailApp.removeLabelFromThreads(threads, fromLabel); + total += threads.length; + Utilities.sleep(1000); + threads = fromLabel.getThreads(0, 500); + } + Logger.log('Moved ' + total + ' threads: ' + m.from + ' -> ' + m.to); + fromLabel.deleteLabel(); + Logger.log('Deleted label: ' + m.from); + }); +} + +// ─── STEP 2: cvlp → Newsletter + archive ───────────────────────────────────── + +function archiveCvlp() { + var cvlpLabel = GmailApp.getUserLabelByName('cvlp'); + if (!cvlpLabel) { + Logger.log('cvlp label not found, skipping'); + return; + } + + var newsletterLabel = GmailApp.getUserLabelByName('Newsletter'); + if (!newsletterLabel) { + newsletterLabel = GmailApp.createLabel('Newsletter'); + } + + Logger.log('Processing cvlp: relabeling to Newsletter and archiving...'); + var threads = cvlpLabel.getThreads(0, 500); + var total = 0; + while (threads.length > 0) { + GmailApp.addLabelToThreads(threads, newsletterLabel); + GmailApp.removeLabelFromThreads(threads, cvlpLabel); + GmailApp.moveThreadsToArchive(threads); + total += threads.length; + Utilities.sleep(1000); + threads = cvlpLabel.getThreads(0, 500); + } + Logger.log('Archived ' + total + ' cvlp threads'); + cvlpLabel.deleteLabel(); + Logger.log('Deleted label: cvlp'); +} + +// ─── STEP 3: Merge purchases into Receipt ──────────────────────────────────── + +function mergePurchasesIntoReceipt() { + var purchasesLabel = GmailApp.getUserLabelByName('purchases'); + if (!purchasesLabel) { + Logger.log('purchases label not found, skipping'); + return; + } + + var receiptLabel = GmailApp.getUserLabelByName('Receipt'); + if (!receiptLabel) { + receiptLabel = GmailApp.createLabel('Receipt'); + } + + Logger.log('Merging purchases -> Receipt...'); + var threads = purchasesLabel.getThreads(0, 500); + var total = 0; + while (threads.length > 0) { + GmailApp.addLabelToThreads(threads, receiptLabel); + GmailApp.removeLabelFromThreads(threads, purchasesLabel); + total += threads.length; + Utilities.sleep(1000); + threads = purchasesLabel.getThreads(0, 500); + } + Logger.log('Moved ' + total + ' threads: purchases -> Receipt'); + purchasesLabel.deleteLabel(); + Logger.log('Deleted label: purchases'); +} + +// ─── STEP 4: Restructure business nested labels ─────────────────────────────── + +function restructureBusinessLabels() { + // Move donations top-level: rename business/donations -> donations + var bizDonations = GmailApp.getUserLabelByName('business/donations'); + if (bizDonations) { + var donationsLabel = GmailApp.getUserLabelByName('donations'); + if (!donationsLabel) { + donationsLabel = GmailApp.createLabel('donations'); + } + Logger.log('Moving business/donations -> donations (top-level)'); + var threads = bizDonations.getThreads(0, 500); + var total = 0; + while (threads.length > 0) { + GmailApp.addLabelToThreads(threads, donationsLabel); + GmailApp.removeLabelFromThreads(threads, bizDonations); + total += threads.length; + Utilities.sleep(1000); + threads = bizDonations.getThreads(0, 500); + } + Logger.log('Moved ' + total + ' threads to top-level donations'); + bizDonations.deleteLabel(); + } else { + // Try top-level donations label + Logger.log('business/donations not found - checking for top-level donations label'); + } + + // Move selling under business: rename selling -> business/selling + var sellingLabel = GmailApp.getUserLabelByName('selling'); + if (sellingLabel) { + var bizSelling = GmailApp.getUserLabelByName('business/selling'); + if (!bizSelling) { + bizSelling = GmailApp.createLabel('business/selling'); + } + Logger.log('Moving selling -> business/selling'); + var threads = sellingLabel.getThreads(0, 500); + var total = 0; + while (threads.length > 0) { + GmailApp.addLabelToThreads(threads, bizSelling); + GmailApp.removeLabelFromThreads(threads, sellingLabel); + total += threads.length; + Utilities.sleep(1000); + threads = sellingLabel.getThreads(0, 500); + } + Logger.log('Moved ' + total + ' threads to business/selling'); + sellingLabel.deleteLabel(); + } + + // Delete contract label (threads already have business label, just remove contract) + var contractLabel = GmailApp.getUserLabelByName('business/contract'); + if (!contractLabel) { + contractLabel = GmailApp.getUserLabelByName('contract'); + } + if (contractLabel) { + Logger.log('Removing contract label from all threads...'); + var threads = contractLabel.getThreads(0, 500); + var total = 0; + while (threads.length > 0) { + GmailApp.removeLabelFromThreads(threads, contractLabel); + total += threads.length; + Utilities.sleep(1000); + threads = contractLabel.getThreads(0, 500); + } + contractLabel.deleteLabel(); + Logger.log('Deleted contract label (' + total + ' threads unaffected)'); + } +} + +// ─── STEP 5: Delete unused labels ──────────────────────────────────────────── + +function deleteUnusedLabels() { + var toDelete = [ + 'Notes', + '[Mailbox]', + 'Later', + 'To Buy', + 'To Read', + 'To Watch', + 'Cold Email', + 'Oak Ridge Moms', + 'music', + ]; + + toDelete.forEach(function(name) { + var label = GmailApp.getUserLabelByName(name); + if (!label) { + Logger.log('Not found (already gone?): ' + name); + return; + } + var count = label.getThreads(0, 1).length; + if (count > 0) { + Logger.log('WARNING: ' + name + ' still has threads - removing label from them first'); + var threads = label.getThreads(0, 500); + while (threads.length > 0) { + GmailApp.removeLabelFromThreads(threads, label); + Utilities.sleep(500); + threads = label.getThreads(0, 500); + } + } + label.deleteLabel(); + Logger.log('Deleted: ' + name); + }); +} + +// ─── RUN ALL ────────────────────────────────────────────────────────────────── + +function runAll() { + Logger.log('=== Starting Gmail label cleanup ==='); + Logger.log('--- Step 1: Merging labels ---'); + mergeLabels(); + Logger.log('--- Step 2: Archiving cvlp ---'); + archiveCvlp(); + Logger.log('--- Step 3: Merging purchases into Receipt ---'); + mergePurchasesIntoReceipt(); + Logger.log('--- Step 4: Restructuring business labels ---'); + restructureBusinessLabels(); + Logger.log('--- Step 5: Deleting unused labels ---'); + deleteUnusedLabels(); + Logger.log('=== Done! ==='); +}