Last night I added native post-to-Twitter support to Camel. In and of itself, this is not particularly remarkable. However, the path to get there is an interesting example of how developers work, and how one small change can create some significant ripples.
Everything started with me deciding to add the ability to make link posts to Camel. That went smoothly and, at first glance, didn’t create any further problems. Until:
@caseyliss You seem to have forgot to somehow expose the links in your RSS. Just links to the post on the site.— Hampus Jensen (@Leonick91) February 6, 2015
@caseyliss Having the item link be your link and including a permalink to your site at the bottom of the post content seem to be "standard".— Hampus Jensen (@Leonick91) February 6, 2015
I needed to do something to fix this.
Diversion #1: RSS Modifications
Thus, I did what was recommended, and I had seen a zillion times before. I modified the RSS generator to set the URL of the post not as my own site, but instead the target of the link post. All seemed well.
Then I made a second link post and realized something else was amiss.
A New Problem
At the time, I was using IFTTT to automatically tweet when I make new posts to this site. That was working perfectly, until I made that change to how the RSS worked. Notice the difference between this tweet, which was before the change:
Camel Gets Link Posts http://t.co/f36LORcVzD— Liss Is More (@caseylisscom) February 6, 2015
As compared to this tweet, after the change:
Top Gear Aftermath http://t.co/849tDD5TVV— Liss Is More (@caseylisscom) February 8, 2015
You’ll notice in the latter tweet, the link is to the external URL, not to this site. This is a change from how it was before I adjusted the RSS feed. Unfortunately, I preferred it the old way.
I was now at an impasse — I could either have RSS work the way I want or Twitter work the way I want. At a glance, there was no way to tell IFTTT to use a different URL for what it posts to Twitter.
Diversion #2: Creating a Twitter App
Most of the motivation for using IFTTT was to avoid having to write my own Twitter client into Camel. I didn’t want to do that specifically because I wasn’t sure how the OAuth flow would work. The idea was for Camel to autonomously post to Twitter; I had no intention of building a login flow just for that.
As it turns out, I had made a poor assumption.
When creating a Twitter application (which is to say, asking Twitter for the ability to access its data programmatically), you can actually get the requisite OAuth tokens by hand for the account that created the application. So, I created an application off of the @caseyliss Twitter account, and was able to mock something up using the twitter package for Node.
However, since I created the app off of my Twitter account, it could only post to my account. Now that I had things working, I need to create a Twitter application off of the @caseylisscom account, so tweets would be posted from there. That should have been easy, until I ran into this:
At a glance, this is no big deal. Until I remembered that my phone number is associated with my @caseyliss account. Unsurprisingly, Twitter only allows a phone number to be used for one account.
Diversion #3: Getting a Burner Number
Most people need a “burner” phone number to do nefarious things. Perhaps they’re trafficking illegal items. Perhaps they’re running around on their spouse.
Me? I’m trying to get my blog to post to Twitter.
I wasn’t sure how to go about getting a burner number that can receive text messages. I asked on Twitter, and got a few responses. Most immediately jumped to Google Voice, but others pointed out that Google Voice can’t receive messages from short codes. So, that was a non-starter.
By the magic of Twitter, though, I did get the winning suggestion:
I quickly checked out Burner, and it looked promising. I downloaded it, set up a number for free, and plugged that into Twitter. It sent me a confirmation code, which Burner received no problem, and I was up and running.
A New New Problem: Determining When to Tweet
The next obstacle was determining the right time to tweet. Camel doesn’t have any sort of database, and Heroku has an ephemeral filesystem, which means I can’t just save a record of the last post that has been tweeted. Thus, the only reliable way to figure out what has been tweeted is to look at the tweets of the account itself. In my case, that meant inspecting @caseylisscom’s tweets.
Though I very rarely write tweets by hand from @caseylisscom, I do on a rare occasion. Thus, it wouldn’t be as simple as just grabbing the most recent tweet, or even the most recent tweet that had a URL associated with it. I needed a reliable way to determine when Camel itself had tweeted last.
The easiest answer? Look for tweets posted by the just-created Twitter app that Camel uses.
The general idea is, Camel looks at the most recent tweet posted using “Camel Spitter” (the aforementioned app) has made. Once it finds the most recent auto-tweet, Camel looks at the URL on that post, and sees if it matches the URL of the most recent post within Camel. If not, Camel will fire off a new tweet for that post.
Diversion #4: Deployment Issues
At this point, I had:
- A RSS feed that points to the external link when the post is a link post
- A Twitter app unique to my website
- Code to automatically tweet when a new post is posted
I had everything I needed, so I went ahead and deployed the changes to Heroku. Which didn’t work:
module.js:340 throw err; ^ Error: Cannot find module 'Twitter' at Function.Module._resolveFilename (module.js:338:15) at Function.Module._load (module.js:280:25) at Module.require (module.js:364:17) at require (module.js:380:17) at Object.<anonymous> (/app/camel.js:21:15) at Module._compile (module.js:456:26) at Object.Module._extensions..js (module.js:474:10) at Module.load (module.js:356:32) at Function.Module._load (module.js:312:12) at Function.Module.runMain (module.js:497:10)
After doing a bit of trial-and-error, I realized that my Mac’s file system (🔔) is not case-sensitive, but the one Heroku uses is. Thus, this line:
var twitter = require('Twitter');
Just needed to be changed to:
var twitter = require('twitter');
And everything was right as rain.
None of the actions above are particularly remarkable, but what made it interesting to me is that a seemingly innocuous change — adding the ability to create link posts to Camel — caused quite the avalanche of random changes and issues.
Despite all the pain, Camel is now considerably more robust than it was just a week ago. I’m extremely happy with where it’s ended up. The challenges were, well, challenging, but that’s exactly what makes software development so much fun.
When the dust settled, after all that work, I got this:
Top Gear Aftermath http://t.co/aySRavVYVu— Liss Is More (@caseylisscom) February 9, 2015
…and I couldn’t be happier.
Burner gives you 60 free text messages & 20 free minutes when you install; it’s an in-app purchase to get more. ↩