Skip to content

Instantly share code, notes, and snippets.

@sklppr
Created September 13, 2012 15:26
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sklppr/3715073 to your computer and use it in GitHub Desktop.
Save sklppr/3715073 to your computer and use it in GitHub Desktop.
Fetches info about the people you follow on Twitter and determines who you could/should unfollow.
# encoding: utf-8
#####################################################################################
#
# Fetches info about the people you follow on Twitter
# and determines who you could/should unfollow based on:
# - activity: was their last update more than 3 months ago?
# - popularity: do they have less than 25 followers?
# - mass following: are they following more than 10000 people?
# - friends-to-follower ratio: are less than 50% following them back?
# This might be incompatible with AP v1.1 and no longer work once v1.0 is shut down.
#
# This uses the public REST API with a sliding window of 180 requests.
# Docs: https://dev.twitter.com/docs/api/
# The actual usage is configurable via the limit argument:
# The script will load friends 0 through {limit}, default is 100.
# If you have a lots of friends you'll have to slowly increase
# this threshold and wait inbetween in order to stay below the threshold.
# Once loaded, all JSON data is cached in "twitter-friends-data".
#
# Usage: ruby twitter-friends.rb USERNAME [LIMIT]
# - USERNAME: Your Twitter username.
# - LIMIT: How many friends to load. Default is 100.
#
# Author: github.com/sklppr // twitter.com/sklppr
#
#####################################################################################
require 'progressbar'
require 'open-uri'
require 'json'
require 'date'
DATA_DIR = "twitter-friends-data"
DEFAULT_LIMIT = 100
MAX_STATUS_AGE = 180
MIN_FOLLOWERS = 25
MAX_FRIENDS = 10_000
MIN_FOLLOWING_RATIO = 0.5
username = ARGV[0]
if username.nil?
puts "Usage: ruby twitter-friends.rb USERNAME [LIMIT]"
puts "- USERNAME: Your Twitter username."
puts "- LIMIT: How many friends to load. Default is 100."
Kernel.exit!
end
batch = ARGV[1].to_i || DEFAULT_LIMIT
Dir.mkdir DATA_DIR unless Dir.exists? DATA_DIR
user_file = "#{DATA_DIR}/#{username}.json"
if File.exists? user_file
json = open(user_file).read
else
json = open("http://api.twitter.com/1/friends/ids.json?screen_name=#{username}").read
File.open(user_file, "w") { |file| file.write(json) }
end
friends = JSON.parse(json)["ids"]
friends_count = friends.count
batch = [batch, friends_count].min
puts "\nAnalyzing the first #{batch} of #{username}’s #{friends_count} friends.\n\n"
if friends_count > 0
today = Date.today
Infinity = 1.0/0
output = "\n"
progress = ProgressBar.new("Analyzing", batch)
friends.slice(0, batch).each do |id|
begin
friend_file = "#{DATA_DIR}/#{id}.json"
if File.exists? friend_file
json = open(friend_file).read
else
json = open("http://api.twitter.com/1/users/show.json?id=#{id}").read
File.open(friend_file, "w") { |file| file.write(json) }
end
friend = JSON.parse(json)
screen_name = friend["screen_name"]
last_status = friend["status"]
last_status = Date.parse(last_status.nil? ? friend["created_at"] : last_status["created_at"])
followers_count = friend["followers_count"]
friends_count = friend["friends_count"]
status_age = (today - last_status).to_i
following_ratio = if friends_count > 0 then Rational(followers_count, friends_count).to_f.round(2) else Infinity end
output << "#{screen_name} has been inactive for #{status_age} days.\n" if status_age > MAX_STATUS_AGE
output << "#{screen_name} only has #{followers_count} followers.\n" if followers_count < MIN_FOLLOWERS
output << "#{screen_name} is following #{friends_count} people.\n" if friends_count > MAX_FRIENDS
output << "#{screen_name} has a followers/friends ratio of #{followers_count}/#{friends_count} = #{following_ratio}.\n" if following_ratio < MIN_FOLLOWING_RATIO
rescue OpenURI::HTTPError
output << "#{id}: HTTP Error\n"
rescue IOError
output << "#{id}: File Error\n"
rescue JSON::ParserError
output << "#{id}: JSON Error\n"
ensure
progress.inc
end
end
progress.finish
puts output + "\n"
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment