Skip to content

Instantly share code, notes, and snippets.

@benbjurstrom
Last active September 10, 2024 10:16
Show Gist options
  • Save benbjurstrom/00cdfdb24e39c59c124e812d5effa39a to your computer and use it in GitHub Desktop.
Save benbjurstrom/00cdfdb24e39c59c124e812d5effa39a to your computer and use it in GitHub Desktop.
PurgeOldEmails

PurgeOldEmails

A Google Apps Script script to automatically delete unarchived mail after 7 days that hasn't been starred or marked as important

Quick Start

  1. Backup your emails using Google Takeout.
  2. Load this script into a new Google Apps Script project.
  3. Execute the setPurgeTrigger() function to set a trigger that will call the purge() function every day.

A detailed blog post with more information can be found at https://benbjurstrom.com/purge-email

Acknowledgements

Thanks to this gist by jamesramsay for getting me started in the right direction.

/*
|--------------------------------------------------------------------------
| PurgeOldEmails
|--------------------------------------------------------------------------
| https://gist.github.com/benbjurstrom/00cdfdb24e39c59c124e812d5effa39a
|
*/
// Purge messages automatically after how many days?
var DELETE_AFTER_DAYS = 7
// Maximum number of message threads to process per run.
var PAGE_SIZE = 150
/**
* Create a trigger that executes the purge function every day.
* Execute this function to install the script.
*/
function setPurgeTrigger() {
ScriptApp
.newTrigger('purge')
.timeBased()
.everyDays(1)
.create()
}
/**
* Create a trigger that executes the purgeMore function two minutes from now
*/
function setPurgeMoreTrigger(){
ScriptApp.newTrigger('purgeMore')
.timeBased()
.at(new Date((new Date()).getTime() + 1000 * 60 * 2))
.create()
}
/**
* Deletes all triggers that call the purgeMore function.
*/
function removePurgeMoreTriggers(){
var triggers = ScriptApp.getProjectTriggers()
for (var i = 0; i < triggers.length; i++) {
var trigger = triggers[i]
if(trigger.getHandlerFunction() === 'purgeMore'){
ScriptApp.deleteTrigger(trigger)
}
}
}
/**
* Deletes all of the project's triggers
* Execute this function to unintstall the script.
*/
function removeAllTriggers() {
var triggers = ScriptApp.getProjectTriggers()
for (var i = 0; i < triggers.length; i++) {
ScriptApp.deleteTrigger(triggers[i])
}
}
/**
* Wrapper for the purge function
*/
function purgeMore() {
purge()
}
/**
* Deletes any emails from the inbox that are more then 7 days old
* and not starred or marked as important.
*/
function purge() {
removePurgeMoreTriggers()
var search = 'in:inbox -in:starred -in:important older_than:' + DELETE_AFTER_DAYS + 'd'
var threads = GmailApp.search(search, 0, PAGE_SIZE)
if (threads.length === PAGE_SIZE) {
console.log('PAGE_SIZE exceeded. Setting a trigger to call the purgeMore function in 2 minutes.')
setPurgeMoreTrigger()
}
console.log('Processing ' + threads.length + ' threads...')
var cutoff = new Date()
cutoff.setDate(cutoff.getDate() - DELETE_AFTER_DAYS)
// For each thread matching our search
for (var i = 0; i < threads.length; i++) {
var thread = threads[i]
// Only delete if the newest message in the thread is older then DELETE_AFTER_DAYS
if (thread.getLastMessageDate() < cutoff) {
thread.moveToTrash();
}
}
}
@JacobTr97
Copy link

Hi, My code is running only twice and doesn't trigger again after 2 mins. May I know what can be done?

Thank you.

chrome_4NjUtKWpfI

Same problem... Anyone have a solution for this? Would be greatly appreciated.

@jtlarson
Copy link

Ben--thanks for this!

@blackstatic42

Is their somehow to make it skip by labeled messages as well? Any insight would be greatly appreciated.

I think every message in gmail is 'labeled' with at least a folder label, so omitting them would be effectively omitting ALL messages. You could probably skip specific labels like this

Add your label list near the top like this:
// Purge messages with these labels: var LABELS_TO_SAVE = [ "mailinglist", "Updates", "Social", "Promotions" ];

Then change line 76 (starts with 'var search') to this:
var search = "(-label:" + LABELS_TO_SAVE.join(" OR -label:") + ") in:inbox -in:starred -in:important older_than:" + DELETE_AFTER_DAYS + 'd'

You can also make this a list of labels to delete--just rename the var to "LABELS_TO_DELETE" in both places for clarity, and remove the "-" before both 'label:' entries.

@vedanta28
Copy link

@jtlarson Is the script in the video working as intended? or we have to make some changes?

@Heady23
Copy link

Heady23 commented May 6, 2024

@jtlarson Is the script in the video working as intended? or we have to make some changes?

I am a newbie this so just created a time based Trigger to run the "purge" function. It ran but processed 0 threads.....still trying to figure out how to run it in the background. I also deployed it but have no idea what that does.

@jtlarson
Copy link

jtlarson commented May 6, 2024

@jtlarson Is the script in the video working as intended? or we have to make some changes?

@vedanta28 I assume it should work as posted, but here's my modified version that only deletes messages with specific labels I don't want to retain:

Edited: updated the description of what my modified search does, and removed the commented-out original search line

/*
|--------------------------------------------------------------------------
| PurgeOldEmails 
|--------------------------------------------------------------------------
| https://gist.github.com/benbjurstrom/00cdfdb24e39c59c124e812d5effa39a
|
*/

// Purge messages automatically after how many days?
var DELETE_AFTER_DAYS = 180

// Purge messages with these labels:
var LABELS_TO_DELETE = [
  "maillist",
  "Updates",
  "Social",
  "Promotions"
];

// Maximum number of message threads to process per run. 
var PAGE_SIZE = 150

/**
 * Create a trigger that executes the purge function every day.
 * Execute this function to install the script.
 */
function setPurgeTrigger() {
  ScriptApp
    .newTrigger('purge')
    .timeBased()
    .everyDays(1)
    .create()
}

/**
 * Create a trigger that executes the purgeMore function two minutes from now
 */
function setPurgeMoreTrigger(){
  ScriptApp.newTrigger('purgeMore')
  .timeBased()
  .at(new Date((new Date()).getTime() + 1000 * 60 * 2))
  .create()
}

/**
 * Deletes all triggers that call the purgeMore function.
 */
function removePurgeMoreTriggers(){
  var triggers = ScriptApp.getProjectTriggers()
  for (var i = 0; i < triggers.length; i++) {
    var trigger = triggers[i]
    if(trigger.getHandlerFunction() === 'purgeMore'){
      ScriptApp.deleteTrigger(trigger)
    }
  }
}

/**
 * Deletes all of the project's triggers
 * Execute this function to unintstall the script.
 */
function removeAllTriggers() {
  var triggers = ScriptApp.getProjectTriggers()
  for (var i = 0; i < triggers.length; i++) {
    ScriptApp.deleteTrigger(triggers[i])
  }
}

/**
 * Wrapper for the purge function
 */
function purgeMore() {
  purge()
}


/**
 * Deletes emails from the "LABELS_TO_DELETE" variable that are older than the DELETE_AFTER_DAYS variable setting
 */
function purge() {
  removePurgeMoreTriggers()
  
  var search = "(label:" + LABELS_TO_DELETE.join(" OR label:") + ") older_than:" + DELETE_AFTER_DAYS + 'd'

  var threads = GmailApp.search(search, 0, PAGE_SIZE)
  
  if (threads.length === PAGE_SIZE) {
    console.log('PAGE_SIZE exceeded. Setting a trigger to call the purgeMore function in 2 minutes.')
    setPurgeMoreTrigger()
  }
  
  console.log('Processing ' + threads.length + ' threads...')
  
  var cutoff = new Date()
  cutoff.setDate(cutoff.getDate() - DELETE_AFTER_DAYS)
  
  // For each thread matching our search
  for (var i = 0; i < threads.length; i++) {
    var thread = threads[i]
    
    // Only delete if the newest message in the thread is older then DELETE_AFTER_DAYS
    if (thread.getLastMessageDate() < cutoff) {
      thread.moveToTrash();
    }
  }
}

@jtlarson
Copy link

jtlarson commented May 6, 2024

I am a newbie this so just created a time based Trigger to run the "purge" function. It ran but processed 0 threads.....still trying to figure out how to run it in the background. I also deployed it but have no idea what that does.

@Heady23 "processed 0 threads" just means the conditions in your var 'search' didn't fit any messages in your account. The original script example only fits messages in the inbox old than 'DELETE_AFTER_DAYS' (7 days) by default, so perhaps you have your messages in alternative folder labels? You can consult my modified version in the reply before this for another search example.

Make sure the 'purge' function is working when run manually in the editor first, then the trigger should work as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment