Parsing JSON from the command line

You can do it with Python and keep it on a single line and zero dependencies

I have this thing I do every day. I wade through tedious menus and sub menus,  just to get a simple discount coupon for students who want to use Rotato. And every time, I find myself wishing I could just have a robot do the menu work.

Selecting it immediately pastes this:

Hi there! You can now take 50% off with the coupon STUDBC93246
when you purchase Rotato.

That's what we're building here. Activate Alfred, type a few characters, hit enter, and get a full sentence pasted into any text field. And, since Alfred lets you back up your settings to Dropbox, it follows you everywhere. Just one problem. To be truly portable, it can't be a separate app or a script elsewhere on my computer. It has to be a script that can be added to Alfred directly.

Sounds good? Let's build it.

Getting to work

Okay, let's get a bit more concrete. For this to work, here's a few tasks that need to work:

  • Recognize the keyword in Alfred
  • Generate a coupon using Paddle's REST API using POST, and extract the coupon from the JSON
  • Wrap that coupon in a friendly string, much like a mail merge.
  • Paste it

We'll start with the hardest, and most important, part - generating the coupon.

Calling the API

I'm using Paddle as my payments partner. From Paddle's reference, we can see that we can grab a coupon with curl like so:

curl -s -X POST \
-d 'vendor_id=VENDOR' \
-d 'vendor_auth_code=AUTHCODE' \
-d 'coupon_type=checkout' \
-d 'coupon_prefix=STU' \
-d 'num_coupons=1' \
-d 'discount_type=percentage' \
-d 'discount_amount=50' \
-d 'allowed_uses=1' \
-d 'expires=2022-10-01' \
https://vendors.paddle.com/api/2.1/product/create_coupon 

And that command outputs this JSON

{"success":true,"response":{"coupon_codes":["STU7A90886F"]}}

Looking good - but sending this whole text to a customer wouldn't be very friendly. We'll need to read the JSON and grab just the coupon.

Parsing JSON with a one-line shell command

Here's how we'll do it: with a tiny Python script. Sure, we could do regular expressions and grep or sed, but why not use something that really understands all the quirks of JSON?

Here it is, reduced to a single line:

import sys,json;obj=json.load(sys.stdin);print(obj['response']['coupon_codes'][0]);

Notice the sys.stdin bit? That's us getting ready to read the piped input from the curl command.

curl -s -X POST \
-d 'vendor_id=VENDOR' \
-d 'vendor_auth_code=AUTH' \
-d 'coupon_type=checkout' \
-d 'coupon_prefix=STU' \
-d 'num_coupons=1' \
-d 'discount_type=percentage' \
-d 'discount_amount=50' \
-d 'allowed_uses=1' \
-d 'expires=2022-10-01' \
https://vendors.paddle.com/api/2.1/product/create_coupon | python -c "import sys,json;obj=json.load(sys.stdin);print(obj['response']['coupon_codes'][0]);"

Run this, and the output is nothing but the coupon. Sweet. This is already pretty useful, but let's take it to the next level. The Alfred level.

Using the shell script in Alfred

In our to-do list above, we said that this should trigger on entering a keyword in the Alfred search bar. So that's our starting point.

We'll go to the Workflow section of the Preferences and open a blank workflow, then add our keyword trigger by right clicking, choosing Inputs, and then Keyword.

Setting up a trigger

Then, we'll fill out the form with the standard stuff it asks about:

The Subtext part is the stuff that Alfred will show you right under the action's title. Here, we're using is as a kind of warning for what's coming.

Okay, the trigger is ready to go. Next, let's tell Alfred what to do when we trigger the action.

Running the script

But wait, where should we save the one-line script we made? Nowhere - that's where! Like I said, we want to make this  setup as portable as possible.

Paste your JSON decoding script and hit Save

Okay, last step is actually pasting the generated coupon code along with the surrounding words that make up the entire friendly sentence.

(Copy and) paste the output of the script

Write whatever you want, and place {query} where you want the output - in our case, the coupon - of the script to appear.

Connecting it all

Our workflow now looks like this

Three lonely boxes. Let's connect them. Drag the little handle from one box to the other so you get this:

Try it out

Go to a text field - any text field - and activate Alfred and begin typing your trigger keyword. Select it, and there it is!

To me, this not only boosted my productivity, but also made me dread the task less. That's the hidden side of automation: bring a little joy into your daily workflows.