CrowdersBot was my first foray into making a bot for Twitter, purely an experiment to see how difficult it was, but I needed a hook, I needed to give the bot purpose. Unpopular opinions seemed to be the way forward.
The thing about an unpopular opinion is that everyone engages. The people who agree share it amongst their peers, creating an echo chamber, and comment in order to reaffirm their beliefs and congratulate the original poster. On the flipside, people who strongly disagree will share with their peers in order to ridicule before commenting to tell the poster why they’re wrong.
Everyone’s a winner, except no one is.
With that decided, I had to think about how I was actually going to start doing this thing.
Tracery is a really simple language that can do complex things. The concept is easy. Using random word lists – called grammars – you can populate a predetermined template and generate text. That’s it.
Here’s an example:
"colour": ["red", "green", "blue", "yellow"], "origin": ["The ball is #colour#."]
You can probably guess the output. It will generate a sentence saying what colour the ball is, choosing the colour at random each time the sentence is generated. The four options will give you four results. Simple.
"size": ["big", "small"] "colour": ["red", "green", "blue", "yellow"], "origin": ["The #size# ball is #colour#."]
Again, pretty simple. With the added two options, we’ve doubled the potential output. The ball can be both big and red, or small and yellow, or any combination.
Let’s step it up a gear.
Here’s where you can start to make things complicated by nesting grammars and saving contextual information to generate more complex pieces of text.
"malename": ["Dave", "Geoff", "Bill", "Richard", "Simon"], "femalename": ["Sarah", "Jill", "Christine", "Helen", "Julia"], "name": ["#malename##male#", "#femalename##female#" ], "male": ["[they:he] [theirs:his] [their:his] [them:him] [gender:man]"], "female": ["[they:she] [theirs:hers] [their:her] [them:her] [gender:woman]"], "object": ["lawnmower", "hedgetrimmer", "garden shears" ], "scream": ["screamed", "shrieked", "hollered", "yelled", "squealed"], "blind": ["curtains", "blinds", "shutters"], "story": ["#person1# yawned as #they# walked out of the shower toward the window, dropping #their# towel as #they# stretched. #they.capitalize# suddenly realised someone was watching #them#. #they.capitalize# #scream#.\n'Oh, hi, #person1#', said #name#, I'm just bringing back your #object#. Looks like you need it.'\n #person1# slammed the #blind# closed."], "origin": ["#[person1:#name#]story#"]
This would produce:
Christine yawned as she walked out of the shower toward the window, dropping her towel as she stretched. She suddenly realised someone was watching her. She shrieked.
‘Oh, hi, Christine ‘, said Bill , I’m just bringing back your hedgetrimmer. Looks like you need it.’
Christine slammed the curtains closed.
Richard yawned as he walked out of the shower toward the window, dropping his towel as he stretched. He suddenly realised someone was watching him. He squealed.
‘Oh, hi, Richard ‘, said Julia , I’m just bringing back your lawnmower. Looks like you need it.’
Richard slammed the shutters closed.
These examples show how the gender of the main person has not only been specified but saved and reused throughout the text, as well as refer to a second person, before returning to the original subject. Handy stuff.
“But that’s a lot of code for a whole lot of nothing!” I hear you cry. Yes, it may be, but there’s no need to cry about it. Just by adding another ‘story’ line we can change the whole scenario, like so:
"#person1# opened the #blind# and looked at #their# #object#. The blades glistened in the sunlight as #they# smiled. #name# was going to pay... with #their# life."
Geoff opened the blinds and looked at his lawnmower. The blades glistened in the sunlight as he smiled. Helen was going to pay… with her life.
Using the existing grammars, you can add multiple templates to generate anything you like. If you find you need to expand, just add more lists.
“What the hell are you talking about? What does this have to do with tweeting unpopular opinions?!” To which I reply “Oh, crap. I knew there was supposed to be a point to all of this.”
Let’s discover what that point is together. To be fair, if you’ve made it this far, you’re probably going to stick with me anyway.
First, let’s search the #unpopularopinion hashtag and find something we can make a template from.
That’s a bingo.
Take out the specifics and we have:
"origin": ["I’m about to make everyone #mad#, but both #food# and #food# are #trash#. \\#UnpopularOpinion"]
Let’s create the grammars to populate it and generate different tweets. (Thesaurus.com is your friend).
"mad": ["mad", "angry", "crazy", "furious", "annoyed"], "food": ["stuffing", "cranberry sauce", "cheese", "cake", "ice cream", "yorkshire puddings", "chips", "ketchup", "mayonnaise"], "trash": ["trash", "rubbish", "garbage", "terrible", "junk", "awful"]
Put it all together and what do you get?
I’m about to make everyone mad, but both mayonnaise and stuffing are rubbish. #UnpopularOpinion
I’m about to make everyone crazy, but both yorkshire puddings and ice cream are junk. #UnpopularOpinion
I’m about to make everyone angry, but both cheese and ice cream are awful. #UnpopularOpinion
With all the different combinations, that’s generated 270 potential unpopular opinions! (I think. Is that right? It sounds right.)
Let’s grab another tweet and convert it to a template.
"origin": ["Unpopular Food Opinion:\n#food# is vastly overrated. It's not even top ten food list items, let alone deserving of #day#. \\#UnpopularOpinion"]
Unpopular Food Opinion:
Mayonnaise is vastly overrated. It’s not even top ten food list items, let alone deserving of Sunday. #UnpopularOpinion
Unpopular Food Opinion:
Cheese is vastly overrated. It’s not even top ten food list items, let alone deserving of Friday. #UnpopularOpinion
Great! Hang on, let’s grab another tweet and modify the template so we can introduce other things people can hold an unpopular opinion on, maybe music?
"origin": ["\\#UnpopularOpinion I #feeling# #thing#"]
And the grammar to go with it:
"feeling": ["love", "hate"], "thing": ["#food#", "#band#"], "band": ["Metallica", "Nirvana", "Pearl Jam", "Iron Maiden", "The Spice Girls"],
Whilst we’re at it let’s combine the two lists and make up our own really simple unpopular opinion, annoying two sets of people at once. We’ll also introduce a list of different hashtags in order to provoke engagement. What fun.
"origin": ["#band# is like #food#, they both make me sick. \\##hashtag#"], "hashtag": ["ChangeMyMind", "ImNotWrong", "UnpopularOpinion", "SorryNotSorry", "JustSaying", "RealTalk", "Fact", "Truth", "DontHate", "DontJudge", "TrueStory", "VoiceOfReason", "Amirite"],
Put everything all together and we have the following weapons in our arsenal.
Unpopular Food Opinion:
Ice cream is vastly overrated. It’s not even top ten food list items, let alone deserving of Wednesday. #Truth
I’m about to make everyone furious, but both cake and chips are awful. #ChangeMyMind
#JustSaying I love cheese
Nirvana is like mayonnaise, they both make me sick. #DontJudge
“This all seems like hard work.” you mumble lazily from beneath your soiled duvet. You might be right until you realise that there are lists of tens of thousands of words, all ready and waiting for you to use – https://github.com/dariusk/corpora/tree/master/data.
Lists of colours, animals, countries, food, sports, body parts, Norweigan first names, bodily fluids, and Egyptian gods. It’s all there, waiting for you. Just copy and paste anything you like.
All you need to do is write the original templates.
Tracery grammars are written in JSON format which can be a bit fiddly. As you can see from the examples, you need to use square brackets, quotes, commas, and colons, all in the right place. Fortunately, once you understand the simple order, the rest of it will come naturally.
You can test out your code before unleashing it on the world by using one of the many Tracery editors. My favourite is Beau Gunderson’s Tracery Writer. It’s nice and clean, generates the output on the fly – so you can see changes as you type, and will highlight any errors.
Here’s a link to all the code we’ve created here, laid out nicely so you can see exactly what’s going on and mess about with it to your heart’s content – Jim’s Shitty Tracery Examples.
You’ve got the concept. You’ve got the code. Your only problem now is where to host your marvellous creation. Well, that’s easy.
You could set up your own server and host everything yourself, but that seems like a pain. Instead, you could visit Cheap Bots Done Quick and sign in with a fresh Twitter account. Paste in your code, select how often you’d like to tweet, and you’re up and running. Job done. It really is that simple, and I know how you like things to be simple.
Now you’ve got the idea on how easy it is to write a twitterbot, you should ignore everything I’ve said and read a proper tutorial for Tracery. And who better to teach you than Kate Compton herself – http://www.crystalcodepalace.com/traceryTut.html
Once you’ve got the hang of things, it may interest you to know that you can also introduce pictures and emoticons and even use Tracery to procedurally generate images. There are lists on Tracery.io and Cheap Bots Done Quick which might help inspire you.
“That’s all well and good, thanks for all the code and that, but who the hell is Stephen Crowders?” you ask.
Seeing him set up a table at a Texas university, trying to be provocative in order to gain attention, was the kind of attitude I wanted for my mindless twitterbot. A little tweak of the name, a stock image of a ‘Businessman looking at the camera‘, and Stephen Crowders was born.
With all that in mind, let’s look at some of the nonsense that CrowdersBot comes out with. See if you can figure out the templates.
Sometimes, through pure chance, he makes a bit of sense.
So there we have it. If you’ve made it this far, you’ve probably come to one of two conclusions: Twitterbots seem fun and aren’t as difficult to create as I thought, or Jim’s an idiot with too much time on his hands.
They’re not mutually exclusive, it’s ok to think both.
CrowdersBot may have been my first but he certainly isn’t my last. I’ve also created a much more useful twitterbot that generates character names for writers, using Victorian forenames and abandoned UK villages as surnames. It also generates character descriptions to go along with them. You can read about that here, or visit him on twitter – @botwriters
Now go out in the world and try and find inspiration to create your own twitterbot using Tracery. Just make sure you come back and tell me all about it.