Hack Your Gmail: A Quick Start For Google App Scripting

For many people, Gmail is synonymous with e-mail. Some people like having cloud access to everything and some people hate having any personal data in the cloud. However you feel about it, one thing that was nice about having desktop software is that you could hack it relatively easily. If you didn’t like how your desktop mail client worked, you had a lot of options: use a different program, write your own, hack the executable of your current program, or in the case of open source just fork it and make any changes you are smart enough to make.

Google provides a lot of features with all of its products, but however you slice it, all the code runs on their servers out of your reach. Sort of. If you know JavaScript, you can use Google Apps Script to add features to many Google products including Gmail. If you’ve used Office scripting, the idea is the same, although obviously the implementation is very different.

With scripting you can make sophisticated filters that would be very hard to do otherwise. For example,  monitor for suspicious messages like those with more than 4 attachments, or that appear to come from a contact between the hours of 2AM and 5AM.

For our example today, I’m going to show you something that is easy but also highly useful.

A Useful Example

How many e-mails do you get telling you about sales? If you are like me, plenty. Do you delete them all? Or can you go back in your mail and tell me what Harbor Freight had on sale in January 2003? I’ve often wished there was a well-known mail header that allowed mails to delete themselves because, honestly, at least half the mail I get is only good to some future date. Invitations, package tracking, sales, notifications about content that is new but on the web.

I started with a mostly manual process. I created a lot of filters that would mark incoming messages that I knew were in this category using a label “keep14”. Then — if I remembered — every two weeks or so I would search for the tag using the “older_than” predicate to find all the old messages with that label. The search string would be:

label:keep14 older_than:14d

Then you can delete the results all in one swoop. Assuming you remember to do it.

Perfect for Automation: Scripting and Triggering in Gmail

Google Script can easily do this. However, there are a few gotchas. The most obvious one is also the easiest one. If you think about it, having a script you have to run every two weeks is no better than doing it manually. Luckily, Google Script provides triggers that can run a script on different events and one of them, unsurprisingly, is time.

So with that detail out of the way, how do we make our keep14 script work?

  1. Go to Google Scripts dashboard and log in
  2. Create a new project (press New Script in the top left and change the untitled name up at the top)
  3. Paste the following:
// Things you might like to change
var LABEL="keep14"; // needs to match the one below in autodelete!
var DELAY=21; // give them 1 week grace period (21 days)

function Install() {
   ScriptApp.newTrigger("autodelete")
      .timeBased().everyDays(1).create();
   GmailApp.createLabel(LABEL);
}

function Uninstall() {
   var triggers = ScriptApp.getScriptTriggers();
   var item;
   for each (item in triggers) {
      ScriptApp.deleteTrigger(item);
   }
}

function autodelete() {
   var threads;
   var item;
   var search = "label:" + LABEL + " older_than:" + DELAY +"d";
   var n=0;
   do {
// Gmail will only do some at a time so let's do only a few
// It is not clear how this works when you delete some of the first 500
      threads=GmailApp.search(search,n,500);
      n=n+500;
      for each (item in threads) {
// Logger.log("Delete %s", item.getFirstMessageSubject());
         item.moveToTrash();
      } // end for each
   } while (threads!=null && threads.length<0);
} // end function

To understand this, you need to know the object model exposed by Google, of course. There’s documentation, although it sometimes leaves something to be desired, like most documentation. For now, skip to the bottom of the code and focus on the autodelete function.

In the Code

Look a the very top of the code. Those first two lines are just there to allow you to easily customize the label and delay time (in days; I decided to give it a week’s grace period).

The first real part of the algorithm inside autodelete is to build the search string just like we’d issue manually. If you knew you would not process a lot of mail, you could make this very simple by calling GmailApp.search to get an object that represents all the matching messages. The problem is, the script will choke if the search returns more than 500 items.

That might seem unrealistic, but if you go through and mark a message you have been getting once a day for years, it could easily be a lot more. To combat that, I do the search in a loop and restrict it to 500 items per pass. The documentation is a bit sparse about how good it is at remembering the query when it doesn’t start at zero. It is possible that deleting some messages in the first set of results will cause messages that started in the second set to be skipped because on the second call, they would be in the first set. I didn’t run tests to tell and the documentation is unclear. However, after a few times, it should get all the messages and once the script is running regularly (and you don’t add more messages from the past), you probably won’t process more than 500 at a time anyway.

You can assume that the results are all old enough to delete or they wouldn’t come back from the search. So a simple for each loop enumerates each message and a moveToTrash call deletes it.

Getting More Complex

There’s not very much to see in that example. Of course, you can do things that are as complicated as you like assuming the data and features you want are exposed through the object model. For example, you might want to skip deleting the e-mail if you’ve replied to it. You can change labels as activity occurs in a thread, or develop more nuanced “nudge” behavior than what Gmail has currently implemented. We’d love to hear what possible uses you can think of in the comments below.

You can also script most of Google’s other major applications, too. And keeping track of what you’ve automate d is fairly straightforward. If you go to the script dashboard, you’ll see a selection on the left side of the screen that says “My Triggers” that will show you what will run and when. The My Executions entry will show you when your scripts ran and what happened. Scripts can time out and there’s some limit on how much CPU time you can consume every day.

Is it as good as writing your own application or modifying their source code? No, of course not. But it is also a lot simpler. Because the script is on their server, the debugging is a little bare, but it does work and it is better a better way if you’ve been banging your head against the keyboard as you perform repetitive tasks to keep on top of that out-of-control inbox.

18 thoughts on “Hack Your Gmail: A Quick Start For Google App Scripting

    1. You can. I have a gmail account as basically a spamtrap. I use it for when I need to provide an email address and have one or two interactions (such as verifying signup to a web site, etc.) with an organization. I use a desktop email application (kmail) and download my gmail via IMAP. I haven’t logged into the gmail website in years.

    1. Back in the old days when I did my own mail I used to create aliases for people who wanted an email address with their name as the user, and a forward rule to me. It allowed you, among other things to see who sold your email address and to whom. The plus thing in google has always been dicey as far as places accepting it.

  1. Maybe I’m weird but I don’t really delete e-mails, ever. Unless it’s pure spam of course. I have e-mails dating back 15+ years. They don’t take up any space, so what does it matter? :)

    1. I’m now paying for Google’s storage. I *think* this is mostly because I have all photos automatically back up to Google Photo, but I also don’t ever delete email, I just archive it.

      A couple of days ago I went to search emails with attachments to see if I could delete enough to not pay for the storage. It’s really weird to go back and see conversations from 2004. Perhaps these things are better if purged every once in a while. But I do like the ability to just search my email for a contact I haven’t been in touch with in a long long time.

    1. Incidentally (gee who would have thought?) with the lack of religion in communities in the United States left-wing extremists have started this god-government-protector-of-the-environment persona they are trying to illustrate. So this guy might very well believe in the global catastrophic armageddon caused by some man-made calamity. Much like the Catholics and the Apocalypse stories in the New Testament.

  2. If it was useful to anyone, I wrote the following to get rid of .. 85653 messages from the “forums” category of Gmail
    ( already sent them a message 3 days ago, with no answer, and after a quick call today, the guy I had on the phone line wasn’t even able to send me links to appscript but just a mail to receive screen captures of the troubles – I told him that in my opinion, the errors I could see in the developper console seemed not that related to the limitation present in Gmail’s ‘delete’ or ‘move to trash’ action, when clicking on the corresponding buttons in the UI – it ‘d seem moving stuff to trash or deleting those has a maximum amount, or that an exception is thrown if a script exceeds some maximum execution time .. otherwise, the non-dev I was talking to was pretty nice if not helpful :) )

    Nb: the following script may be modded to not use ‘triggers’, but it seems something worth trying for further other hacks ;p
    “`javascript/appscript
    function purgeGmailForumThreads_final() {
    console.log(‘purgeGmailForumThreads() helper called ..’);
    try {
    var batchLen = 500;
    // get threads related to forums
    var forumThreads = GmailApp.search(‘category:forums’, 0, batchLen);
    var howManyLeft = forumThreads.length; // check how many we’re left with
    console.log(howManyLeft + ‘ threads left here ..’);
    var batchChunkCntr = 0;
    // delete stuff
    for(var i=0; i seems to work fine otherwise
    //forumThreads[i].deleteThread(); // no errors, so exist ? .. -> seems to throw error ..
    //Gmail.Users.Threads.remove(‘me’, forumThreads[i].getId()); // should do the trick ? ..
    Gmail.Users.Threads.remove(‘@gmail.com’, forumThreads[i].getId()); // R: put UR username ..
    //Gmail.Users.Threads.remove(”, forumThreads[i].getId()); // also other way ? ..
    }
    console.log(‘purgeGmailForumThreads: batch of ‘+batchLen+’ remooooved ;P’); // auto-using ‘StackLogging’ ..
    /* — untested one —
    ScriptApp.newTrigger(“purgeGmail”)
    .timeBased().everyMinutes(10).create();
    */
    if(howManyLeft >0 ){
    console.log(‘still forum threads to be deleted ..’);
    ScriptApp.newTrigger(“purgeGmailForumThreads_final”)
    .timeBased()
    .at(new Date((new Date()).getTime() + 100)) // don’t wait sh** ;p
    .create();
    }

    } catch (e) {
    // some exception was thrown or some error happened ? ..
    console.log(‘some uncool stuff happened ? :’ + e);
    }
    }

    “`

    hoping this ‘ll help someone else too :P
    ps: 79705 threads left to be processed .. automagically hopefully ;)

  3. Is it possible to have a script where you’d upload a CSV file of emails you’ve SENT to, and then have it return a filtered CSV file that now only shows the email addresses that have not replied from that list?

Leave a Reply

Please be kind and respectful to help make the comments section excellent. (Comment Policy)

This site uses Akismet to reduce spam. Learn how your comment data is processed.