Web Fonts

At work we have what's called "Brown Bag Lunch", where a co-worker will give a talk about some emerging tech or strategy, or an age old thing, like someone once did Brainstorming. On Friday, our designer gave a talk about Web Fonts, and I decided to try to find one on Google Fonts that made my headers look good. I went with Mate SC. It's pretty nice.

Habits

My variable/class/filename/etc naming habits have changed recently, I'd say in the past year. Previously, I'd name CSS classes, html element ids, etc, in camel case. But I've moved towards hyphen delimited.

Old way: someObjectNameOrID

New way: some-object-name-or-id

This is for file names as well. It's just one of those transitions that takes place where I'm not sure how I feel about it, and I definitely haven't taken the time to think about the possible repercussions. I'm just going with the flow. If I could name C# classes that way, I probably would.

The Power of Runtime File Combining

This can apply to any language and architecture, but I've been applying it in my Node.js programming. The idea of combining is to have all of your resources separated for development, but combined at runtime since the browser doesn't care about your structure, and fewer files is fewer requests, so the page loads faster. I did this with javascript at first, but was later faced with a huge css file, and decided to take the same approach.

So you have a tag or control or whatever your dev environment provides, where you pass it all of the files you would like to combine. On the server, if the combine file doesn't exist, you create it, writing the contents of all of the files to that single file, then you write out the html tag that points to the combined file on the server, with the correct MIME type tag (script tag for js, style or link tag for css, etc).

The "do it at runtime" version of this method takes the same list of files, but checks the modified date of each file, and compares it to the modified date of the resulting, combined file. If there's a newer version of any of the files, you overwrite the combined file. Then you write out the tag with the millisecond representation of the date modified of the combined file appended to the querystring of the file! It might look like this:

<style src="/css/combine/style.css?t=347483929" />

I'm typing on my iPad, otherwise I'd have code samples and maybe correct html syntax, if that's not correct... I don't even know :)

Posting to Twitter with Node.js

I have a site, givit.me, where people can post stuff that they don't need or want, and sell it or give it away (hence the name). The site has yet to take off, but with social sharing, and cheapness of hosting, it can pretty much stay up there forever without costing a lot and with minimal use. Although we hope it will take off.

I wanted to take some pain out of doing social sharing, so posting items automatically to twitter seemed like a good step to take. Of course, with my paradigm shift into Node.js and MongoDB, and it being fairly new across the board, I would have to write it myself, with the added benefit of being able to share how it was done!

Twitter's API documentation is pretty good, as far as API documentation goes. Their OAuth docs are on par with Google's and Facebook's (I use both of those on the site as well, as they are OAuth 2.0 and 2.0 is generally much easier than 1.0). All of their documentation can be found on dev.twitter.com, and that's also where you would create and manage your app, app keys and secrets, etc.

Data You'll Need

Now, completely ignoring the rate limiting part of the Twitter API, we can write a pretty straightforward method to send status updates as the user you created the application under. You can get the access tokens right in the Details section of your application. You'll need the access token and access token secret, as well as the consumer key and the consumer secret key. Don't give these to anyone!

Details Tab

The hardest part about OAuth 1.0 is generating the signature correctly. It's not that hard, either, since twitter provides excellent documentation on creating the signature. The base string used for the signature is created like this

The Code

These are the only requires you'll require (heh)

var https = require("https"), querystring = require("querystring"), crypto=require("crypto"); var oauthData = { oauth_consumer_key: consumerKey, oauth_nonce: nonce, oauth_signature_method: "HMAC-SHA1", oauth_timestamp: timestamp, oauth_token: accessToken, oauth_version: "1.0" }; var sigData = {}; for (var k in oauthData){ sigData[k] = oauthData[k]; } for (var k in data){ sigData[k] = data[k]; }

Here we're gathering up all of the data passed to create the tweet (status, lat, long, other parameters), and also the oauthData minus the signature. Then we do this:

var sig = generateOAuthSignature(url.method, "https://" + url.host + url.path, sigData); oauthData.oauth_signature = sig;

Which calls this function

function generateOAuthSignature(method, url, data){ var signingToken = urlEncode(consumerSecret) + "&" + urlEncode(accessSecret); var keys = []; for (var d in data){ keys.push(d); } keys.sort(); var output = "POST&" + urlEncode(url) + "&"; var params = ""; keys.forEach(function(k){ params += "&" + urlEncode(k) + "=" + urlEncode(data[k]); }); params = urlEncode(params.substring(1)); return hashString(signingToken, output+params, "base64"); } function hashString(key, str, encoding){ var hmac = crypto.createHmac("sha1", key); hmac.update(str); return hmac.digest(encoding); }

With this function, you can successfully generate the signature. The next part is passing the OAuth headers correctly. I simply do this:

var oauthHeader = ""; for (var k in oauthData){ oauthHeader += ", " + urlEncode(k) + "=\"" + urlEncode(oauthData[k]) + "\""; } oauthHeader = oauthHeader.substring(1);

And then create the request and pass it along on the request like this:

var req = https.request(url, function(resp){ resp.setEncoding("utf8"); var respData = ""; resp.on("data", function(data){ respData += data; }); resp.on("end", function(){ if (resp.statusCode != 200){ callback({error: resp.statusCode, message: respData }); } else callback(JSON.parse(respData)); }); }); req.setHeader("Authorization", "OAuth" + oauthHeader); req.write(querystring.stringify(data)); req.end();

Twitter Limits

There are other checks, like when you include a URL in the tweet text, you'll need to see that your text plus the length of the generated t.co URL doesn't exceed 140 characters. That URL length will change fairly infrequently, and slower and slower as time goes on, since more URLs can be generated with just 1 more character added. This data is available though. I have another function that gets the configuration from twitter, and passes that along to my function that actually generates tweets from the database.

function getHttpsNonAuthJSON(host, path, query, callback){ var url = { host: host , path: path }; if (query != null) url.path = url.path + "?" + querystring.stringify(query); https.get(url, function(resp){ resp.setEncoding("utf8"); var respData = ""; resp.on("data", function(data){ respData += data; }); resp.on("end", function(){ callback(JSON.parse(respData)); }); }); }

This is a general function to get any non-authenticated https request and perform a callback with a JS object. I might call it like this, for example

getHttpsNonAuthJSON("api.twitter.com", "/1/help/configuration.json", null, function(config){ console.log(config.short_url_length_http); });

For some completeness, this is the complete function that makes POST requests with OAuth

function postHttpsAuthJSON(host, path, data, nonce, callback){ var url = { host: host, path: path, method: "POST" }; var timestamp = Math.floor(new Date().getTime() / 1000); var oauthData = { oauth_consumer_key: consumerKey, oauth_nonce: nonce, oauth_signature_method: "HMAC-SHA1", oauth_timestamp: timestamp, oauth_token: accessToken, oauth_version: "1.0" }; var sigData = {}; for (var k in oauthData){ sigData[k] = oauthData[k]; } for (var k in data){ sigData[k] = data[k]; } var sig = generateOAuthSignature(url.method, "https://" + url.host + url.path, sigData); oauthData.oauth_signature = sig; var oauthHeader = ""; for (var k in oauthData){ oauthHeader += ", " + urlEncode(k) + "=\"" + urlEncode(oauthData[k]) + "\""; } oauthHeader = oauthHeader.substring(1); var req = https.request(url, function(resp){ resp.setEncoding("utf8"); var respData = ""; resp.on("data", function(data){ respData += data; }); resp.on("end", function(){ if (resp.statusCode != 200){ callback({error: resp.statusCode, message: respData }); } else callback(JSON.parse(respData)); }); }); req.setHeader("Authorization", "OAuth" + oauthHeader); req.write(querystring.stringify(data)); req.end(); }

Thanks for reading! I hope this helps you out. A reminder, this is just for posting as a single user, it doesn't go get the auth token or do any other of the handshaking that is needed for OAuth. Twitter generates an access token and a access secret token for use with the user that created the application, and you can do calls to the API with that, as that user.