2020-12-19

Gmail and tracker.debian.org: add a label to team emails

Recently the Python Team started the migration from Alioth mailing list to a tracker.debian.org team.

Alioth mailing list, being a real ml, included a List-Id header, which is handled by Gmail natively and which you can use to create a filter based on it.

Tracker.d.o instead uses a custom header, X-Distro-Tracker-Team, and Gmail doesn't allow to create filters on custom headers. But there's a solution: Google Apps Scripts.

1. Apps script and its configuration

To create a new apps script, click "New project" from the scripts dashboard: this will open a code editor, so now cut and paste this code (change the label name to match the one you want to use, and the header too if you're going to use this for another team):

function tagPythonTeamEmails() {
  
  // search for all recent threads (hours is the smallest interval searchable)
  var threads = GmailApp.search("newer_than:1h");
  // this is the label i want to apply to the messages
  var label = GmailApp.getUserLabelByName("Debian_Python_Pkgs");  // <-- EDIT THIS

  for (var i = 0; i < threads.length; i++) {
     
    // get all messages in a given thread
    var messages = threads[i].getMessages();

    // for each message in the thread
    for (var j = 0; j < messages.length; j++) {
      var message = messages[j];
      // needed to have access to the full email: headers + body
      var email = message.getRawContent();
      // this is the header i want to check for: if present, add label and archive
      if (email.indexOf("X-Distro-Tracker-Team: python") > -1) {  // <-- EDIT THIS
        threads[i].addLabel(label);
        threads[i].moveToArchive();
      }
    }
  }
}

(This code is largely inspired by this StackOverflow answer.)

Given we're going to perform changes to the emails, we need to add extra permissions to the script; first we need to toggle the option "View > Show manifest file" which will show the appsscript.json file. Once done, edit it and update the scope of the script to include:

  "oauthScopes": [
    ...
    "https://www.googleapis.com/auth/gmail.readonly",
    "https://www.googleapis.com/auth/gmail.modify"
  ],

These new permission will be granted when we deploy the script, which we can actually do right now: from the editor window click on "Publish > Deploy from manifest..." and then "Install add-on"; it will make the app script available to operate on Gmail by opening the usual Google grants authorization popup.

2. Schedule the app script execution

Unlike filters defined on the web UI, apps scripts are not triggered every time a mail is received (there's a pub-sub mechanism, but it doesn't look like it would work for this), so if we want to execute periodically the new script, we need to create a time-driven trigger.

The simplest way is probably from the script editor (but you can also follow this doc): "View > Current project's triggers" and then "Add trigger" from there. For my specific use-case i set:

  • event source = time-driven
  • type of time based trigger = minutes timer
  • minute interval = 5 minutes
  • Failure notification settings = notify me immediately
it's not great that you have to wait 5 minutes for getting proper labels, and while it's true you can schedule the trigger for every minute, there are further considerations for that, discussed below.

3. Additional information

Be aware of quotas and limitations for Apps scripts; in particular the "Triggers total runtime" set to "90 min / day" is what led me to set the trigger every 510 mins.

Use the scripts dashboard (doc) to monitor the script execution logs, in the "My Executions" section, and specifically the "Duration" column: the script execution time depends exclusively on the number of emails to process at every execution, so fine-tune the trigger time to stay within your quota.

The newer_than search operator doesn't allow a granularity smaller than hours (although that's not even documented), so that means we're going to scan multiple times the same emails.

UPDATE (2020-12-26): I've already had to bump from every 5 to every 10 minutes, as otherwise i'd get Exception: Service invoked too many times for one day: gmail. (remember the quota!)

No comments: