#!/usr/bin/env ruby # -*- mode: ruby -*- $KCODE = 'u' require 'pathname' $LOAD_PATH.unshift(Pathname($0).dirname.parent.join('lib')) require 'rubygems' require 'digest' require 'friendfeed' require 'friendfeed/unofficial' require 'main' require 'mechanize' require 'uri' require 'tempfile' require 'twitter' require 'yaml' require 'yaml/store' MYNAME = File.basename($0) TWITTER_URI = URI.parse('http://twitter.com/') def ConfigDir() $config_dir ||= begin config_dir = File.expand_path('~/.%s' % MYNAME) if !File.directory?(config_dir) Dir.mkdir(config_dir, 0700) end config_dir end end def ConfigFile() $config_file ||= File.join(ConfigDir(), 'config.yml') end def Config(keypath, default = :omitted) $config ||= YAML.load_file(ConfigFile()) keypath.split('.').inject($config) { |hash, key| hash.is_a?(Hash) or raise TypeError hash.fetch(key) } rescue => e return default if default != :omitted STDERR.print < cursor) break if result.users.nil? list.concat(result.users) cursor = result[:next_cursor] break if cursor.zero? } list end def get_profile(name) friends(:screen_name => name).first end end def twitter_client $tw_client ||= begin username = Config('twitter.username') password = Config('twitter.password') putinfo 'Logging in to Twitter as %s', username Twitter::Base.new(Twitter::HTTPAuth.new(username, password)) end end Main { description 'Twitter to FriendFeed migration helper' def run print usage.to_s end mode 'config' do description 'Edit the configuration file' def run if !File.exist?(ConfigFile()) File.open(ConfigFile(), 'w') { |f| f.print < 1000) + ffcli.get_user_discussed_entries ).sort_by { |entry| entry["published"] }.each { |entry| # Just in case this entry is not covered by the 'liked' list next if entry['likes'].any? { |like| like['user']['nickname'] == ff_me } # Is the source Twitter? url = entry['service']['profileUrl'] or next m = TWITTER_URI.route_to(url).to_s.match(%r{\A([A-Za-z0-9_]+)/statuses/([0-9]+)\z}) or next id = m[2].to_i # A different entry with the same source may be liked already next if liked.include?(id) if favorited.include?(id) entryid = entry['id'] putinfo "Adding a like in FriendFeed: %s", entry['link'] ffcli.add_like(entryid) liked << id end } end end mode 'replies' do description 'Produce an RSS feed of Twitter @replies' option('include-friends') { description 'Include @reples from friends' cast :bool default false } option('include-protected') { description 'Include @reples from protected users' cast :bool default false } argument('filename') { description 'Output RSS to this file' } def run require 'nokogiri' require 'rss' require 'set' require 'time' include_friends = params['include-friends'].value include_protected = params['include-protected'].value filename = params['filename'].value File.open(filename, 'w') { |w| feed = RSS::Maker.make("2.0") { |rss| rss.channel.title = 'Twitter replies' rss.channel.link = 'http://twitter.com/replies' rss.channel.description = 'Twitter replies' friends = Status('friends').to_set twitter_client().mentions.each { |reply| user = reply.user next if !include_protected && user.protected name = user.screen_name next if !include_friends && friends.include?(name.downcase) text = '%s: %s' % [name, reply.text] url = 'http://twitter.com/%s/statuses/%d' % [name, reply.id] timestamp = Time.parse(reply.created_at) rss.items.new_item { |item| item.title = Nokogiri.HTML(text).inner_text item.link = url item.description = text item.date = timestamp } } } w.print feed.to_s } end end mode 'protected' do description 'Produce an RSS feed for Twitter entries from protected friends' argument('filename') { description 'Output RSS to this file' } def run require 'nokogiri' require 'rss' require 'set' require 'time' filename = params['filename'].value friends = Status('friends').to_set friends_subscribed_real = Status('friends_subscribed_real').to_set items = [] twitter_client().mentions.each { |reply| user = reply.user next if !user.protected name = user.screen_name next if friends.include?(name.downcase) text = '[%s]: %s' % [name, reply.text] url = 'http://twitter.com/%s/statuses/%d' % [name, reply.id] timestamp = Time.parse(reply.created_at) items << [timestamp, text, url] } twitter_client().friends_timeline.each { |status| user = status.user next if !user.protected name = user.screen_name next if friends_subscribed_real.include?(name.downcase) text = '[%s]: %s' % [name, status.text] url = 'http://twitter.com/%s/statuses/%d' % [name, status.id] timestamp = Time.parse(status.created_at) items << [timestamp, text, url] } File.open(filename, 'w') { |w| feed = RSS::Maker.make("2.0") { |rss| rss.channel.title = 'Twitter entries from protected friends' rss.channel.link = 'http://twitter.com/home' rss.channel.description = 'Twitter entries from protected friends' items.sort { |a, b| b.first <=> a.first }.each { |timestamp, text, url| rss.items.new_item { |item| item.title = Nokogiri.HTML(text).inner_text item.link = url item.description = text item.date = timestamp } } } w.print feed.to_s } end end mode 'refresh' do description 'Urge FriendFeed to refresh services (import feed entries)' def run ffcli = friendfeed_client() profile = ffcli.get_profile ffcli.get_services.each { |service| putinfo "Refreshing %s..." % service['service'] ffcli.refresh_service(profile['id'], service['serviceid'], service['service']) } end end }