MySpace, my contacts: the story behind Knx.to’s seventh social network

 +  

We’re glad to announce that Knx.to “connects to” MySpace! You can now add up to 400 of your top friends from MySpace to your own real-time social address book. By indexing their profiles along with all of your other contacts from Facebook, Twitter, LinkedIn, Flickr, Google Mail, and Yahoo! Mail, you can pull together a complete picture of your connections’ photos, statuses, and other activities before you call, write, or text them.

We used the MySpaceID JavaScript Library (JSL), one of the MySpace Platform’s newest tools, to protect the privacy of this information by delivering it directly to your browser. For anyone who wants to take it for a spin, head on over to http://knx.to/ — but if you’re a developer who’d like to learn a few of the more arcane tips and tricks to getting this working, read on…

At first glance, MySpace offers a daunting range of APIs and tools accumulated in support of a slew of open standards and other initiatives over the last several years. The most common types of applications are games and quizzes that run on-site using the OpenSocial standards (yes, plural, since some of their APIs are 0.7, 0.8, 0.9, and as of last week, some will also be based on 1.0 as well as custom extensions). Off-site integration opportunities are another emerging category, including authentication (OpenID and MySpaceID), real-time stream access (Streams), lifestream aggregation (Activity Streams), synchronization with other social networks (Twitter Sync), and access to address books (Portable Contacts). To keep track of all this, there’s also a passel of testing tools (OpenID, OAuth, OpenSocial 0.7 & 0.8, JSL) and even more new APIs on the way (Analytics). And if you’re keeping score at home, there’s sample code in PHP, C#, JavaScript, Ruby, and more.

The JSL is analogous to Facebook Connect, in that it enables sites to work with MySpace without installing anything more complex than an HTML file on the server and adding a <SCRIPT> tag. However, the sample code doesn’t actually work, so you’ll need to patch around those errors:

  • Ignore the curly braces surrounding the consumerKey and targetDomain when replacing lines 4 and 5. Same goes for line 39 – not only does it reference a nonexistent file, but that call will now fail because the link doesn’t begin with http:
  • Note that while Facebook Connect accepts any complete URL for your cross-domain receiver file (so you could rename xd_receiver.html as you please), the MySpace JSL insists on appending the suffix /RelayReceiver.html to whatever your targetDomain is set to (hence, targetDomain also isn’t actually a DNS domain name, but more properly understood to be an URL prefix).
  • Speaking of which, you actually have to go create a RelayReceiver.html file yourself: cut-and-paste it from Step 6 of the Getting Started docs. If you’re using Firebug, you won’t notice the hidden IFRAME anywhere in the DOM, because it’s only inserted on-demand.
  • There’s a missing parenthesis at the end of line 15: close the alert() function call.
  • If you’re getting cryptic numeric alerts, you may want to replace line 19 with MSID.Connect.Enums.getStatusMessage(response.statusCode), which provides plainer English translations.
  • Posting to an activity stream won’t work until you go to the MySpace Developer site and create corresponding templates in their system before referencing them. For example, we created one imaginatively named Template_Name with one variable, body to replace postActivity() with:

    params[opensocial.Activity.Field.TITLE_ID] = "Template_Name";
    params[opensocial.Activity.Field.TEMPLATE_PARAMS] = {"body":prompt('body')};
    var mediaItemArray = [];
    mediaItemArray.push(opensocial.newMediaItem("", "http://api.myspace.com/v1/users/329669099"));
    params[opensocial.Activity.Field.MEDIA_ITEMS] = mediaItemArray;

  • Delete the stray close-parenthesis on line 97.
  • There is no access to the “About Me” profile field to begin with, so line 107 will always pop-up as undefined. Consider something like this instead: alert("id: "+id+" nickname: "+nickname);
  • dataReq on line 116 must be renamed dr to work. Nor is there an idArray for use in the loop following on line 117, so consider inserting your own user id, like so: var idArray = ['myspace.com:329669099'];
  • On line 128, there’s the reverse of the same error: rename dr to be dataReq here.
  • Finally, on line 158, there isn’t actually a downloadable logo for MySpaceID logins (or for any other MySpace branding elements, surprisingly), so I referred to an image hosted by our colleagues over at SocialAmp.com: <img alt="Login with MySpaceID" src="http://www.socialamp.com/img/stories/myspaceid.png"/>. The MySpaceID C# SDK hosted at Google Code includes some useful artwork on its home page, too.

Once you’ve made all those changes, well, now you can test a fraction of what you see in the live demo app hosted on Google AppEngine — that app’s source code is another valuable source of reusable code snippets (and probably a better starting point than MySpace’s official sample).

From here, we proceeded to build a tail-recursive loop for iteratively paging through your lists of friends, which is unfortunately capped at 40 results per API call, making it prohibitive to manipulate large graphs without Knx.to’s client-side caching technology.

Once data’s being cached, though, it needs to be revalidated, and that ended up consuming the vast majority of our development time on this project. You would expect that a READY callback means that an app can start issuing API calls. Not quite — MySpace’s tryConnect() function returns that exact same statusCode for two entirely distinct conditions: when the JSL library has been loaded; and when a popup-login box has succeeded. Worse, the callback function supplied to tryConnect gets called more than once: when calling requestLogin() to draw a pop-up sign in dialog box, that calls tryConnect() once more internally and ends up calling your own callback function too.

So if you’d think “on a READY callback, start asking for friends” you’d be wrong. Instead, you might resort to MSID.Connect.registerConnectHandler(), which sounds like it ought to fire once, when there's a valid session cookie, but it doesn’t work on return visits to the same page (and don’t copy from the docs: it's mistakenly spelled requestConnectHandler on the very next line!).

We ended up having to write a layer of code that works like Facebook Connect’s handy ensureInit() and requireSession() guards, which defer execution of code blocks until the corresponding condition is true — and only executes it once. Our workaround includes ugly hacks such as a special flag to discard the first callback after a login pop-up, for example.

A final note: make sure to pay attention to the “External Site Settings” on the API Key configuration (when it’s up :). You need to check the box, ignore the incorrect references to separate Protocol and Domain boxes in the Validation field, and precisely copy the targetDomain path, trailing slashes and all — or you’ll get a confusing “Oops!” dialog box instead of a login form.