∴ikura

An ikura app — part III

As we round the bend with our bonegram application, we are confronted
with some hard requirements for dealing with Twitter. Although there
are plenty of good-will ’bots in action on Twitter, ’bots are probably
tolerated at best over there; we want to make sure we tread lightly.
So, what can we do to make sure we aren’t a pest?

Design requirements

A tweet with the same content (consecutively) leads to an
error with Twitter’s API. So, we want to design our app to never
tweet the same thing twice to the same person. This actually is
trickier than it seems, as we are not using a database.

A second hard requirement is we don’t want to do huge batch tweets.
This is a bit easier to design for, but still forces us to be
cautious & a tad creative.

Tweaking the code

In our ‘src/bonegram_lib.erl’ module, we
previously wrote two routines that
queried & collected tweets from Twitter (recall that the aritiy-one
version gathered the delta from the last time it was run). With this
already in place, we introduce new/2 and supporting functions. The
finalized module looks as follows:

This module is now capable of tweeting; new/2 takes details from
what it gathers via new/1 & updates bonegram’s Twitter status. For
every broken arm in the Twitter-verse, bonegram now will tweet one
of these (for example):

“@twitterMan21: Ugg.. no fun having a broken arm. Feel better :)”

Safeguarding bonegram

When we start the bonegram application, the initial ikura call should
prime our application; nothing more. In the many subsequent ikura
calls, however, we will be sending out bonegram tweets as usual.
What we don’t want is to tweet to the initial collection. Why?
We want to create a safeguard. If we tweeted to the first collection,
these users would potentially be hearing from us as often as we
stopped & started the application. Recall our hard requirements
above. It’s not that we plan to restart the app a lot, it’s just that
we want to prevent multiple tweets. This is why we design it this way.

Storing progress

To hold our state, handle errors, and behave predictably, we will use
a supervised ‘gen_server’ behavior. A new module is put into place
without too much thinking; ‘src/bonegram_srv.erl’ is as follows:

You can see that bonegram will talk with ikura via one HTTP
endpoint: ‘/latest.json.’ Notice, we preserved the ‘/test’
endpoint as well. You will see why, soon enough.

Cowboy glue

We have arrived at the end of our coding chores. Recall that our old
‘src/bonegram_handler.erl’ answered to ‘/test’ requests. That has not
changed, but we will now build-out the new ‘/latest.json’ handler which
wires our whole application together. Out last hacks are as follows:

This module is the meat & potatoes of our application’s flow. There is
a good amount going on, but mainly it works like this:

when ‘/latest.json’ is first called, it initializes our server state. It returns ‘{"tweets" : "initialized"}’ only once. Remember, this corner case exists so we adhere to our hard requirements.

for all subsequent calls, a count of the messages bonegram tweets is what is returned, ie. ‘{"tweets" : 3}.’

But ‘src/bonegram_handler.erl’ does even more. If you look closely,
you will also notice that unless our app is called locally or from
ikura’s IP, it will return a 401 error code. Meaning,
‘latest.json’ is never executed. This is good; we
don’t want to have, say, ‘https://your-bone-url.cc/latest.json’
publicly reachable for just anyone to call. Only ikura will have the
permission to run the code at this endpoint. We deliberately left the
‘/test’ endpoint public, however, so you can verify this to be true.

(It’s up to you to create an OTP release of this code & put the app
up on a server for ikura to call.)

Ikura will-call

Over at ikura.co, adding a timer to call our application
could not be easier. The Channel (e.g. http://123.344.555.666:8004) &
Pool (i.e. /latest.json) are the only two pieces of information we
need. We set the timing service for every fifteen minutes, and presto! —
we have a love-sharing Twitter ’bot tasked to run four times an hour.
Easy. (And reliable.)