One of my desired blog specs was automation. The first automation I made was, a script modelled on this blog's example that would, from the command line, create a fresh new .md file with my Pelican template stuff filled-in. My latest little automation is a script I named (because was too annoying to type). automates the process of Pelican making -> publishing to my webhost -> updating Twitter. It's super simple, and I'll be using this blog entry as the first integration test/guinea pig.

Step 1: Pelican-make the blog

# Jan 2018. Automate publishing + tweeting. 

echo 'Pouring blog out into local angelaambroz/ dir'
make html

After creating this blog post, in Markdown and in my blog/content/ folder, I need to run make html to pour it into the local directory where the rest of my website is. This comes built-in with Pelican (docs).

Step 2: Securely copying my local website copy onto my webhost server, using ssh

Oh man, how I love SSH (secure shell)! I could go on and on about it. And I love scp (secure copy protocol), which lets you copy files and folders to remote servers. It's awesome.

Before I could get (or any bash scripting into remote servers) to work, then, I needed to get an SSH handshake set up between me and the webhost. I did this:

  1. Create new SSH keys, specific to my webhost's server, with ssh-keygen.
  2. Put those keys on the server via the UI. This is similar to how it works on GitHub. Further reading: Adding a new SSH key to your GitHub account
  3. Set up my .ssh/config file to use only those website-specific keys when SSHing or SCPing into that server. Further reading: SSH - Too Many Authentication Failures. This was to troubleshoot a "too many attempts" disconnection I kept getting. It was trying all my other SSH keys.

Once all that was done, I could now securely connect to my website from the command line. Awesome! I copied a test file over with scp, and voila! Things were ready. I added the following to

echo 'SCPing over to'
scp -r /path/to/local/website/blog my_username@my_webhost:~/path/to/website/

Step 3: Let Twitter know

Okay, so I'm skipping over a Python script that I wrote,, that tells the Twitter world about a new blog post when it happens. It's not terribly interesting, or terribly different from my post on Making a Twitter bot. Here's the only different stuff:

def _make_link(blog_slug, blog_date):
    Example link:
    Twitter will handle link shortening via 

    base_url = ''
    date = datetime.strptime(blog_date, '%Y-%m-%d %H:%M')
    year = date.year
    month = date.strftime('%b')
    day =
    return f"{base_url}/{year}/{month}/{day}/{blog_slug}/index.html"

def tweet_about_blog(post, connection):
    """Tweet about post
    robot_emoji = b'\xf0\x9f\xa4\x96'
    url = _make_link(post['slug'], post['date'])
    tweet = bytes(f"{post['blurb']} {url} ", 'utf-8') + robot_emoji 

def get_last_n_blog_entries(n=1):
    """Grab last n (default=1) blog entries from the content/ folders"""
    latest_blog_posts = sorted([x for x in os.listdir('content/') if ".md" in x])[-n:]

    title_list = []
    for blog_post in latest_blog_posts:

        with open(f"content/{blog_post}") as f:
            blog_list = f.readlines()

        title = [x for x in blog_list if 'Title: ' in x][0]
        slug = [x for x in  blog_list if 'Slug: ' in x][0]
        date = [x for x in  blog_list if 'Date: ' in x][0]
            'blurb': title.strip().replace('Title: ', 'New blog: '),
            'slug': slug.strip().replace('Slug: ', ''),
            'date': date.strip().replace('Date: ', '')

    return title_list

The most interesting hm!-inducing stuff in this Python script were just:

  • Finding the the unicode (or is it hex? what is that thing?) for the robot_emoji.
  • List comprehensions are always nice? e.g. latest_blog_post = sorted([x for x in os.listdir('content/') if ".md" in x])[-n:]
  • I'm still in love with Python 3.6's fancy print formatting, f"Hello, {variable_for_name}!".

All that said, it just pops into like so:

echo 'Updating Twitter...'

echo 'Done!'

And that's it!

And, okay, I just realized I could have used or (both built-in Pelican publishing tools) for this... Oop!