Skip to content

Instantly share code, notes, and snippets.

@zoidy
Forked from jordanlambrecht/autoDeleteGmail.js
Last active April 13, 2024 14:24
Show Gist options
  • Save zoidy/38773c6f128d4dfb94e77df99af7c446 to your computer and use it in GitHub Desktop.
Save zoidy/38773c6f128d4dfb94e77df99af7c446 to your computer and use it in GitHub Desktop.
Auto Delete / Archive Emails in Gmail
/*
Original Sources:
- https://pixelbakery.com/recipes/gmail-automatically-delete-or-archive-emails
- https://benbjurstrom.com/purge-email
v1.1
https://gist.github.com/zoidy
Auto-archive and delete Gmail messages based on a defined retention period. Functionality:
- Uses Gmail labels to apply retention rules (archive or delete)
- Ability to customize retention period using the label text
- Handles large numbers of emails to process via (side steps the Google Apps Script execution limit by processing in batches).
To set up
1. Create a new Apps Script in your Google Drive and paste this code, then click the Save button
2. Under Services on the left hand side of the Apps Script editor, add Gmail
2. Run the install() function manually once to set up the permissions to gmail (select it from dropdown in the toolbar and click Run)
3. Add labels to Gmail messages to indicate what to archive and delete. The format is
ARCHIVE_LABEL:#[d|w] A single message can have one ARCHIVE_LABEL and one DELETE_LABEL simultaneously.
Example:
Archive:2d = archive the message after 2 days
Delete:12w = deletes the message after 12 weeks (84 days).
*/
ARCHIVE_LABEL = "Archive"; // label for messages that should be archived
DELETE_LABEL = "Delete"; // label for messages that should be deleted
DEFAULT_TIMEPERIOD_DAYS = 2.0; //retention period if none is specified
// Maximum number of message threads to process per run.
// It takes about 1.2 sec per email thread, so a 4 minute delay between processing runs
// when there are more than 175 items in a page is safe.
PAGE_SIZE = 175;
NEXT_RUN_DELAY = 4;
// How often to run the check, in hours. After installing the script, the interval can
// be changed either by running the uninstall function, changing the interval, and
// running the install function again, or by manually editing the existing trigger in the
// AppsScript editor
PURGE_INTERVAL = 8;
// ####################################################################################
/**
* Create a trigger that executes the purge function every day.
* Execute this function to install the script.
*/
function install() {
GmailApp.getUserLabels();
ScriptApp.newTrigger('purge').timeBased().everyHours(PURGE_INTERVAL).create();
}
/**
* Deletes all of the project's triggers
* Execute this function to unintstall the script.
*/
function uninstall() {
var triggers = ScriptApp.getProjectTriggers();
for (var i = 0; i < triggers.length; i++) {
ScriptApp.deleteTrigger(triggers[i]);
}
}
/**
* Create a trigger that executes the purgeMore function NEXT_RUN_DELAY minutes from now
*/
function setPurgeMoreTrigger(){
ScriptApp.newTrigger('purgeMore').timeBased()
.at(new Date((new Date()).getTime() + (1000 * 60 * NEXT_RUN_DELAY))).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);
}
}
}
/**
* Wrapper for the purge function
*/
function purgeMore() {
purge();
}
function getDaysFromLabel(label) {
/*
Expected format of label is LABEL:#T where LABEL is one of ARCHIVE_LABEL or DELETE_LABEL,
# is a number, T is a time unit, currently only 'd' and 'w' is supported. Default is 'd'.
Returns a number representing the number of days.
*/
var timeunit = label.substring(label.length-1);
var convertMult;
var days;
switch(timeunit) {
case "d":
// days
convertMult = 1;
break;
case "w":
// convert weeks to days
convertMult = 7;
break;
default:
return DEFAULT_TIMEPERIOD_DAYS;
}
days = parseFloat(label.substring(label.indexOf(":")+1,label.length-1)) * convertMult;
if(isNaN(days))
days = DEFAULT_TIMEPERIOD_DAYS;
return days;
}
function process(action, labels) {
/*
Performs the action on the labels in the given array, extracting the retention period from the label.
*/
console.log('Started ' + action + ' run.');
var threadActions = [];
switch(action) {
//other actions are ["markRead", "markUnimportant"] but it slows things down
case "autoArchive":
threadActions = ["moveToArchive"];
break;
case "autoDelete":
threadActions = ["moveToTrash"];
break;
default:
Logger.log("Unrecognized action " + action);
return;
}
for (var i = 0; i < labels.length; i++) {
var labelToProcess = labels[i].getName();
var retentiondays = getDaysFromLabel(labelToProcess);
Logger.log("Processing " + labelToProcess + ", with retention " + retentiondays + " days.");
var maxDate = new Date();
maxDate.setDate(maxDate.getDate()-retentiondays);
var totalthreads = GmailApp.getUserLabelByName(labelToProcess).getThreads().length;
var processthreads = GmailApp.search('label:' + labelToProcess.replace(' ', '-') + ' older_than:' + retentiondays + 'd',
0, PAGE_SIZE);
if(processthreads.length > 0){
Logger.log('Found ' + (totalthreads == 500 ? 'at least ' + totalthreads : totalthreads) +
' emails tagged with ' + labelToProcess + '. Processing ' + Math.min(processthreads.length, PAGE_SIZE));
if (processthreads.length === PAGE_SIZE) {
Logger.log('PAGE_SIZE exceeded. Processing will continue in ' + NEXT_RUN_DELAY + ' minutes.');
setPurgeMoreTrigger();
}
var counter = 0;
try {
for(var j=0; j < processthreads.length; j++){
var thread = processthreads[j];
var threadDate = thread.getLastMessageDate();
if (threadDate<maxDate){
for(var k=0; k < threadActions.length; k++){
thread[threadActions[k]]();
}
thread.removeLabel(labels[i]); //so processed items don't get reprocessed on future runs
counter++;
//uncomment for debugging but it slows things down
//Logger.log('processed: ' + thread.getFirstMessageSubject() + ' [date: ' + threadDate + ']');
}
}
Logger.log('Successfully processed ' + counter + ' emails.');
}
catch(e){
Logger.log('Error processing: ' + e);
}
}
else
Logger.log('Found ' + processthreads.length + ' emails marked for processing. Exiting.');
}
}
function purge(){
removePurgeMoreTriggers();
var allLabels = GmailApp.getUserLabels();
var archiveLabels = allLabels.filter((label) => {
return label.getName().startsWith(ARCHIVE_LABEL);
});
var deleteLabels = allLabels.filter((label) => {
return label.getName().startsWith(DELETE_LABEL);
});
process('autoArchive', archiveLabels);
process('autoDelete', deleteLabels);
}
@pauliebp
Copy link

pauliebp commented Mar 17, 2024

Firstly, thank you for this contribution. Works for me and I Iove it!
My code knowlage is unfortunately insuficient to make a useful contributions, but I would like to suggest to add the <markUnread()> comonent to the command.

@zoidy
Copy link
Author

zoidy commented Mar 19, 2024

I'm not quite sure what you're wanting to achieve, but you can mark the messages as unread by adding markUnread to the array that holds the actions to perform on each thread (line 131 or 134). E.g. to mark a message as unread when the autoarchive action executes line 131 will look like threadActions = ["moveToArchive", "markUnread"];

@arhuber88
Copy link

Thank you, I've been dying for a tool like this for years! I set this up a couple days ago and my "archive me" and "delete me" labels are working. However, I also created a label called "archive me:3w" and those emails are not being archived (I see a few emails from last fall that still are sitting in the inbox this morning). I feel like it's got to be user error, but I'm not sure how to validate the custom label time functionality.

@zoidy
Copy link
Author

zoidy commented Apr 12, 2024

Thank you, I've been dying for a tool like this for years! I set this up a couple days ago and my "archive me" and "delete me" labels are working. However, I also created a label called "archive me:3w" and those emails are not being archived (I see a few emails from last fall that still are sitting in the inbox this morning). I feel like it's got to be user error, but I'm not sure how to validate the custom label time functionality.

Perhaps you can try uncommenting line 175 and running the purge function manually and looking at the Apps Script execution logs to see which messages are being processed

@arhuber88
Copy link

Thank you for the quick response! And I see that line. After running the logging, it gives

10:26:09 AM Info Processing archive me:3w, with retention 21 days.
10:26:09 AM Info Found 0 emails marked for processing. Exiting.

I also tried creating another label, "archive me:21d", to test, and applied that to an email from Feb. as well as one email from today. Same results; logging shows that it's processing archive me:21d but that it found 0 emails. I feel like the labels are correct and that the script sees them since it's adding the processing for both the 3w and 21d label. But I'm not sure why it's not finding any emails with those labels applied.

@zoidy
Copy link
Author

zoidy commented Apr 12, 2024

Thank you for the quick response! And I see that line. After running the logging, it gives

10:26:09 AM Info Processing archive me:3w, with retention 21 days.
10:26:09 AM Info Found 0 emails marked for processing. Exiting.

I also tried creating another label, "archive me:21d", to test, and applied that to an email from Feb. as well as one email from today. Same results; logging shows that it's processing archive me:21d but that it found 0 emails. I feel like the labels are correct and that the script sees them since it's adding the processing for both the 3w and 21d label. But I'm not sure why it's not finding any emails with those labels applied.

I'm not sure what the issue could be. Retrieving the threads to process involves using Gmail's built-in search (line 149). You can try manually searching in the Gmail search box using the query "label:archive me:3w older_than:21d" and see what is returned.

Edit: I just realized the issue could be the space in the label. You can try modifying Line 149 to add quotes around the label like so: 'label:"' + labelToProcess + '" older_than:'. If it works, let me know and I can update the script

@arhuber88
Copy link

I wasn't able to get the quotes to work, but you are right about the space. I changed my labels to use an underscore instead of space (archive_me:21d) and that worked! Also, I'm not sure why, but only 21d works, not 3w. Even googles search doesn't seem to like trying to use weeks. I've edited my labels to only use days now and those all work as well.

Thank you, again, for this script and the quick responses! Super excited to see my inbox stay clean now :)

@zoidy
Copy link
Author

zoidy commented Apr 12, 2024

I wasn't able to get the quotes to work, but you are right about the space. I changed my labels to use an underscore instead of space (archive_me:21d) and that worked! Also, I'm not sure why, but only 21d works, not 3w. Even googles search doesn't seem to like trying to use weeks. I've edited my labels to only use days now and those all work as well.

Thank you, again, for this script and the quick responses! Super excited to see my inbox stay clean now :)

Sure thing. Using 'w' is working for me so I'm not sure where the issue is there. As for the space, I'll play around with the code a bit when I get the chance. I feel like it should have a solution!

Edit: bug with using spaces in the label name is fixed with v1.1

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