Skip to content

Instantly share code, notes, and snippets.

@jordanlambrecht
Last active May 11, 2024 03:38
Show Gist options
  • Save jordanlambrecht/ec6ad36b5a48271fcbb4bb811512e477 to your computer and use it in GitHub Desktop.
Save jordanlambrecht/ec6ad36b5a48271fcbb4bb811512e477 to your computer and use it in GitHub Desktop.
Auto Delete / Archive Emails in Gmail
function autoDelete() {
console.log('Started autoDelete run.');
var delayDays = 2;
var maxDate = new Date();
maxDate.setDate(maxDate.getDate()-delayDays);
var label = GmailApp.getUserLabelByName("delete me");
var threads = label.getThreads();
if(threads.length > 0){
console.log('Found ' + threads.length + ' emails marked for deletion.');
var counter = 0;
try {
for(var i =0; i < threads.length; i++){
if (threads[i].getLastMessageDate()<maxDate){
Logger.log('deleted email: ' + threads[i].getFirstMessageSubject());
threads[i].markUnimportant();
threads[i].markRead();
threads[i].moveToTrash();
counter++;
}
}
console.log('Successfully moved ' + counter + 'emails to the trash.');
}
catch(e){
console.error('Could Not Start Run: ' + e);
}
}
else{
console.log('Found ' + threads.length + 'emails marked for deletion. Exiting.');
}
}
function autoArchive() {
console.log('Started autoArchive run.');
var delayDays = 2;
var maxDate = new Date();
maxDate.setDate(maxDate.getDate()-delayDays);
var label = GmailApp.getUserLabelByName("archive me");
var threads = label.getThreads();
if(threads.length > 0){
console.log('Found ' + threads.length + ' emails marked for archival.');
var counter = 0;
try {
for(var i =0; i < threads.length; i++){
if (threads[i].getLastMessageDate()<maxDate){
Logger.log('archived email: ' + threads[i].getFirstMessageSubject());
threads[i].markRead();
threads[i].moveToArchive();
threads[i].markUnimportant();
counter++;
}
}
console.log('Successfully archived ' + counter + 'emails.');
}
catch(e){
console.error('Could Not Start Run: ' + e);
}
}
else{
console.log('Found ' + threads.length + 'emails marked for archival. Exiting.');
}
}
@MichaelTD83
Copy link

MichaelTD83 commented Mar 11, 2022

Thanks for this code, I've been trying to automate stuff in Gmail by age of email for a while and this is the only thing that works and wasn't too difficult. For some reason I couldn't comment on the Pixel Bakery page. Anyways, I had an addons that I figured I would post here to help anyone else that finds this

I wanted to set mine up to cover multiple subfolders within a folder and this is what I came up with that works. I usually use R so this took me a little bit to figure out, let me know what you think.

function autoDelete() {
  console.log('Started autoDelete run.');
  var delayDays = 60;
  var maxDate = new Date();
  maxDate.setDate(maxDate.getDate() - delayDays );

////////////////////////////////////////////////////////////////////////////////////////////////
  var label = GmailApp.getUserLabelByName("Folder/Subfolder1");
  var labelName = label.getName()
  var threads = label.getThreads();
    if(threads.length > 0){
      console.log('Found ' + threads.length + ' emails in ' + labelName);
    var counter = 0;
    try {
      for(var i =0; i < threads.length; i++){
        if (threads[i].getLastMessageDate()<maxDate){ 
          Logger.log('deleted email: ' + threads[i].getFirstMessageSubject());
          threads[i].markUnimportant();
          threads[i].markRead();
          threads[i].moveToTrash();
          counter++;
        }
      }
       console.log('Successfully moved ' + counter + ' emails to the trash.');
    }
    catch(e){
      console.error('Could Not Start Run: ' + e);
    }
  }
  else{
    console.log('Found ' + threads.length + ' emails marked for deletion in ' + labelName + '. Exiting.');
  } 

////////////////////////////////////////////////////////////////////////////////////////////////
  var label = GmailApp.getUserLabelByName("Folder/Subfolder2");
  var labelName = label.getName()
  var threads = label.getThreads();
    if(threads.length > 0){
      console.log('Found ' + threads.length + ' emails in ' + labelName);
    var counter = 0;
    try {
      for(var i =0; i < threads.length; i++){
        if (threads[i].getLastMessageDate()<maxDate){ 
          Logger.log('deleted email: ' + threads[i].getFirstMessageSubject());
          threads[i].markUnimportant();
          threads[i].markRead();
          threads[i].moveToTrash();
          counter++;
        }
      }
       console.log('Successfully moved ' + counter + ' emails to the trash.');
    }
    catch(e){
      console.error('Could Not Start Run: ' + e);
    }
  }
  else{
    console.log('Found ' + threads.length + ' emails marked for deletion in ' + labelName + '. Exiting.');
  }
/// etc......
}

@cmyden
Copy link

cmyden commented Dec 21, 2023

Thanks so much for this! I haven't even verified yet that it's working, but I have it all set up, and just waiting to see.

The only hiccup I ran into in the current instructions was that I needed to attempt to Run it first so that it would change 'MyFunction' to 'AutoArchive' and 'AutoDelete' in the dropdown menus. At first when I tried selecting the trigger it was just named 'MyFunction'.

@JCKE
Copy link

JCKE commented Jan 24, 2024

The only hiccup I ran into in the current instructions was that I needed to attempt to Run it first so that it would change 'MyFunction' to 'AutoArchive' and 'AutoDelete' in the dropdown menus. At first when I tried selecting the trigger it was just named 'MyFunction'.

For me I forgot to press save the script and had this problem, for anyone else don't forget to save!

@charmarkk
Copy link

This is great! Seems like it'll only truly shine once an inbox is somewhat under control, though - I'm getting errors that it's timing out after about 5 minutes.

@samsawyer
Copy link

samsawyer commented Feb 20, 2024

Thanks for a great first pass at this.

One issue with timing out might be that it's going to process every thread under a label every time — for deleted threads, that'll resolve after 30 days when they get permanently deleted out of trash, but for archived threads, the label will stay on them forever and they'll keep getting "rearchived".

Here's some refactored code that handles a couple of things:

  1. It'll process any sets of labels of the form Automate/DeleteAfterN or Automate/ArchiveAfterN, where N is the number of days after which to take action. So you can have multiple timing options without having to continually extend and modify the code.
  2. It'll remove the label on which it's acting, so in the archival case it won't have to keep re-processing the same threads over and over.

Ideas for extending this in the future:

  • add some batch processing so it will only attempt to deal with 500 messages per day or something until the inbox is under control
  • use GmailApp.search instead of getting all messages under a label, to apply the date filtering to the search instead of iterating through each message
  • switch to a paged query if there are still too many messages to process as search results
function gmailLabelAutoActions() {
  GmailApp.getUserLabels()
    .filter(label => label.getName().startsWith('Automate/'))
    .forEach(takeAction);
}

function takeAction(label) {
  const match = label.getName().match(/^Automate\/(Delete|Archive)After([0-9]+)$/);
  let count = 0;
  if (match) {
    let maxDate = new Date();
    maxDate.setDate(maxDate.getDate()-match[2]);
    label.getThreads()
      .filter(thread => thread.getLastMessageDate() < maxDate)
      .forEach(function (thread) {
        try {
          if (match[1] === 'Delete') {
            thread.moveToTrash();
          } else if (match[1] === 'Archive') {
            thread.moveToArchive();
          }
          count++;
          // If action has worked, then mark as unimportant and read
          thread.markUnimportant();
          thread.markRead();
          // Also remove label to prevent a thread from being reprocessed on next run
          thread.removeLabel(label);
        } catch (e) {
          Logger.log(`Could not process thread ${thread.getFirstMessageSubject()}: ${e}`);
        }

        if (count > 0) { 
          Logger.log(`For label ${match[1]}After${match[2]}: ${match[1]}d ${count} threads.`);
        }
      });
  }
}

@samsawyer
Copy link

And just saw in the forks that someone has already done a much more complete version of this:
https://gist.github.com/zoidy/38773c6f128d4dfb94e77df99af7c446

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