Using Google Apps Script to Send Tasks to OmniFocus
Tags:
Bottom Line: How to use a Google Apps Script to automate making OmniFocus tasks from emails.
Gmail filters are pretty powerful, and you can easily set one up to forward to your OmniFocus Mail Drop address. In conjunction with IFTTT (iOS App), you can create even more powerful actions to send customized tasks using Gmail triggers. This kind of basic task generation automation is a great place to start, and it’s reasonably easy even for tech novices.
However, if you want to get into some really powerful email-to-task automation, you ought to look into Google Apps Script. It’s basically JavaScript that Google runs for you, with some really cool integration with things like Gmail and Google Sheets. It basically lets you write scripts that access and are triggered by things like a particular Gmail message or spreadsheet cell and gives you pretty fine control over what you do with the message.
Consider this situation, for example. Let’s say you get emails about bank transactions, and you want to know about them ASAP. Great — set up a Gmail filter to send a notification via Pushover or use IFTTT. However, let’s say instead that you only want notifications for transactions over a certain dollar amount. That’s where this type of script may be your best bet, using something like a regex to find the amount of the transaction, parseFloat()
, and a comparison. For example,
var emailContent = 'You had a transaction for $24.50';
// Set up the regex (parenthesis indicate a "group")
var transAmountRegex = /\$(\d+\.\d{2})/;
// Get first matching group
var transAmountString = transAmountRegex.exec(emailContent)[1];
// Turn the string into a number
var transAmount = parseFloat(transAmountString);
if ( transAmount >= 25.00 ) {
console.log("That was a big transaction.");
sendNotification();
}
else {
console.log("Honey badger don't care.");
}
Don’t worry too much if you don’t know JavaScript. I don’t really know any either (compare my posts tagged javascript
to those tagged python
or AppleScript
for example). But if you have even a little background in scripting and access to Google, you can probably figure it out enough to get a working script using my template. In my script below, I’ve included a template function called bankEmail
to provide a basic example.
Initial setup
- Create a new Gmail label called `scripts` (or choose your own label name).
- If desired, create a Gmail filter to automatically label certain emails.
- Go to https://script.google.com and choose `Blank Project`.
- Copy and paste my script below (replacing anything already in the script), save, and choose a new name.
- Change the variables at the top of the script to your label and your Mail Drop address (or whatever address you’re sending the message to).
Make your test in the processMessage() function
Because I plan on using this same script and label to process a number of different kinds of emails, the next step is to use an if
statement to see what pattern a given email fits into. Depending on the complexity of your rule, this may be redundant (as a Gmail filter would serve a similar purpose), but it allows for much more flexibility and more powerful matching with things like regular expressions, as I mentioned above.
You’ll need to make a new test for each “type” of email you want to match, and a new function for how to deal with that type of email.
- Go to the
function processMessage(message)
part of the script. - You can leave my example for
bankEmail
(near line 61) there as a template, if you want. - Change the section
else if ( to == '[email protected]'
section to fit the pattern of emails you want to match.- You may want to brush up on JavaScript regular expressions.
- The
from
field isn’t just the address — Click the small down arrow to the right of a Gmail message,Show original
, and look for what it has underFrom:
. - You’ll probably want to use
/yourRegex/i.test(emailField)
in this portion, as it returnstrue
orfalse
, which is what you need for matching.
- In the curly brackets
{ }
afterif
, put in the name of the new function that you want to be called when that particular email matches, and send it the messageId as an argument, i.e. replaceyourNewFunction(messageId);
with your new values.
Set up your function
Next we’ll make your custom function that is called when your email pattern matches.
- Make a new function following the pattern demonstrated in
function yourNewFunction(messageId) {
. - In the curly braces
{ }
… do what you want! Get attributes like the message content, the subject, etc. using the GmailMessage class. - Use this data you’ve extracted from the email to make a concise, informative task.
- Consider using the JavaScript
Date()
function to add a “Processed on: “ type line. - You’ll use MailApp.sendEmail to send the task to OmniFocus via Mail Drop.
- Don’t forget your subject will be the task name, and your email body will be the task note.
- Consider including using something like
GmailMessage.getPlainBody()
in your note to give your task appropriate context.
- Add a
message.getThread().removeLabel(label);
to the bottom of your function to make sure the label gets removed before the next run.
Test and debug
Next, give it a shot!
- In Gmail, label a message that should pass your “test” and trigger your custom function.
- A few seconds after labeling it, go back to the script, save, then go to
Run
->processLabel
. - During the first run, you should expect an
Authorization required
box asking to be able to read your emails and send emails. Take a look through your code and make sure you’re okay with this.
If by some miracle your script worked on the first try, you’re a lot better at this than me.
If you got an error, hopefully it gave you a helpful error message. One thing that might help you figure it out is to click the line number of the script at some point before the error is being picked up, which tells it to debug there. Then use the button that looks like a bug, which will run the script up to that point, then pause and show you details about all your variables.
If it didn’t work but you didn’t get an error, that might mean your message didn’t properly get picked up in the processMessage
function. In this case, set the myDebug
global variable up top to true
, run the script again, then go to View
-> Logs
; you should get a printout of most of the values you would use to match a message, such as From
, To
, Subject
, etc. Make sure the values you’re trying to match are exactly right.
If all else fails, Google is your friend. You can try me on Twitter, but I probably won’t be a whole lot of help, since I didn’t know how to do any of this before this morning.
Set up your trigger
Once it seems to be working manually, the hard part is through! Now you just need it to run automatically. Unfortunately, I don’t think there’s a way to trigger your script immediately when a new email arrives or gets labeled, so I think time-based is the best option available.
Resources
-> Current project's triggers
My current settings:
- Run: processLabel
- Time-driven
- Minutes timer
- Every 5 minutes
- Notifications: Once daily
Well, that’s about it. As one last reminder, when you set up your Gmail filter, you can have a single filter run the same rule for a bunch of different email patterns by using curly braces { }
to indicate or
, e.g. {this, that} == this OR that
and parenthesis ( )
to indicate and
, e.g. (this, that) == this AND that
.
For example, if I want my custom Google Apps script to run on emails from [email protected]
that have cars
in the subject and on emails from [email protected]
that has the phrase let's go riding
somewhere in them, a Gmail filter with the following will match both of them:
Has the words: { (from:[email protected], subject:cars), (from:[email protected], "let's go riding") }
Then just have that filter do something like this for its action:
- Apply the label:
scripts
- Never mark as spam
- Skip the Inbox (optional)
- Mark as read (optional)
Okay, that’s it! I’m excited to have finally broken into the world of Google Apps Script. Let me know if you have suggestions for improvement or questions in the comments section below!