This program, in the form of a configuration script and a main script, allows for complicated Gmail search queries to be used as filters. It also lets you do more advanced stuff you can't do with ordinary filters, like label based on whether an email contains a specific kind of attachment.
- Go to script.google.com.
- Go to File > New > Script File, and type
Main
as the title for the new script. This will be for the main script. - Delete any pre-filled text in the script file, and copy
main.gs
from this gist to that file. - Go to File > New > Script File again, and type
Config
as the title for the new script. This will be for configuration. - Delete any pre-filled text in the script file, and enter the desired configuration.
- Click Run > Run function > __install to install.
- If a dialog comes up to approve permissions, disregard any warning of Google considering it unsafe (as it's obviously not signed, approved, or anything) and accept them. This script doesn't send anything to any server, nor does it attempt to do anything malicious behind your back - the source code isn't even obfuscated here.
If there's a bug, new feature, or something you want, you can update it pretty easily, too.
- Go to the script file you created when installing.
- Click the "Config" script and update it as necessary.
- Click the "Main" script.
- Copy the updated script into it.
- Click Run > Run function > __install to update.
- If a dialog comes up to approve permissions, accept them. This script doesn't send anything to any server, nor does it attempt to do anything malicious behind your back.
If you, for any reason, wish to uninstall the script and revert back to what you used previously, here's how you do it.
- Go to the script file you created when installing.
- Click Run > Run function > __uninstall to uninstall.
- Delete your script from Google Apps Script if you wish.
Configuration is a simple __setup = {...}
block with two options queries
and notify
. If you're non-technical, it's not as complicated as you might think. A basic configuration file might look like this:
__setup = {
// The queries to run filters over
queries: [
["some query", function (thread) { /* ... */ }],
["another query", function (thread) { /* ... */ }],
],
// Info for the weekly summary. Set to `false` instead to just do it in the
// background.
notify: {
// The email the script runs under - must be a valid email.
email: "[email protected]",
// The subject for the weekly summary
subject: "Filter Summary",
// The email body as plain text - `%c` acts as a placeholder for the
// filtered count
body: "%c emails processed in the past 7 days.",
},
};
queries
is required to know which emails to filter, and the thread
in each of those is a GmailThread
for you to do things with. notify
provides various options for the weekly summary, emailed Monday every week at 6 in the morning.
You can omit notify
or just set it to true
, in which it just defaults to this:
__setup = {
// ...
notify: {
email: "The email of the account you used to create the script",
subject: "Weekly Filter Totals",
body: "Number of emails successfully processed this past week: %c",
},
};
You can also omit individual parts of notify
(like email
) to default to one of these. Note that if you specify notify
without email
and it can't detect the email for you, it will return an error and let you know it has to be specified explicitly.
Here's an example configuration based on my own one.
// Helper method. One caveat to be aware of is that you should not start
// variables with two underscores - those are reserved for internal use.
function trash(thread) {
thread.moveToTrash();
}
__setup = {
queries: [
["in:all -in:trash category:social older_than:15d -is:starred", trash],
["in:all -in:trash category:updates older_than:15d -is:starred -label:Important-Label", trash],
["in:all -in:trash category:promotions older_than:15d -is:starred -label:Company-News", trash],
["in:all -in:trash category:forums older_than:90d -is:starred", trash],
],
};
Here's another configuration, one that just adds a Company
label to company emails with a more informative custom body:
__setup = {
queries: [
["from:[email protected]", function (thread) {
thread.addLabel(GmailApp.getUserLabelByName("Company"));
}],
],
notify: {
body: "%c threads from Company labeled",
},
};
Note: the "Company" label must have already been created for this to work.
If you want to iterate emails, not just threads, use GmailThread#getMessages
and iterate it like this:
function iterate(thread) {
thread.getMessages().forEach(function (email) {
// Do something with `email`
});
}
This is useful for seeing if a thread has a PDF attachment and acting accordingly, something you can't do using Gmail's built-in filters:
__setup = {
queries: [
["in:all -in:trash -label:has-pdf", function (thread) {
var hasPDF = false
thread.getMessages().forEach(function (email) {
email.getAttachments().forEach(function (attachment) {
if (attachment.getContentType() === "application/pdf") hasPDF = true;
});
});
if (hasPDF) {
thread.addLabel(GmailApp.getUserLabelByName("Has PDF"));
}
}],
],
};
Note: the "Has PDF" label must have already been created for this to work.
Of course, if you're a programmer, you probably already noticed this could've been done a lot more efficiently:
// If you know JS well enough
var label = GmailApp.getUserLabelByName("Has PDF");
__setup = {
queries: [
["in:all -in:trash -label:has-pdf", function (thread) {
if (thread.getMessages().some(function (email) {
return email.getAttachments().some(function (attachment) {
return attachment.getContentType() === "application/pdf";
});
})) {
thread.addLabel(label);
}
}],
],
};
// Or maybe if you're used to Java or C++:
var label = GmailApp.getUserLabelByName("Has PDF");
__setup = {
queries: [
["in:all -in:trash -label:has-pdf", function (thread) {
var messages = thread.getMessages();
for (var i = 0; i < messages.length; i++) {
var attachments = messages[i].getAttachments();
for (var j = 0; j < attachments.length; j++) {
if (attachments[j].getContentType() === "application/pdf") {
thread.addLabel(label);
return;
}
}
}
}],
],
};
Blue Oak Model License 1.0.0. The full text is in main.gs
as well as in LICENSE.md in this gist.
@Michele2795
thread
below)thread.moveToArchive()
to archive a thread.Your
__setup
might look something akin to this: