How to make nikola run actions when deploying new posts

So last week I set up Nikola to automatically tweet when I deploy a new blog post. Wordpress has had this functionality for a while now, and I like the idea of sharing my posts with all my twitter followers, so after my very first post I started looking into ways to do this.

Nikola has nice integrated plugin and theme repositories, and while I was hoping to find that a plugin to do what I was looking for hadn't yet been created, I was (half-way) disappointed. It turns out there is a deploy hooks plugin, which basically lets you define shell commands or python callables in a list for each new post you create. I'm not sure if this goes for pages as well, though I'm thinking not.

Installing the deploy hooks plugin

You can install this plugin (as long as you have nikola), by doing

nikola plugin -i deploy_hooks

That command will make a "plugins" subdirectory inside your site folder (all nikola commands like build, deploy, etc) need to be run from within a site dorectory. If you want to install this or any other plugin for the entire system, try...

nikola plugin --user -i pluginname

At any rate, when installing, you should see a sample config file printed out. Personally I think this should just tell you that there is such a file, and then provide the path to it, but ehh. Anyways, if you installed the plugin only to your site, rather than system wide, the contents of that sample config file is at plugins/deploy_hooks (relative to your site directory). This sample config file defines a few things:

  • DEPLOYED_HOOKS[]: A list of iether shell commands or python callables. If it's a python callable, just list the function name, the only argument will be an entry object, more on that in a bit. If it's a shell command, just put it as well as any other args to be passed in quotes. I'm not sure how you would substitute in data about the post it's calling the script for, though.
  • UNDEPLOYED_HOOKS[]: This is the same as above, accept items in this list get called once for each post that isn't deployed. This does mean for posts that are marked as "drafts", or who's date is set in the future, in which case the helpful sample config suggests you might want to schedule a chrontab job to deploy a post at that time.

How to add hooks to your site config

I couldn't find any documentation that specifically said that all plugin config examples needed to be incorperated into your own config (the one in your site's directory), I guess it was implied. Either way, that's what you do with all nikola plugins, look at the example configs (if provided), and then add a customized version of the options shown to your main site's conf.py.

Getting a twitter app

For this specific hook, we'll be using the Twitter API, which python helpfully has several packages to allow us to use. I chose Tweepy, because I've used it in previous projects, though you can very easily adapt this to your favorite twitter python wrapper, or even a non-python wrapper as long as you can call it with commandline arguments).
The important thing here is that you make a Twitter app, called whatever you want (you can even reuse one here from a previous project like I did) - all you need is 4 keys:

  • A consumer key - this is a key unique to your application.
  • Consumer key secret - This key is the second in a matched pair, specific only to your app.
  • Token - This is an account token specific to your account and the app your using. If the twitter account you used to create the app is the one you would like to tweet from, then you can easily get this on the twitter applications page linked above, under the "Keys and access tokens" tab, "Your access token" heading. If the Twitter account that owns your application is different than the account that you would like nikola to tweet from, then you'll need to do the OAuth dance with twitter. No worries, your chosen API wrapper (python or not), almost certainly has this covered if it's worth using.
  • Token secret - Again, this is specific to the app and account your using with twitter, see previous item for obtaining one.

After you have these keys, your ready to tell nikola what to do.

The tweet function and hook

For my automatic tweet function, I did the following:

import tweepy
twitter={
  'c_key' : 'Your twitter consumer key here',
  'c_secret' : 'Your twitter consumer secret here',
  'token' : 'Your account-specific twitter auth token here',
  'token_secret' : 'Your account-specific token secret here'
}

def sendblogtweet(entry):
  """Sends a tweet. What else did you expect it to do... Make you coffee?"""
  auth = tweepy.OAuthHandler(twitter['c_key'], twitter['c_secret']) 
  auth.set_access_token(twitter['token'], twitter['token_secret'])
  api = tweepy.API(auth)
  #Now that we've got a twitter API object, we can form our tweet
  tweet='New blogpost! %s - %s' %(entry.title(), entry.permalink(absolute=True))
  api.update_status(status=tweet)

The hook

The hook is the simplest part:

DEPLOYED_HOOKS=[sendblogtweet]

Testing it out

At this point you might want to test your function's guts to make sure they work. E.G. Copy the function and keys / tokens dict to a notepad, fill out your keys and tokens, hardcode a tweet, and then run it, sans the "entry" stuff. This will be the easiest way to check for errors either with your API wrapper, or twitter itself (like missing or incorrect keys). Once this works, your ready to move on. Now save and close your config file, after making sure there aren't any obvious syntax errors. You can probably import conf at a python prompt like any other module to check for said errors, as far as I know the file doesn't run any functions, just (now) defines them as well as variables dicts, and lists.
Then, write or deploy a previously unpublished blog post, and keep an eye on twitter, as well as the deployment output. If it worked, nice. If not, then you might want to check the following:

  • Are your Twitter keys and tokens correct? This might seem like an obvious thing, but it's obvious because it's also one of the most common problems.
  • Does your chosen API wrapper work when you test it independently like in a python console?
  • Did you add the DEPLOY_HOOKS list to your config file?

Making this better

This is really just a minimal example and an excuse for me to post something here, but it can be extended quite easily. Some things you might want to look into are:

  • Adding tags to the tweet that you use in each post you deploy, as the config example for deploy_hooks suggests.
  • Shortening the URL of the blogpost if the title + the URL is over 140 characters.
  • Including a snippet of the post or it's teaser if you use thos, in the tweet.
  • Mentioning authors if your Nikola system supports those, and they have an asociated twitter handle.

Hope this post is useful, and happy blogtweeting!

Comments