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.
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.
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:
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" } ] }
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",
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.
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
.)
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.
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
.
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.
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.
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.
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.
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.
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.
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.
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:
- Alice
- IAmBob
Chapter 1
Sat 09 Jul
- 07:13 I just woke up.
- 08:07 Going to work now.
- 09:12 I'm at work.
- 12:26 What a boring day.
- 13:19 Lunch!
- 14:41 I'm bored.
- 14:50 You are a fictional character being used in a software demo. No wonder your life is boring.
- 14:53 I know, right? It sucks.
- 17:06 Going home.
- 18:26 I sure love my cat.
Sun 10 Jul
- 08:09 Oh no, I overslept!
- 08:16 I've been up for three hours already!
Chapter 2
Sat 16 Jul
- 13:09 I've decided never to post to Twitter again!!!!
Mon 18 Jul
- 09:25 Perhaps my earlier decision was premature.
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."}
...
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.
myscript/
for your script.
myscript/config.json
.
myscript/input.txt
python make_timeline.py myscript
to time-code
your script.
myscipt/timeline.html
for errors.
python enact.py
myscript
to enact the script on Twitter.
Markup reference:
== Chapter 1
= The next day
7A My tweet
11P My tweet
15M My tweet
1H My tweet
1D6A My tweet
10D10P My tweet
+1H My tweet
R10M My tweet
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 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 Monday, June 05 2023, 16:00:01 Nowhere Standard Time.
| Document tree: Site Search: |