23 sept. 2016

How do I blog? - Blogger posts from markdown and CLI

There are plans to migrate Silly Bytes to Hakyll and GitHub pages, but till then I'm still using Blogger and I wanted to make the posting process as painless and automatic as possible.

Every post I write is currently a separate git repo hosted on the Silly Bytes GitHub organization. The post is written and maintained in Markdown using Pandoc and a convenient Makefile generated by the made script.

Writing posts in Markdown is nice but is not very useful if you still have to mess around with Blogger's web interface, so here is the plan:
  • Write post in Markdown
  • Use made to generate a Makefile
  • Generate HTML from the Makefile post $ make
  • Push the HTML post to Blogger using Google APIs

The first 3 steps are already covered so lets dig into the Blogger negotiation part.

API script

Google APIs come in handy here, the best language option was Python (I refuse tu use Java). So starting from an example I came up with this ugly code:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from __future__ import print_function
from googleapiclient import discovery
from oauth2client import client
from oauth2client import file
from oauth2client import tools
import sys
import os
import httplib2
import argparse


def main(argv):
    if (len(argv) < 3):
        print("Post title must be provided as the first argument and html file as the second")

    post_title = argv[1]
    input_file = argv[2]

    scope = 'https://www.googleapis.com/auth/blogger'

    parent_parsers = [tools.argparser]
    parser = argparse.ArgumentParser(
    flags = parser.parse_args("")

        client_secrets = os.path.join(os.path.expanduser("~") + '/.sillybytes/',
        print("Can't find secrets.json file maybe?")

    flow = client.flow_from_clientsecrets(client_secrets,

    storage = file.Storage('auth_data' + '.dat')
    credentials = storage.get()
    if credentials is None or credentials.invalid:
        credentials = tools.run_flow(flow, storage, flags)

    http = credentials.authorize(http = httplib2.Http())
    service = discovery.build('blogger', 'v3', http=http)

        content = open(input_file, 'r').read()
    except FileNotFoundError:
        print("Input file not found")

    body = {
        "kind": "blogger#post",
        "title": post_title,
        "content": content

        posts = service.posts()
        request = posts.insert(blogId=SILLYBYTESID, body=body, isDraft=False)
        result = request.execute()
        print("Live: " + result['url'])
        print("Can't execute request")

if __name__ == '__main__':

The script will initiate a OAuth negotiation when needed and store the authentication tokens in the auth_data.dat file.
Install some python dependencies (Arch):

$ pacman -S python-google-api-python-client
$ pacman -S python-oauth2client


API project

Before we're able to use this we need to create a new API project, configure it and get the client_secrets.json that the script will use to start the OAuth negotiation.

First enable the Blogger API at: https://console.developers.google.com/apis/library

Then create a new project, create a new credential and download the JSON file from it. Don't worry you'll find your way in the interface. If you read the script you'll notice my client_secrets.json file will be located at ~/.sillybytes.
Now doing:

$ python deploy.py "post title" "post HTML"

Will push the post to Blogger! So we're done (?) not yet.


CLI tool

This is good enough already, we could invoke deploy.py from the Makefile, but it can be better.

I wrote a tool: silly.

$ silly help

I think the screenshot explains all by it self... Now i can create all the post boilerplate by doing silly new and deploying the current post with silly deploy how cool is that?