Last active
January 17, 2024 00:40
-
-
Save macabreb0b/dfa622f8aa03aebc67a3dd5f89da709c to your computer and use it in GitHub Desktop.
email eater - google apps script
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// script forked from https://gist.github.com/jamesramsay/9298cf3f4ac584a3dc05 | |
// to start timed execution: select "Install" from the menu above and hit "run" | |
// to stop timed execution: select "Uninstall" from the menu above and hit "run" | |
// to initialize a batch run ad-hoc and immediately: select "_processNextBatch" from the menu above and hit "run" | |
const LABELS_TO_DELETE = [ | |
// ... list of labels ... | |
// 'test-delete-after-label', | |
'spammy-alerts', | |
// 'delete-after-30d', | |
// TODO - add support for different time bombs | |
// 'delete-after-7d', | |
// 'delete-after-1d', | |
// TODO - add support for archive | |
// 'archive-after-30d', | |
// 'archive-after-7d', | |
]; | |
// Constants for trigger names | |
const DAILY_TRIGGER_NAME = "dailyProcessTrigger"; | |
const DELAYED_BATCH_TRIGGER_NAME = "delayedBatchTrigger"; | |
// timezone probably doesn't really matter if we're only working in days. | |
const TIMEZONE = "America/Los Angeles"; | |
// Batch size and timing configuration | |
const MAX_THREADS_TO_PROCESS_PER_BATCH = 500; // max argument size is 500 | |
const MINUTES_TO_WAIT_BETWEEN_BATCHES = 5; // TODO - why? | |
const DELETE_AFTER_DAYS = 30; | |
function _getPurgeBeforeDate() { | |
const today = new Date(); | |
// TODO - change DELETE_AFTER_DAYS based on the label | |
const age = new Date(today.setDate(today.getDate() - DELETE_AFTER_DAYS)); | |
return Utilities.formatDate(age, TIMEZONE, "yyyy-MM-dd"); | |
} | |
function _processNextBatch() { | |
console.log('START process next batch'); | |
// Clear all batch triggers | |
_clearAllDelayedBatchTriggers(); | |
_processEmails(); | |
console.log('FINISH process batch'); | |
} | |
function _processEmails() { | |
let threadsProcessedCount = 0; | |
const threadsProcessedPerLabel = {}; | |
const purgeBeforeDate = _getPurgeBeforeDate() | |
LABELS_TO_DELETE.forEach(function(label) { | |
const threadBudgetLeftForThisBatch = MAX_THREADS_TO_PROCESS_PER_BATCH - threadsProcessedCount; | |
if (threadBudgetLeftForThisBatch <= 0) { | |
return; // Skip this label if the maximum number of threads to process has been reached | |
} | |
const search = 'label:' + label + ' before:' + purgeBeforeDate; | |
console.log('Searching for threads with label: ' + label); | |
const threads = GmailApp.search(search, 0, threadBudgetLeftForThisBatch); | |
const threadsToDeleteCount = threads.length; | |
console.log('Found ' + threadsToDeleteCount + ' threads to delete for label: ' + label); | |
for (let i = 0; i < threadsToDeleteCount; i++) { | |
// NB: this moves the thread to the trash. so thread will not get *permanently* deleted | |
// until 30 days *after* it gets moved to the trash. | |
threads[i].moveToTrash(); | |
} | |
threadsProcessedCount += threadsToDeleteCount; | |
threadsProcessedPerLabel[label] = (threadsProcessedPerLabel[label] || 0) + threadsToDeleteCount; | |
}); | |
// Log the deleted email count per label | |
console.log('Deleted emails from labels:'); | |
for (let label in threadsProcessedPerLabel) { | |
console.log(' ' + label + ': ' + threadsProcessedPerLabel[label]); | |
} | |
// Schedule another run if we hit the processing limit. | |
// NB: Since we're only loading up to MAX_THREADS_TO_PROCESS_PER_BATCH at a time, we don't | |
// know if there are more threads to process in the next execution. So, if we reach the limit, | |
// there are *probably* more threads to process. It's possible there's no more left but it's not | |
// a big deal to just run it again. | |
if (threadsProcessedCount >= MAX_THREADS_TO_PROCESS_PER_BATCH) { | |
// TODO - when would threadsProcessedCount be above MAX_THREADS_TO_PROCESS? | |
console.log("Reached processing limit, scheduling next batch..."); | |
ScriptApp.newTrigger(DELAYED_BATCH_TRIGGER_NAME) | |
.timeBased() | |
.after(MINUTES_TO_WAIT_BETWEEN_BATCHES * 60 * 1000) | |
.create(); | |
} else { | |
console.log('Did not reach batch limit, no follow-up needed.'); | |
} | |
} | |
function _clearAllDelayedBatchTriggers() { | |
// cancel all delayed jobs | |
const existingTriggers = ScriptApp.getProjectTriggers(); | |
console.log('Checking for triggers other than the daily trigger...'); | |
let foundDelayedBatchTrigger = false; | |
for (let i = 0; i < existingTriggers.length; i++) { | |
const trigger = existingTriggers[i]; | |
if (trigger.getHandlerFunction() === DELAYED_BATCH_TRIGGER_NAME) { | |
ScriptApp.deleteTrigger(trigger); | |
console.log('Deleted delayed batch trigger: ' + trigger.getUniqueId()); | |
foundDelayedBatchTrigger = true; | |
} | |
} | |
if (!foundDelayedBatchTrigger) { | |
console.log('No delayed batch triggers found.') | |
} | |
} | |
function dailyProcessTrigger() { | |
// run this daily to initiate the auto delete process | |
console.log('START run daily deletion process for Gmail threads...'); | |
_processNextBatch(); | |
console.log('FINISH run daily deletion process.'); | |
} | |
function delayedBatchTrigger() { | |
// run this to start the process again if we didn't finish the first time | |
console.log('START process delayed batch'); | |
_processNextBatch(); | |
console.log('FINISH process delayed batch'); | |
} | |
function Install() { | |
console.log('Installing triggers...'); | |
// Clear all existing triggers before setting up new ones | |
Uninstall(); | |
// Set up the new daily trigger | |
ScriptApp.newTrigger(DAILY_TRIGGER_NAME) | |
.timeBased() | |
.everyDays(1) | |
.create(); | |
console.log('Installation complete.'); | |
} | |
function Uninstall() { | |
console.log('Uninstalling all triggers...'); | |
const triggers = ScriptApp.getProjectTriggers(); | |
for (let i = 0; i < triggers.length; i++) { | |
ScriptApp.deleteTrigger(triggers[i]); | |
} | |
console.log('All triggers have been removed.'); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment