A bit over two years ago I played around with the (then) recently released Marvel API to build some cool demos. The end result of that experiment was a simple web app that randomly displayed a Marvel comic book cover every minute: http://marvel.raymondcamden.com/.
This weekend I was thinking about a few Twitter accounts I follow that just post random pictures. (I'll share a list of them at the end.) I like these accounts because they're easy to ignore, provide something simple and cool to my feed, and are just a random piece of coolness during the day. I thought it might be kind of fun to build a similar mechanism for comic books (well, Marvel comics, I need to see if DC has an API).
In theory - all I needed to do was:
- Create a way to select a random cover (which was already done - by me)
- Create a way to Tweet (there's probably a npm library for that - yep - there is)
- Create a schedule (there's probably a npm library for that too - yep - there is)
It ended up being very quick to develop - maybe two hours total. Here is the complete source of the main script file. (Note, the entire thing is up on GitHub - the link will be at the bottom.
/*eslint-env node*/
var request = require('request');
var express = require('express');
var credentials = require('./credentials.json');
var Twitter = require('twitter');
var client = new Twitter(credentials.twitter);
var marvel = require('./marvel');
marvel.setCredentials(credentials.marvel.private_key, credentials.marvel.api_key);
// cfenv provides access to your Cloud Foundry environment
// for more info, see: https://www.npmjs.com/package/cfenv
var cfenv = require('cfenv');
var app = express();
app.use(express.static(__dirname + '/public'));
// get the app environment from Cloud Foundry
var appEnv = cfenv.getAppEnv();
// start server on the specified port and binding host
app.listen(appEnv.port, '0.0.0.0', function() {
// print a message when the server starts listening
console.log("server starting on " + appEnv.url);
});
var MONTHS = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
function tweetRandomCover() {
console.log('First, we get a random cover.');
marvel.getCover(function(res) {
console.log('back from mavel');
console.dir(res);
var tweet = res.title + ' published '+(MONTHS[res.date.getMonth()])+' '+res.date.getFullYear() +'\n'+res.link;
console.log('Now going to fetch the image link.');
request.get({url:res.url,encoding:null}, function(err, response, body) {
if(!err && response.statusCode === 200) {
console.log('Image copied to RAM');
client.post('media/upload', {media: body}, function(error, media, response) {
if(error) {
console.error('Error from media/upload: '+error);
return;
}
// If successful, a media object will be returned.
console.log('Image uploaded to Twitter');
var status = {
status: tweet,
media_ids: media.media_id_string
}
client.post('statuses/update', status, function(error, tweet, response){
if (!error) {
console.log('Tweeted ok');
}
});
});
}
});
});
}
app.get('/forceTweet', function(req, res) {
tweetRandomCover();
res.end('Done (not really)');
});
var cron = require('cron');
var cronJob = cron.job('0 6,12,18 * * *', function() {
console.log('do the cover');
tweetRandomCover();
console.log('cron job complete');
});
cronJob.start();
Let's break it down bit by bit, focusing on the important parts. To handle the Twitter API, I used the twitter Node library. As you will see a bit later in the code, it is incredibly trivial to use, even when creating Tweets with media attached.
The Marvel API is just a copy of the code I used before, although I've modified it a bit so I can pass in my credentials.
The real meat of the code is in tweetRandomCover
. We begin by asking the Marvel API for a random cover. If you read my post from two years ago you'll note that I have to fake that a bit. I essentially select a random month+year and grab everything I can from there - then select an item.
Once I have the random issue, I use the request library to suck down the binary of the image into a variable. I've heard of this library quite a bit, but I've never actually used it. Big mistake on my part.
Finally - I have to create the tweet. Twitter requires you to upload the media first so it is a two step process. First the image is posted and then the actual Tweet is created. I've got a bit of "Callback Hell" going on here and if this app did anything else I'd abstract this logic out of the main script, but since this isn't a web app people will hit, I'm not going to worry about it.
The final aspect is scheduling - which you can see is done via node-cron. Easy to use - it took me longer to figure out the right cron syntax than it did to implement the code. As you can see, I've selected a schedule that should post tweets three times a day which "feels" right for this kind of account. I may tweak that later.
You can find the complete code (although there's not much else) up on GitHub: https://github.com/cfjedimaster/randomcomicbook. I'm hosting the app up on IBM Bluemix.
And of course, you can (and should!) follow the Twitter acount: https://twitter.com/randomcomicbook
Incredible Hulk (1962) #146 published December 1971https://t.co/3VyfTGup9r pic.twitter.com/5jJFJHj58h
— Random Comic Book (@randomcomicbook) February 22, 2016
PS...
So yeah - about those random Twitter accounts I follow for pictures? Here they are:
- https://twitter.com/EmrgencyKittens
- https://twitter.com/iLove_Aviation
- https://twitter.com/Aviation4_Life
- https://twitter.com/ClassicStarWars
I used to follow some related to historical pictures, but they either turned to spam or shared pictures unrelated to history, which to me is a cardinal sin of these types of accounts. (Another example - news organizations that will RT their sports or entertainment accounts. I freaking hate that.)