Skip to content

Instantly share code, notes, and snippets.

@natarius
Last active December 13, 2015 18:39
Show Gist options
  • Save natarius/4957319 to your computer and use it in GitHub Desktop.
Save natarius/4957319 to your computer and use it in GitHub Desktop.
Nice little snipped to get your users friends from Facebook/Twitter and merge/autocomplete them. It will also notify the users that their friends signed up via email or whatever you want to implement. It can group does notifications in batches, to create waves of people who know each other and be notified in a short timespan :) Requires a workin…
# encoding: UTF-8
class Friend
include Mongoid::Document
include Mongoid::Timestamps
field :fb_id
field :twitter_id, :type => Integer
field :email
field :twitter_name
field :fb_name
field :phone
field :first_name
field :last_name
field :profile_picture
field :gender
field :birthday, :type => Date
field :locale
field :website
field :timezone, :type => Integer, :default => 0
field :twitter_followers, :type => Integer, :default => 0
field :twitter_following, :type => Integer, :default => 0
field :send_signup_notification, :type => Boolean, :default => false
belongs_to :user, index: true
index fb_id: 1
index twitter_id: 1
index send_signup_notification: 1
index({user_id: 1, twitter_followers: 1}, {background: true})
def self.import_from_twitter(user, friend_ids)
Parallel.map(friend_ids, :in_threads=>(Rails.env.production? ? 4 : 0)).each do |friend_id|
begin
Twitter.oauth_token = user.twitter_token
Twitter.oauth_token_secret = user.twitter_secret
twitter_user = Twitter.user(friend_id.to_i)
new_friend = user.friends.where(:twitter_id => nil,
:website => "/(.*#{twitter_user.url}.*|.*#{twitter_user.screen_name}.*)/i").first
new_friend = user.friends.where(:twitter_id => nil,
:first_name => "/.*#{TwitterHelper.new.get_first_name(twitter_user.name)}.*/i",
:last_name => "/.*#{TwitterHelper.new.get_last_name(twitter_user.name)}.*/i").first if !new_friend
new_friend = user.friends.find_or_initialize_by(:twitter_id => friend_id) if !new_friend
new_friend.twitter_name = twitter_user.screen_name
new_friend.first_name = TwitterHelper.new.get_first_name(twitter_user.name)
new_friend.last_name = TwitterHelper.new.get_last_name(twitter_user.name)
new_friend.locale = twitter_user.lang
new_friend.website = twitter_user.url
new_friend.timezone = TwitterHelper.new.translate_utc_offset(twitter_user)
new_friend.profile_picture = twitter_user.profile_image_url
new_friend.twitter_followers = twitter_user.followers_count.to_i
new_friend.twitter_following = twitter_user.friends_count.to_i
new_friend.save
rescue => e
Rails.logger.error "Error importing twitter friend. Msg: #{e}"
end
end
end
def self.import_from_facebook(user, friends)
Parallel.map(friends, :in_threads=>(Rails.env.production? ? 4 : 0)) do |friend|
begin
fb_user = FbGraph::User.new(friend.identifier, :access_token => user.fb_token).fetch
new_friend = user.friends.where(:fb_id => nil,
:website => "/(#{fb_user.website.split("/n").map {|elem| ".*#{elem}.*" }.join '|'})/i").first if !fb_user.website.blank?
new_friend = user.friends.where(:fb_id => nil,
:first_name => "/.*#{fb_user.first_name}.*/i",
:last_name => "/.*#{fb_user.last_name}.*/i").first if !new_friend
new_friend = user.friends.find_or_initialize_by(:fb_id => friend.identifier) if !new_friend
new_friend.fb_name = fb_user.username
new_friend.first_name = fb_user.first_name
new_friend.last_name = fb_user.last_name
new_friend.email = fb_user.email
new_friend.gender = fb_user.gender
new_friend.birthday = fb_user.birthday
new_friend.locale = fb_user.locale
new_friend.website = fb_user.website
new_friend.timezone = fb_user.timezone.to_i
new_friend.profile_picture = fb_user.picture
new_friend.save
rescue => e
Rails.logger.error "Error importing facebook friend. Msg: #{e}"
end
end
end
#instance classes
def get_user
User.where(:fb_user_id => self.fb_id).first
end
def get_full_name
"#{self.first_name} #{self.last_name}"
end
def has_account?
!self.get_user.nil?
end
def has_provider?(provider)
case provider
when "twitter"
return !self.fb_id.blank?
when "facebook"
return !self.twitter_id.blank?
else
false
end
end
end
module User::FriendSignupMethods
def self.included(base)
base.instance_eval do
extend ClassMethods
has_many :friends, dependent: :destroy
after_create :get_friends
end
end
def get_friends
Resque.enqueue(GetFriends, self.id)
end
def import_facebook_friends
return if !self.is_fb_connected?
begin
friends = get_fb_friends(self)
Friend.import_from_facebook(self, friends)
rescue => e
Rails.logger.error "Error importing facebook friends. Msg: #{e}"
Rails.logger.error e.backtrace.join("\n")
end
end
def import_twitter_friends
return if !self.is_twitter_connected?
begin
Twitter.oauth_token = self.twitter_token
Twitter.oauth_token_secret = self.twitter_secret
friend_ids = TwitterHelper.new.get_all_twitter_friends(self) + TwitterHelper.new.get_all_twitter_followers(self)
Friend.import_from_twitter(self, friend_ids)
rescue => e
Rails.logger.error "Error importing twitter friends. Msg: #{e}"
end
end
def notify_friends
selected_users = self.get_app_friends(true)
batch_size = 2 # group users in batches to create waves
if selected_users.size > batch_size
Rails.logger.info "Sending notifications because batch size big enough!"
selected_users.each do |selected_user|
user = selected_user[:user]
friend = selected_user[:friend]
Resque.enqueue_in(Random.new.rand(1..720).minutes,
SendFriendSignupNotification, user.id, self.id, friend.id)
end
else
Rails.logger.info "Not sending notifications because batch size too small!"
end
end
def notify_user_friend_signed_up(friend)
result = false
begin
Rails.logger.info "Sending signup notification to: #{self.name}"
UserMailer.friend_signup(self, friend).deliver
result = true
rescue => e
Rails.logger.error "Error notifying user about friend signup. Msg: #{e}"
end
result
end
private
def get_fb_friends(user)
fb_user = FbGraph::User.me(user.fb_token).fetch
fb_friends = fb_user.friends
fb_subscribers = fb_user.subscribers
friends = []
begin
fb_friends.each do |fb_friend|
friends << fb_friend
end
end while fb_friends.next.present? && fb_friends = fb_friends.next
begin
fb_subscribers.each do |fb_subscriber|
friends << fb_subscriber
end
end while fb_subscribers.next.present? && fb_subscribers = fb_subscribers.next
friends
end
def get_app_friends(without_signup_notification = false)
selected_users = []
if without_signup_notification
selected_friends = self.friends.where(:send_signup_notification => false)
else
selected_friends = self.friends
end
users = User.all
selected_friends.each do |friend|
fb_based_user = users.where(:fb_user_id => friend.fb_id).first if !friend.fb_id.blank?
twitter_based_user = users.where(:twitter_id => friend.twitter_id).first if !friend.twitter_id.blank? and !fb_based_user
if fb_based_user and fb_based_user.id != self.id
selected_users << {:user => fb_based_user, :friend => friend}
elsif twitter_based_user and twitter_based_user.id != self.id
selected_users << {:user => twitter_based_user, :friend => friend}
end
end
selected_users
end
module ClassMethods
def notify_friends!
begin
User.all.only(:id).each do |user|
Resque.enqueue(FriendSignupNotifier, user.id)
end
rescue => e
Rails.logger.error "Error notifying friends. Msg: #{e}"
end
end
end
end
module FriendSignupNotifier
@queue = :low
def self.perform(user_id)
user = User.where(:_id => user_id).first
if user
user.notify_friends
end
end
end
gem 'resque'
gem 'resque-scheduler', "~> 2.0.0", :require => 'resque_scheduler'
gem 'parallel', "~> 0.5.16"
gem 'fb_graph', "~> 2.4.4"
gem "twitter"
gem 'mongoid'
module GetFriends
@queue = :low
def self.perform(user_id)
user = User.where(:_id => user_id).first
if user
user.import_twitter_friends
user.import_facebook_friends
end
end
end
module SendFriendSignupNotification
@queue = :low
def self.perform(receiver_user_id, sender_user_id, receiver_friend_id)
receiver_user = User.where(:_id => receiver_user_id).first
sender_user = User.where(:_id => sender_user_id).first
receiver_friend = Friend.where(:_id => receiver_friend_id).first
return if receiver_friend.send_signup_notification or !sender_user.has_quotes?
receiver_friend.send_signup_notification = receiver_user.notify_user_friend_signed_up(sender_user)
receiver_friend.save
end
end
include User::FriendSignupMethods
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment