|
/** |
|
* Gmail Auto-Delete Script |
|
* Automatically deletes emails based on label naming conventions |
|
* Label format: "delete.in.{number}.{unit}" (e.g., "delete.in.7.days") |
|
* Supported units: days/day, hours/hour, minutes/minute |
|
*/ |
|
|
|
// Configuration constants |
|
const CONFIG = { |
|
SOFT_DELETE_MODE: false, // Set to true to test without actually deleting emails |
|
BATCH_SIZE: 50, // Number of threads to process per batch |
|
DELETE_LABEL_PREFIX: 'delete.in' |
|
}; |
|
|
|
// Time conversion constants (in milliseconds) |
|
const TIME_UNITS = { |
|
MINUTE: 60 * 1000, |
|
HOUR: 60 * 60 * 1000, |
|
DAY: 24 * 60 * 60 * 1000 |
|
}; |
|
|
|
/** |
|
* Main execution function |
|
* Processes all labels with deletion rules and applies them |
|
*/ |
|
function executeEmailDeletion() { |
|
try { |
|
const deletionLabels = getLabelsWithDeletionRules(); |
|
|
|
if (deletionLabels.length === 0) { |
|
Logger.log('No deletion labels found'); |
|
return; |
|
} |
|
|
|
Logger.log(`Found ${deletionLabels.length} deletion labels to process`); |
|
|
|
for (const labelName of deletionLabels) { |
|
processLabelForDeletion(labelName); |
|
} |
|
|
|
Logger.log('Email deletion process completed'); |
|
} catch (error) { |
|
Logger.log(`Error in main execution: ${error.toString()}`); |
|
} |
|
} |
|
|
|
/** |
|
* Gets all user labels that contain deletion rules |
|
* @returns {string[]} Array of label names with deletion rules |
|
*/ |
|
function getLabelsWithDeletionRules() { |
|
const userLabels = GmailApp.getUserLabels(); |
|
const deletionLabels = []; |
|
|
|
for (const label of userLabels) { |
|
const labelName = label.getName(); |
|
if (labelName.includes(CONFIG.DELETE_LABEL_PREFIX)) { |
|
deletionLabels.push(labelName); |
|
} |
|
} |
|
|
|
return deletionLabels; |
|
} |
|
|
|
/** |
|
* Processes a specific label for email deletion |
|
* @param {string} labelName - The name of the label to process |
|
*/ |
|
function processLabelForDeletion(labelName) { |
|
try { |
|
const deletionRule = parseDeletionRule(labelName); |
|
if (!deletionRule) { |
|
Logger.log(`Invalid deletion rule format for label: ${labelName}`); |
|
return; |
|
} |
|
|
|
const label = GmailApp.getUserLabelByName(labelName); |
|
if (!label) { |
|
Logger.log(`Label not found: ${labelName}`); |
|
return; |
|
} |
|
|
|
Logger.log(`Processing label: ${labelName} (delete after ${deletionRule.amount} ${deletionRule.unit})`); |
|
|
|
let processedCount = 0; |
|
let page = 0; |
|
let hasMoreThreads = true; |
|
|
|
while (hasMoreThreads) { |
|
const startIndex = page * CONFIG.BATCH_SIZE; |
|
const threads = label.getThreads(startIndex, CONFIG.BATCH_SIZE); |
|
|
|
for (const thread of threads) { |
|
if (shouldDeleteThread(thread, deletionRule)) { |
|
deleteThread(thread); |
|
processedCount++; |
|
} |
|
} |
|
|
|
page++; |
|
hasMoreThreads = threads.length >= CONFIG.BATCH_SIZE; |
|
} |
|
|
|
Logger.log(`Processed ${processedCount} threads for label: ${labelName}`); |
|
|
|
} catch (error) { |
|
Logger.log(`Error processing label ${labelName}: ${error.toString()}`); |
|
} |
|
} |
|
|
|
/** |
|
* Parses deletion rule from label name |
|
* @param {string} labelName - Label name containing deletion rule |
|
* @returns {Object|null} Deletion rule object or null if invalid |
|
*/ |
|
function parseDeletionRule(labelName) { |
|
try { |
|
const ruleStart = labelName.indexOf(CONFIG.DELETE_LABEL_PREFIX); |
|
if (ruleStart === -1) return null; |
|
|
|
const ruleSubstring = labelName.substring(ruleStart); |
|
const ruleParts = ruleSubstring.replace(`${CONFIG.DELETE_LABEL_PREFIX}.`, '').split('.'); |
|
|
|
if (ruleParts.length < 2) return null; |
|
|
|
const amount = parseInt(ruleParts[0]); |
|
const unit = ruleParts[1].toLowerCase(); |
|
|
|
if (isNaN(amount) || amount <= 0) return null; |
|
|
|
// Normalize unit names |
|
const normalizedUnit = normalizeTimeUnit(unit); |
|
if (!normalizedUnit) return null; |
|
|
|
return { |
|
amount: amount, |
|
unit: normalizedUnit, |
|
milliseconds: calculateMilliseconds(amount, normalizedUnit) |
|
}; |
|
|
|
} catch (error) { |
|
Logger.log(`Error parsing deletion rule for ${labelName}: ${error.toString()}`); |
|
return null; |
|
} |
|
} |
|
|
|
/** |
|
* Normalizes time unit names to standard format |
|
* @param {string} unit - Time unit from label |
|
* @returns {string|null} Normalized unit name or null if invalid |
|
*/ |
|
function normalizeTimeUnit(unit) { |
|
switch (unit) { |
|
case 'day': |
|
case 'days': |
|
return 'days'; |
|
case 'hour': |
|
case 'hours': |
|
return 'hours'; |
|
case 'minute': |
|
case 'minutes': |
|
return 'minutes'; |
|
default: |
|
return null; |
|
} |
|
} |
|
|
|
/** |
|
* Calculates milliseconds for given amount and unit |
|
* @param {number} amount - Number of time units |
|
* @param {string} unit - Time unit (days, hours, minutes) |
|
* @returns {number} Time in milliseconds |
|
*/ |
|
function calculateMilliseconds(amount, unit) { |
|
switch (unit) { |
|
case 'days': |
|
return amount * TIME_UNITS.DAY; |
|
case 'hours': |
|
return amount * TIME_UNITS.HOUR; |
|
case 'minutes': |
|
return amount * TIME_UNITS.MINUTE; |
|
default: |
|
return 0; |
|
} |
|
} |
|
|
|
/** |
|
* Determines if a thread should be deleted based on deletion rule |
|
* @param {GmailThread} thread - Gmail thread to check |
|
* @param {Object} deletionRule - Deletion rule object |
|
* @returns {boolean} True if thread should be deleted |
|
*/ |
|
function shouldDeleteThread(thread, deletionRule) { |
|
try { |
|
const threadDate = thread.getLastMessageDate(); |
|
const currentDate = new Date(); |
|
const timeDifference = currentDate.getTime() - threadDate.getTime(); |
|
|
|
return timeDifference > deletionRule.milliseconds; |
|
|
|
} catch (error) { |
|
Logger.log(`Error checking thread for deletion: ${error.toString()}`); |
|
return false; |
|
} |
|
} |
|
|
|
/** |
|
* Deletes or logs deletion of email thread |
|
* @param {GmailThread} thread - Gmail thread to delete |
|
*/ |
|
function deleteThread(thread) { |
|
try { |
|
const subject = thread.getFirstMessageSubject(); |
|
const threadDate = thread.getLastMessageDate(); |
|
const daysSinceReceived = Math.floor((new Date() - threadDate) / TIME_UNITS.DAY); |
|
|
|
Logger.log(`Deleting email: "${subject}" (${daysSinceReceived} days old)`); |
|
|
|
if (!CONFIG.SOFT_DELETE_MODE) { |
|
thread.moveToTrash(); |
|
Logger.log('Email moved to trash'); |
|
} else { |
|
Logger.log('SOFT DELETE MODE: Email would be deleted'); |
|
} |
|
|
|
} catch (error) { |
|
Logger.log(`Error deleting thread: ${error.toString()}`); |
|
} |
|
} |
|
|
|
/** |
|
* Test function to list all deletion labels |
|
* Useful for debugging and verification |
|
*/ |
|
function testListDeletionLabels() { |
|
Logger.log('=== Testing Deletion Labels ==='); |
|
|
|
try { |
|
const deletionLabels = getLabelsWithDeletionRules(); |
|
|
|
if (deletionLabels.length === 0) { |
|
Logger.log('No deletion labels found'); |
|
return; |
|
} |
|
|
|
Logger.log(`Found ${deletionLabels.length} deletion labels:`); |
|
|
|
for (const labelName of deletionLabels) { |
|
const rule = parseDeletionRule(labelName); |
|
if (rule) { |
|
Logger.log(`✓ ${labelName} -> Delete after ${rule.amount} ${rule.unit}`); |
|
} else { |
|
Logger.log(`✗ ${labelName} -> Invalid rule format`); |
|
} |
|
} |
|
|
|
} catch (error) { |
|
Logger.log(`Error in test function: ${error.toString()}`); |
|
} |
|
} |