The problem
FriendFeed aggregates your blog posts from WordPress.com. Naturally, people prefer to comment on your post at FriendFeed – it’s quicker, easier and more fun. However, you would like to see an indication of this activity back at the original blog post.
The solutions
You could self-host your blog using software from WordPress.org. This allows you to install plugins such as FriendFeed comments. But you’re at WordPress.com because you don’t want to self-host, right? So you just have to live with the absence of useful plugins. My advice: don’t try discussing issues like this one in the WordPress.com forums unless you’re the kind of person who enjoys comment threads at YouTube.
For intelligent, mature and constructive discussion go to FriendFeed of course, where Lars writes:
How hard would it be to make a web service that reads the RSS feed from you blog, accesses FriendFeed via the API, identifies comments on FriendFeed related to your blog posts, and reposts them on your blog? If you want to, you could also keep track of “likes”…
Lets find out – using Ruby!
Disclaimer
I am a Ruby novice. This is rough and ready code, without tests or adequate error checking. Constructive and polite suggestions for improvement are welcome.
Also, this is not a complete, working solution :-)
For the impatient
To see the results, take a look at the comment by “FriendFeed Summary” on this blog post. For usage, scroll down this page.
Here’s what we want to achieve:
- Find the FriendFeed entries that correspond to our blog post
- Extract the FriendFeed comments and likes
- Post them back to WordPress.com as a comment
1. Find the FriendFeed entries that correspond to our blog post
require 'rubygems'
require 'mechanize'
require 'json'
require 'net/http'
def fetch_ff_entries(url)
ffurl = "http://friendfeed.com/api/feed/url?url=#{url}"
resp = Net::HTTP.get_response(URI.parse(ffurl))
data = resp.body
result = JSON.parse(data)
if result.has_key? 'errorCode'
raise "Web service error"
end
return result
end
We define a method, fetch_ff_entries, which takes a URL (of the blog post) as an argument. It builds a second URL (ffurl) and calls the FriendFeed API to find all FriendFeed entries related to the blog post. These are returned in JSON format. JSON.parse() converts the output to a Ruby data structure, which is returned.
To get an idea of what this JSON looks like, try:
curl "http://friendfeed.com/api/feed/url?url=http://nsaunders.wordpress.com/2009/01/28/big-data-shoot-first-ask-questions-later/" > outfile
It looks complex – it is – but it’s basically a set of keys: id, title, link, comments etc. and values: user => name, user => profileURL and so on. See the FriendFeed API documentation for the details. On conversion to a Ruby data structure we get hash keys with values that may themselves be hashes, arrays, arrays of hashes, strings and so on.
On to the next step.
2. Extract the FriendFeed comments and likes
def ff_reactions_to_wp(ff,url)
c = ""
l = Array.new
if url =~ /^(http:\/\/.*?)\//
service = $1
else
raise "Does not look like a WP blog URL?"
end
ff['entries'].each {|entry|
if entry['service']['profileUrl'] == service
# likes
if entry['likes'].length > 0
entry['likes'].each {|like|
l << "<a href=\"#{like['user']['profileUrl']}\">#{like['user']['name']}</a>"
}
end
c += "Liked by #{l.length} people: " + l[0..l.length-2].join(", ") +
" and #{l[l.length-1]}" + "\n\n"
# comments
if entry['comments'].length > 0
entry['comments'].each {|comment|
c += "<a href=\"#{comment['user']['profileUrl']}\">" +
"#{comment['user']['name']}</a> said:\n" +
"#{comment['body']}\n\n"
}
end
# ID and timestamp
c += "\n<em>submitted using ff2wp: #{entry['id']} #{entry['updated']}</em>\n"
end
}
return c
end
This method, ff_reactions_to_wp, takes two parameters: the parsed JSON output from the previous method and the blog post URL. First, it uses a regex to try and find the URL of the WordPress blog. It assumes that the root URL is contained between “http://” and the first “/”. This assumption is probably a very poor idea: it works for my blog, “nsaunders.wordpress.com” but would fail for e.g. “nsaunders.wordpress.com/blog/myposts/are/down/here”.
Second, the method loops through each entry in the JSON output and looks for the “service” key. If this matches the blog URL, we have found the aggregated blog post at FriendFeed. Note that there may be other FriendFeed items related to the blog post which are not from the blog – for example, if another user saves the post to del.icio.us.
Third, the method loops through the “likes” and the “comments”. For “likes”, an HTML-formatted string is generated by pushing user names and their FriendFeed URLs onto an array, then formatting the array elements. “Comments” are processed in a similar fashion (but an intermediate array is not required). The final comment contains the user name (linked to their profile) and the text of their comment.
Finally, a unique identifier is created using the “id” and “updated” keys from the JSON output. This can be used later on to check that we haven’t already posted this set of comments and likes.
update – the updated key doesn’t work as a timestamp; however, individual comments are dated
Put all that together and return the string (c).
3. Post them back to WordPress.com as a comment
To use these methods, you can try something like this:
usage = "Usage: ff2wp.rb WP.com_blog_post_URL"
url = ARGV[0]
if url != nil
if url =~ /^http:\/\/.*?/
ff = fetch_ff_entries(url)
post = ff_reactions_to_wp(ff,url)
puts post
else
puts usage
end
else
puts usage
end
Save the whole lot as “ff2wp.rb” and run “ff2wp.rb WP_blog_post_URL” – obviously, replacing the argument with the URL of your blog post. The code above contains a cursory and inadequate check for an argument resembling a URL. If it works, you’ll see the text of the comments, all ready for posting to a WordPress blog post.
The next step would be automated posting of the comment – and this is where I stop.
Why? I have written the code for my own use but in its current state, it allows comments to be posted to any WordPress.com blog – not just your own. In other words, it’s an excellent tool for comment spam! I’m sure that comment spammers know all the automated posting tricks, but I don’t want to be responsible for contributing another one, in public. If you’d like to see the full working code, you’re a responsible individual and I know you, send me a message.
Next steps
The ideal solution to the comment spam issue would be to write a web interface around this code, forcing the user to log in and post comments to only their own WordPress blog. Might make a rather nice Rails project.


