Sycorax: Bring Fictional Characters to Life on Twitter

His mother was a witch, and one so strong
That could control the moon, make flows and ebbs,
And deal in her command without her power.

The Tempest, Act V, Scene I

Sycorax is a Twitter client, written in Python, that choreographs the online behavior of fictional characters. Other tweet schedulers make your personal Twitter stream look like a clockwork robot is behind it, posting tweets at the optimal time for penetration into your social network. Syxorax lets fictional characters use Twitter the way real people do. Your characters can post at odd hours and talk to each other, taking their lines from a simple script you write, but without any ongoing work from you.

Sycorax was originally written to enact the Twitter feeds for my novel Constellation Games. To see what it can do, check out the feeds for Ariel Blum and Tetsuo Milk, then see how the same feeds are archived according to a fictional timeline. The software is made available under a BSD-style license. It is hosted on Github.

Table of Contents

Let's get started

Here's a Sycorax script in which Alice, over the course of a few days, complains about her boring life, while her "friend" Bob taunts her. Don't worry about understanding the notation—I'm putting this at the top so you can see how little there is to a Sycorax script. Even without knowing what "7A" and "+R10M" mean, you should be able to follow the script.

== Chapter 1 7A I just woke up. 45M Going to work now. 1H I'm at work. 3H What a boring day. 1H Lunch! 2P I'm bored again. +R10M You are a fictional character being used in a software demo. No wonder your life is boring. R5M I know, right? It sucks. 5P Going home. 90M I sure love my cat. 8A Oh no, I overslept! +R5M I've been up for three hours already! == Chapter 2 11A I've decided never to post to Twitter again!!!! 2D9A Perhaps my earlier decision was premature.

Now let's see what it takes to stage this drama on Twitter.

Setting the stage

First you need to install a recent version of the python-twitter and pytz libraries, as well as their dependencies. Here's the command I ran:

$ sudo easy_install twitter $ sudo easy_install pytz

The next step is to create a directory to hold your script and its configuration. The Sycorax package contains an example directory for the Alice-Bob drama in example/.

Within the directory you just created, create a file config.json. This file gives Sycorax four pieces of information about your drama which is not kept in the script itself:

  1. The start date
  2. The time zone
  3. The chapter length
  4. The characters who will be posting to Twitter

Take a look at the example config.json, and then we'll go through it in detail:

{ "start_date" : "2011/07/09", "timezone" : "US/Central", "chapter_duration_days" : 7, "authors" : [ { "account" : "Alice", "twitter_token" : "ALICE'S TOKEN GOES HERE", "twitter_secret" : "ALICE'S SECRET GOES HERE" }, { "account" : "IAmBob", "code" : "+", "color" : "#ddaadd", "twitter_token" : "BOB'S TOKEN GOES HERE", "twitter_secret" : "BOB'S SECRET GOES HERE" } ] }

When will the drama begin?

Sycorax needs to know when to start posting to Twitter. This is the start_date variable. This line of Python tells Sycorax to start the feeds going on July 9, 2011:

"start_date" : "2011/07/09",

Where does the drama take place?

The main character of Constellation Games lives in Austin, Texas. The computer I'm using to script this character isn't in Texas: it's in California, and its clock is set to UTC. Rather than figure out the time difference between Austin time and UTC, and use that when time-coding my script, I coded all the tweets using Austin time, and set the timezone variable to Austin time:

"timezone" : "US/Central",

Thus, when I say in the script that a tweet should be posted at 1 PM, I mean 1 PM Central Time.

How long does a chapter last?

Sycorax divides the drama into chapters. A chapter might last a day, a week, or longer. Once all of a chapter's tweets have been posted, no more will be posted until the next chapter begins.

This line of JSON tells Sycorax that a chapter lasts one week:

"chapter_duration_days" : 7,

(If you don't want to use chapters, you can just put all your tweets into a single chapter, and it won't matter what you put for chapter_duration_days.)

Who are the characters?

You'll need to set up one Twitter account for every character in your drama. You'll also need to authorize each Twitter account with an OAuth access token for Sycorax.

The characters go into the authors list. In the example above, there were two authors, Alice and Bob. Alice was the default author, so let's put her in first:

"authors" : [ { "account" : "Alice", "twitter_token" : "ALICE'S TOKEN GOES HERE", "twitter_secret" : "ALICE'S SECRET GOES HERE" } ]

We specify the name of Alice's Twitter account as well as the OAuth token and OAuth secret. Since Alice is the main character in this drama, that's all we need to specify. She's the default.

Bob is not the default character, so we need to provide two additional pieces of information for him: code and color:

"authors" : [ { "account" : "Alice", "twitter_token" : "ALICE'S TOKEN GOES HERE", "twitter_secret" : "ALICE'S SECRET GOES HERE" }, { "account" : "IAmBob", "code" : "+", "color" : "#ddaadd", "twitter_token" : "BOB'S TOKEN GOES HERE", "twitter_secret" : "BOB'S SECRET GOES HERE" } ]

The value of the code variable is a punctuation mark that's used in the script to distinguish Bob's lines from Alice's lines. In AUTHORS above I set Bob's code to the plus sign. The + in this line indicates that this is Bob's line, not Alice's:

+R10M You are a fictional character being used in a software demo. No wonder your life is boring.

The value of the color variable is the color that should be used to mark up Bob's lines in HTML representations of the script.

Getting the OAuth tokens

Creating OAuth tokens for your characters can be tricky, so I've provided a standalone script get_access_token.py that does most of the work for you. Here's what it looks like:

$ python get_access_token.py Let's set up a character with Sycorax! 1. Log in to Twitter as your character. 2. Visit this URL: https://api.twitter.com/oauth/authorize?oauth_token=tokenstring 3. Authorize Sycorax to access your character's account. 4. Come back here, type in the PIN you got from Twitter, and hit Enter:

At this point I log in to Twitter as IAmBob and visit the api.twitter.com URL. Twitter asks if I want to authorize Sycorax to access Bob's Twitter account, and I say "authorize that sucker!" Twitter then gives me a six-digit code like "9181370". I go back to the terminal window, type 9181370, and hit enter.

4. Come back here, type in the PIN you got from Twitter, and hit Enter: 9181370 Success! Put something like this as your "authors" list in config.json: [{"twitter_token": "BOB'S TOKEN GOES HERE", "account": "IAmBob", "twitter_secret": "BOB'S SECRET GOES HERE"}]

The script gives me some JSON to start with when I add Bob's entry to the author list in config.json.

Writing the script

The script to your drama goes in the same directory as the config.json file. The script file should be a text file called input.txt.

What goes in that file? Let's take another look at the example script, and then I'll explain all the symbols like "==" and "7A" and "R5M" so that you can write your own script.

== Chapter 1 7A I just woke up. 45M Going to work now. 1H I'm at work. 3H What a boring day. 1H Lunch! 2P I'm bored again. +R10M You are a fictional character being used in a software demo. No wonder your life is boring. R5M I know, right? It sucks. 5P Going home. 90M I sure love my cat. 8A Oh no, I overslept! +R5M I've been up for three hours already! == Chapter 2 11A I've decided never to post to Twitter again!!!! 2D9A Perhaps my earlier decision was premature.

Chapters

Create a new chapter by writing two equal signs, then a space, then the name of the chapter.

== Chapter 2

Once the tweets for the first chapter run out, no new tweets will be posted until the next chapter starts. (chapter_duration_days controls how long this is.)

If your chapters have subsections, you can create a subsection by writing two dashes and the subsection name:

== Chapter 1 -- The First Day

This has no effect on the spacing of the tweets, it's just for your own convenience.

Commands

The first "word" of your tweet may be a command. In the example above, "7A", "45M", and "+R10M" are all commands. You can use commands to say which character is posting the tweet, to say what time of day the tweet should happen, and to make one tweet a reply to the previous tweet.

Set the author

If you don't specify an author, the tweet will be posted by your default author. In our example, Alice is the default author, because we put her first in AUTHORS. Here's a tweet from Alice:

2P I'm bored again.

If a command includes the punctuation mark associated with one of the authors, that tweet will be posted by the corresponding author. Here's a tweet from Bob, whose punctuation mark (as defined in AUTHORS) is the plus sign:

+R10M You are a fictional character being used in a software demo. No wonder your life is boring.

Make it a reply

The capital letter "R" in a tweet will make it a reply to the previous tweet. My research indicates Twitter will only accept this if the two tweets have different authors—you can't reply to your own tweet.

You should now be able to recognize this as a dialogue between Alice and Bob:

2P I'm bored again. +R10M You are a fictional character being used in a software demo. No wonder your life is boring.

Set the time of day

You can say approximately what time of day a tweet should happen by including in the command a number from 1 to 12, and then "A" for AM or "P" for PM. "2A" means 2 AM. "12P" means noon. "4P" means 4 in the afternoon.

If you put "4P" in a command, Sycorax will not post the tweet right at 4 PM on the dot. That's not how real people behave. Instead, Sycorax will post the tweet sometime in the four o'clock hour: sometime between 4:00 and 4:45.

You should now be able to recognize this as a dialogue between Alice and Bob, which is supposed to start at around 2 PM.

2P I'm bored again. +R10M You are a fictional character being used in a software demo. No wonder your life is boring.

Set the delay between tweets

Instead of giving an absolute time of day for a tweet, you can say how much time should elapse between two tweets. The command for this is some number, and then "M" for minutes, "H" for hours, and "D" for days. "15M" is fifteen minutes, "2H" is two hours, and "2D" is two days.

The actual delay will not be exactly fifteen minutes (or whatever). Sycorax introduces a fuzz factor of twenty percent. So a "15M" tweet might be posted 13 minutes after the previous tweet, or 17 minutes after the previous tweet, or anywhere in between.

You should now be able to completely understand this exchange between Alice and Bob:

2P I'm bored again. +R10M You are a fictional character being used in a software demo. No wonder your life is boring.

Alice makes her initial tweet some time in the two o'clock hour, and Bob responds approximately (but not exactly) ten minutes later.

If your delay is longer than one day, you should probably also specify a time of day:

11A I've decided never to post to Twitter again!!!! 2D9A Perhaps my earlier decision was premature.
"2D9A" means "2 days later, at around 9 AM." If you omit the "9A", the second tweet will happen two days later, within a few hours of 11 AM— that is, sometime in the morning or early afternoon. You probably don't want to give Sycorax that much leeway.

Generating the timeline

Once you've written your script, it's time to turn it into a timeline. Sycorax will take your approximate timecodes like "8A" and "10M", and schedule the tweets for precise times like "8:21 AM" and "8:33 AM".

To generate a timeline, run the make_timeline.py program and pass in a script directory as the first argument:

$ python make_timeline.py example Writing HTML timeline to example/timeline.html. Writing JSON timeline to example/timeline.json.

(Try it with the example/ directory in the Sycorax package!)

If your tweets are tightly packed together, or if you just have bad luck with the random number generator, Sycorax might not be able to come up with a consistent timeline for your script. If this happens, make_timeline.py will fail:

ValueError: Calculated timestamp for "Tweet 2" is 13:20, which comes before calculated timestamp for the previous tweet "Tweet 1" (14:53). Trying again may help.

You can try running make_timeline.py again, and Sycorax will usually pick better times the second time. If you consistently get errors, you should space out your tweets a bit more so that Sycorax has enough time to work with.

Sycorax generates two timeline files and places them both in the script directory. The JSON timeline is used when actually posting to Twitter, and the HTML timeline is easy for you to look over for script and scheduling errors. Let's look at the HTML timeline first:


Author guide:

Chapter 1

Sat 09 Jul

Sun 10 Jul

Chapter 2

Sat 16 Jul

Mon 18 Jul


With this HTML view you can quickly scan the script to make sure it'll be enacted as you envisioned. Here, the first chapter plays out over July 9 and July 10. The second chapter picks up a week after July 9 (remember, we set chapter_duration_days to 7 days), and plays out over the first few days of that week.

You shouldn't need to look at timeline.json unless something goes wrong. But if you're curious, it looks like this:

{"timestamp": "09 Jul 2011 13:39:02 UTC", "in_reply_to": null, "author": "Alice", "internal_id": "423a125e03512e3b09a0579289ea22c4", "text": "I just woke up."}
{"timestamp": "09 Jul 2011 14:20:28 UTC", "in_reply_to": null, "author": "Alice", "internal_id": "228d31428851822d44a0d557e0e9c785", "text": "Going to work now."}
...

Enacting the drama

The enact.py script enacts the drama by posting to Twitter as the various characters. It should be run as a cron job, very frequently: once a minute, or once every five minutes. To run enact.py, you pass it the path to your script directory.

$ python enact.py example Posting "I just woke up."

When you run this program, Sycorax will load the timeline, and look for a tweet that should have been posted by now but hasn't been. If it finds such a tweet, Sycorax will post that tweet to the appropriate Twitter account, and write it to a progress file, located within the script directory. If all the tweets that should have been posted, have been posted, Sycorax will do nothing.

The name of the progress file is progress.json. Tweets are stored here once posted, so that Sycorax knows not to post them again. Here's what progress.json might look like after the first tweet has been posted:

{"text": "I just woke up.", "planned_timestamp": "09 Jul 2011 13:39:02 UTC", "actual_timestamp": "10 Nov 2011 09:10:54 ", "twitter_id": 120943957396785, "internal_id": "423a125e03512e3b09a0579289ea22c4"}

It's the same information as in the original timeline.json, but there are two new fields: actual_timestamp, which is the time the tweet was actually posted, and twitter_id, which is the ID Twitter assigned to the tweet once it was posted. As with timeline.json, you shouldn't need to look at this file unless something goes wrong.

The next time you run enact.py, either nothing will happen (because it's not time for the next tweet yet), or the next tweet will be posted and written to progress.json.

$ python enact.py example Posting "Going to work now."

Over time, all the tweets in timeline.json will be copied to progress.json. Once the whole script has been enacted, you can continue to run enact.py, but nothing will happen.

Quick reference

  1. Create a directory myscript/ for your script.
  2. Put the Sycorax configuration for your scirpt in myscript/config.json.
  3. Write the script in myscript/input.txt
  4. Run python make_timeline.py myscript to time-code your script.
  5. (If you get a timing error, try again or increase the time between tweets.)
  6. Look over myscipt/timeline.html for errors.
  7. Once you're ready, start running python enact.py myscript to enact the script on Twitter.

Markup reference:

Advanced topics

The only code in Sycorax that actually deals with Twitter is in enact.py. This means that you can write a script, time-code it with make_timeline.py, and use the resulting timeline.json to do... whatever you want.

You can write your own code that posts the script to identi.ca, to a Facebook wall, to some other microblogging service, or to a web page. You can send your updates through email or do whatever you want. The hard part is turning a human-readable script into something that looks natural and is time-coded down to the minute, and make_timeline.py takes care of that part.


This document (source) is part of Crummy, the webspace of Leonard Richardson (contact information). It was last modified on Thursday, June 27 2013, 14:15:02 Nowhere Standard Time and last built on Wednesday, August 27 2014, 12:00:05 Nowhere Standard Time.

Crummy is © 1996-2014 Leonard Richardson. Unless otherwise noted, all text licensed under a Creative Commons License.

Document tree:

http://www.crummy.com/
software/
sycorax/
Site Search: