Murrell's Inlet, South Carolina beach. The sky is clear aside from wispy clouds and the ocean is relatively calm.
On Life and Love

Using Google Assistant with Habitica

So here’s what I did instead of finishing part two of my video game post (or doing FPG work) last night: I got a little service in place so that I can use Google Assistant to add tasks to Habitica, my (adorable) task tracking system.

I quite often think of to dos while I’m driving, and haven’t had a good way to get them into Habitica. They’re often quick, contextual thoughts that won’t stick around, like a reminder to check if there’s a Beat Saber map for a song or artist I’m listening to, or a note to check out a game from a podcast I’m listening to.

It’s best, of course, if I don’t have to touch my phone at all to get the task in, which rules out voice-to-text keyboards. Google Assistant and IFTTT are natural components to make that happen, since I’m an Android user.

My avatar and stats in Habitica. I'm currently a level 143 Rogue, and I'm currently wearing some jellyfish clothing and I have an aquatic cactus as a friend.
Me in Habitica! Currently wearing some jellyfish clothing and I have an aquatic cactus as a friend.

The complexity is on the Habitica side. There are third party integrations with the service, and the API is clear enough, but some of the more mature and/or complicated utilities are a little… dusty.

For instance, the existing IFTTT-related service lives on a platform called Glitch, which sometime in 2020 started blocking access from “ping”-like services, which apparently includes IFTTT. So that just doesn’t work as a platform anymore, at least as the original instructions specify.

The code itself, however, is still usable, so I grabbed it, tidied it up a little, integrated 1Password Secrets Automation for credentials (I have some Thoughts about this service), and then tried to figure out where to deploy it.

No-go: DigitalOcean App Platform

My first thought was actually the new serverless function service DigitalOcean is rocking these days, but I had the node.js/Express service running just fine and didn’t want to restructure it for a platform quite that different and new-to-me. This is one of those utilities that I just wanted up and running more than I wanted to spend hours getting it there on a new platform.

The slightly-less-new App Platform also appealed, since this was a super basic tiny service. Why deal with a VM and nginx and all that? (note: one reason to use a VM includes cost)

Getting it spun up was easy: point it at a Github repo and it does most of the work for you. I’ve only used Heroku once (also for a Habitica thing), and DO’s approach seemed on par, but simpler.

Here was the deal breaker, though: my 1Password Connect server is behind a real snug firewall, and DO Apps don’t get fixed IP addresses. In fact, even using ping to get the current IP(s) of the service and allowing them through the firewall didn’t work.

DO has no timeline for supporting the app platform in firewall configuration, through tags or otherwise. No way am I opening up access to my 1Password server to all IPs, thank you. Bye, App Platform.

DigitalOcean VM

So yeah, I just threw that shit on an existing little VM. 😁 Weirdly, I don’t seem to have deployed an Express app to a plain-Jane VM before, so I had to look up how to get it to start on boot and all that. Dreamhost uses Passenger for non-PHP stuff, and I can set that up in my sleep. Other node.js things I’ve written live as AWS Lambda functions, and I throw them up there and pray I never have to revisit them.

This guide on setting up node.js with pm2 for production got me most of what I needed, although I struggled a bit with getting all my environment variables in place correctly for pm2. I did this setup all manually instead of through Ansible, because I didn’t know WTF I was doing enough to try to make it idempotent during the initial setup. I’ll go back and get it in there.

The nginx proxy and certbot stuff is all standard fare, and I set that up though Ansible.


A diagram from IFTTT that reads "If - Say a phrase with a text ingredient + Then - Make a web request".
The IFTTT chain for this Google Assistant/Habitica applet.

IFTTT—If This, Then That—is a well-established platform for connecting two services. I’ve been using it forever, from way back when was a thing in my life.

In case you aren’t familiar with it, the kinds of “applets” (ugh) I have are:

  1. If I start a new Twitch stream, call a webhook that posts an announcement to Mastodon.
  2. If this SmartThings-connected light switch is turned on, turn on all my Wyze lights/plugs.
  3. If I like a video on YouTube, call a webhook that adds the video to my Shaarli bookmark system.

I have a bunch of those, and because I’m a weirdo tech person, most of them use webhooks that call microservices on the “Then That” side.

This little Habitica service slots right in there as the original Glitch version instructed, with Google Assistant’s “Say a phrase with a text ingredient” trigger using phrases like:

  • Add a task to Habitica to $
  • Add to Habitica $
  • Add a to do to Habitica to $

The “Then That” is a webhook that calls my new service with a secret key and the text from Google Assistant.

Upsides and Downsides

I tend to format my to dos (to-dos? todos? I have spelled it every which way in drafting this) a particular way, often prefixing the name with the context, since tags require an extra click to get to and aren’t available at all in the “Add Task” browser extension. Tasks tend to end up named “FPG – Do a thing” or “Skanska – Do a thing”.

The good thing is that saying “Skanska dash do a thing” does produce “skanska – do a thing”.

The bad thing is that it does not capitalize anything. That’s a bit of an eyesore.

I also haven’t tested it with weirder song, band, or game names, and I suspect that I’ll be reviewing some entries with a bit of a side-eye. I don’t make that many tasks, so they’ll jog my memory just fine.

Just a Little Thing

My last technical post was a Herculean undertaking when it came to writing it up—as was the original implementation, to be fair. This is quicker-and-dirtier on both fronts, but maybe even more niche? Oh, well. I write code to solve problems I have.

If anyone wants more detail or code samples, holla in the comments, on Mastodon, or Cohost (I guess? I can’t post there yet).