Have you ever wondered which GitHub developers you're following don't reciprocate the connection? Unlike social media platforms that clearly show mutual connections, GitHub doesn't provide an easy way to see who follows you back. This article explains a fun Ruby script that solves this problem by revealing your one-sided GitHub relationships.
The Social Imbalance on GitHub
GitHub, while primarily a code-hosting platform, also functions as a social network for developers. You can follow other developers to stay updated on their activities, contributions, and projects. However, GitHub's interface doesn't explicitly show whether someone you follow also follows you back.
This asymmetry creates an interesting dynamic - you might be following hundreds of developers, but how many of them are following you in return? The script we're discussing today bridges this information gap.
The Script
#!/usr/bin/env ruby
require 'net/http'
require 'json'
require 'optparse'
# Class that handles checking GitHub follower relationships
class GitHubFollowerChecker
BASE_URL = "https://api.github.com"
# Initialize with username and optional authentication token
def initialize(username, token = nil)
@username = username
@token = token
@headers = { 'Accept' => 'application/vnd.github.v3+json' }
@headers['Authorization'] = "token #{token}" if token
end
# Get all users the specified user is following
def get_following
fetch_all_pages("#{BASE_URL}/users/#{@username}/following")
end
# Get all followers of the specified user
def get_followers
fetch_all_pages("#{BASE_URL}/users/#{@username}/followers")
end
# Find users you follow who don't follow you back
def find_not_following_back
puts "Fetching users you follow..."
following = get_following
puts "Fetching your followers..."
followers = get_followers
following_usernames = following.map { |user| user['login'] }
follower_usernames = followers.map { |user| user['login'] }
puts "Analyzing relationships..."
following_usernames - follower_usernames
end
private
# Helper method to fetch all pages of results from the GitHub API
def fetch_all_pages(url, results = [])
uri = URI(url)
begin
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
request = Net::HTTP::Get.new(uri)
@headers.each { |key, value| request[key] = value }
http.request(request)
end
case response.code
when '200'
page_results = JSON.parse(response.body)
results.concat(page_results)
# Handle pagination using the Link header
if response['link']&.include?('rel="next"')
next_link = response['link'].split(',').find { |link| link.include?('rel="next"') }
if next_link
next_url = next_link.match(/<(.+?)>/)[1]
print "." # Show progress
fetch_all_pages(next_url, results)
end
end
when '401'
puts "\nError: Authentication failed. Check your token."
exit 1
when '403'
puts "\nError: Rate limit exceeded. Use a token or wait before trying again."
exit 1
when '404'
puts "\nError: User '#{@username}' not found."
exit 1
else
puts "\nError: #{response.code} - #{response.message}"
puts JSON.parse(response.body)['message'] if response.body
exit 1
end
rescue SocketError
puts "\nError: Could not connect to GitHub. Check your internet connection."
exit 1
rescue JSON::ParserError
puts "\nError: Invalid response from GitHub."
exit 1
end
results
end
end
# Main script execution
def main
# Parse command line options
options = {}
OptionParser.new do |opts|
opts.banner = "Usage: github_follower_checker.rb -u USERNAME [-t TOKEN]"
opts.on("-u", "--username USERNAME", "GitHub username") do |username|
options[:username] = username
end
opts.on("-t", "--token TOKEN", "GitHub personal access token (recommended to avoid rate limits)") do |token|
options[:token] = token
end
opts.on("-h", "--help", "Show this help message") do
puts opts
exit
end
end.parse!
# Validate required parameters
if options[:username].nil?
puts "Error: GitHub username is required"
puts "Usage: github_follower_checker.rb -u USERNAME [-t TOKEN]"
exit 1
end
# Execute the check
checker = GitHubFollowerChecker.new(options[:username], options[:token])
not_following_back = checker.find_not_following_back
# Display results
puts "\nGitHub users you follow who don't follow you back (#{not_following_back.count}):"
if not_following_back.empty?
puts "Everyone you follow also follows you back! 🎉"
else
not_following_back.each_with_index do |username, index|
puts "#{index + 1}. #{username}"
end
end
end
# Run the script
main if __FILE__ == $PROGRAM_NAME
Understanding the Ruby Script
The script is a command-line tool written in Ruby that identifies GitHub users you follow who aren't following you back. Let's break down how it works and the technology behind it:
Core Functionality
The script performs four main tasks:
- Fetches the list of users you're following
- Fetches your followers
- Compares these two lists to identify non-reciprocal connections
- Presents the results in a clear, easy-to-read format
Technical Implementation
The code is organized around a GitHubFollowerChecker
class that handles the GitHub API interactions:
class GitHubFollowerChecker
BASE_URL = "https://api.github.com"
def initialize(username, token = nil)
@username = username
@token = token
@headers = { 'Accept' => 'application/vnd.github.v3+json' }
@headers['Authorization'] = "token #{token}" if token
end
# Other methods...
end
Fetching Data from GitHub
The script uses Ruby's Net::HTTP
library to make API calls to GitHub's endpoints:
def get_following
fetch_all_pages("#{BASE_URL}/users/#{@username}/following")
end
def get_followers
fetch_all_pages("#{BASE_URL}/users/#{@username}/followers")
end
Handling Pagination
GitHub's API returns paginated results when dealing with large data sets. The script handles this elegantly by recursively fetching all pages:
if response['link']&.include?('rel="next"')
next_link = response['link'].split(',').find { |link| link.include?('rel="next"') }
if next_link
next_url = next_link.match(/<(.+?)>/)[1]
print "." # Show progress
fetch_all_pages(next_url, results)
end
end
Finding Non-Reciprocal Connections
The core analysis is remarkably simple but effective:
def find_not_following_back
puts "Fetching users you follow..."
following = get_following
puts "Fetching your followers..."
followers = get_followers
following_usernames = following.map { |user| user['login'] }
follower_usernames = followers.map { |user| user['login'] }
puts "Analyzing relationships..."
following_usernames - follower_usernames
end
The magic happens in the last line, where Ruby's array subtraction operator (-
) does all the heavy lifting, returning elements in the first array that aren't in the second.
Error Handling
The script includes robust error handling for various scenarios:
case response.code
when '401'
puts "\nError: Authentication failed. Check your token."
exit 1
when '403'
puts "\nError: Rate limit exceeded. Use a token or wait before trying again."
exit 1
# Other cases...
end
Why This Script Is Useful
While created for fun, this script offers several practical benefits:
- Network Analysis: Understand the reciprocity in your professional network
- Community Engagement: Identify potential opportunities to build stronger connections
- Account Cleanup: Find inactive or unengaged connections you might want to reconsider
- GitHub API Learning: Serves as an excellent example of working with GitHub's API
Running the Script
Using the script is straightforward:
- Save the code as
github_follower_checker.rb
- Make it executable:
chmod +x github_follower_checker.rb
- Run with your username:
./github_follower_checker.rb -u YOUR_USERNAME
For better performance and to avoid rate limits:
./github_follower_checker.rb -u YOUR_USERNAME -t YOUR_PERSONAL_ACCESS_TOKEN
The output will show you a list of users you follow who don't follow you back:
GitHub users you follow who don't follow you back (42):
1. octocat
2. defunkt
3. torvalds
...
Enhancing Your GitHub Experience
This script demonstrates how a simple tool can unveil insights about your GitHub connections that aren't readily available through the platform's interface. It's a perfect example of using programming to enhance your experience on a platform you use regularly.
While GitHub may not emphasize the reciprocity of connections as much as traditional social networks do, understanding these relationships can help you build a more engaged and meaningful developer network.
Conclusion
The GitHub follower analysis script is both a fun project and a useful tool for developers active on the platform. It showcases how a few lines of Ruby code can provide valuable insights into your GitHub social graph, helping you make more informed decisions about who you connect with in the developer community.
Whether you're looking to clean up your GitHub connections or just curious about the reciprocity of your network, this script offers a simple solution to a question many GitHub users have pondered.
Remember that GitHub is primarily about code, collaboration, and learning - but the social aspect adds another dimension to your professional development journey. Happy coding, and happy networking!