Using Google Apps Scripts to apply labels for catch-all email addresses
Yes, it has been a really long time since I wrote my last blog entry. I would say I’ve been busy.. but luckily, I’ve made space again to publish some technical writings / blog posts and get back into the spirit of the web as an open space to create content.
Let me get back to tonight’s topic: E-mail automation using Google Apps Scripting
So I have for the last year and a half maintained by own personal domain for e-mail which keeps my personal development projects and consulting seperate from any work email and my @gmail.com
. Having an entire Google Workspace to myself just gives a lot of benefit: it allows me to tie up a lot of loose ends and authentication between the realms you are expected to juggle.
But the coolest thing with having your domain, is that you have unlimited mailboxes - and I wanted to be able to partition my emails with some labelling magic.
I called the script, APPLYDEMLABELS.gs
The Google Workspace MX delegation is the same as this blog, rod.codes
. When I configured the mail I only set up one mailbox called me@rod.codes
and then setup a catch-all to basically catch all emails sent to <anything>@rod.codes
and deliver it to me@rod.codes
.
Google has the following steps to configure the catch-all. https://support.google.com/a/answer/2685650?product_name=UnuFlow&visit_id=637861669862774545-4194007274&rd=1&src=supportwidget0#zippy=%2Cset-up-a-catch-all-address and I’ve provided images of my configuration and the advice at the bottom of this document (if you are interested).
This is how it works
On Initialization, a global dictionary (LABEL_DICT
) of all labels are retrieved for this mailbox.
First, I created a Gmail Rule as follows that whenever an email not destinated for either me@rod.codes
or my older gmail.com
email address, it adds the unicode label “🆕” (indicatorLabelString
).
Note the negative operator in front of the email address
Second, the AppScript basically filters by that Label and then either attaches a label (if it exists in the global LABEL_DICT
) or creates it and then attaches the label to the mail.
The indicatorLabelString
🆕 is then removed and the enumeration continues until
there are no more emails tagged with 🆕.
The App Script Code
// Configure this trigger to run often
// (*how* often depends on the desired response time *and* how willing you are to risk hitting Google Apps Script’s rate limits;
// 10 minutes is good enough for me generally)
var LABEL_DICT = {}
var EMAIL_WORKSPACE_DOMAIN = "" // do not include the @ sign, so for XYZ@joebloggs.com, put joebloggs.com
function getUserLabels() {
GmailApp.getUserLabels().map((value) => {
LABEL_DICT[value.getName().toString()] = value;
return { "label": value, "name": value.getName() };
});
}
function getLabelByName(name) {
return LABEL_DICT.hasOwnProperty(name) ? LABEL_DICT[name] : undefined;
};
function createLabelIfNotExists(name) {
var newLabel = null;
var label = getLabelByName(name);
if (!label) {
newLabel = GmailApp.createLabel(name.toString());
LABEL_DICT[name.toString()] = newLabel;
label = newLabel;
}
return label;
};
function getLabelByEmailType(emailAddress) {
if (!emailAddress) {
return;
}
let emailLower = emailAddress.toString().toLowerCase();
if (emailLower.toString().endsWith(EMAIL_WORKSPACE_DOMAIN)) {
let futureLabel = emailLower.toString().split("@");
return createLabelIfNotExists("📬 " + futureLabel[0]);
};
};
function triggerScriptForEmail() {
getUserLabels();
let indicatorLabelString = "🆕";
var LABEL_TO_REMOVE = LABEL_DICT[indicatorLabelString];
var GET_THREADS_AT_TIME = 5;
var threads = new Array();
while (Array.isArray(threads)) {
Logger.log(`Calling getUserLabelByName = ${indicatorLabelString}`);
threads = GmailApp.getUserLabelByName(indicatorLabelString).getThreads(0, GET_THREADS_AT_TIME);
Logger.log(`Thread length == ${threads.length}`);
if (threads.length == 0) {
Logger.log('All gone');
return;
}
threads.forEach((thread) => {
var messages = GmailApp.getMessagesForThread(thread);
Logger.log(`${messages}`);
var emailAddresses = messages.map((msg) => (msg.getTo ? [msg.getTo()] : [])).map((value) => value.toString().match(/([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)/gi));
[...new Set(emailAddresses)].forEach((recipient) => {
Logger.log(`Checking email address ${recipient}`);
var labelToAttach = getLabelByEmailType(recipient);
if (labelToAttach) {
Logger.log(`Attaching label ${labelToAttach.getName()}`);
labelToAttach.addToThread(thread);
}
});
Logger.log(`Removing indicator label`);
LABEL_TO_REMOVE.removeFromThread(thread);
return null;
})
}
}
function doGet(e) {
triggerScriptForEmail();
return `OK`;
}
How to configure the Google Workspace Catch-all
At the time of writing this blog post, the current steps were defined.
Interesting, I ended up with something different to that under https://admin.google.com/ac/apps/gmail/defaultrouting.
This is how my configuration looks:
Regardless, I believe the outcome is the same is similar. If not, let me know…