Instantly share code, notes, and snippets.

Embed
What would you like to do?
A twitter bot, here designed to work with some active record models, but the logic is there.
require 'twitter'
require 'logger'
# 🐵 patch search results, because they sucks
module Twitter
class SearchResults
attr_reader :rate_limit
def initialize(request)
@client = request.client
@request_method = request.verb
@path = request.path
@options = request.options
@collection = []
@rate_limit = request.rate_limit
self.attrs = request.perform
end
end
end
#
# Thaaat's my boy 😊
#
module Evil
class Bot
FRIEND_MAX_COUNT = 1500
PUBLISH_HOURS = [20]
LOG_LEVEL = Logger::DEBUG
RETWEET_MIN = 50
FAVORITE_MIN = 30
DO_NOT_TRACK = [1344745638, 2818911550]
DEBUG = Rails.env.development?
LOG_PATH = './evil_bot.log'
def initialize(screen_name)
@l = Logger.new(LOG_PATH, 'monthly')
@l.level = LOG_LEVEL
@screen_name = screen_name
@account = Account.where(name: screen_name).first
if @account and @account.is_complete?
else
@l.error "User not found or account incomplete"
end
end
def
@🔨 = {
consumer_key: @account.consumer_key,
consumer_secret: @account.consumer_secret,
access_token: @account.access_token,
access_token_secret: @account.access_token_secret
}
@search_hashtags = @account.search_queries.where(kind: :search).pluck(:query)
@tweet_hashtags = @account.search_queries.where(kind: :tweet).pluck(:query)
@limits = {
new_friendship: 10, #TODO remove that -> 15
destroy_friendship: 15,
retweet: 15,
starring: 15,
search: 180,
crawl_user: 15
}
@limits.each do |label, limit|
create_method "mark_#{label}".to_sym do
if @limits[label] > 0
@limits[label] -= 1
return true
else
return false
end
end
create_method "can_#{label}?".to_sym do
return @limits[label] > 0
end
end
@client = Twitter::REST::Client.new(@🔨)
@user = @client.user
📷 and 💾
@followed_ids = @client.friend_ids.to_h[:ids]
@following_me = @client.follower_ids.to_h[:ids] - DO_NOT_TRACK
@can_be_removed = (@followed_ids - @following_me).reverse
@starred_ids = @client.favorites({count: 100}).map{|fav| fav.id}
@rt_ids = @client.retweeted_by_me({count: 200, exclude_replies: true}).map{|rt| rt.id}
@followed_cache = @followed_ids
@followed_users = FollowedUser.where(account_id: @account.id).first_or_create
@seen_users = (@followed_users.users || []) + (@followed_ids + @following_me)
end
# 😂
def create_method(name, &block)
self.class.send(:define_method, name, &block)
end
# ✌️
def make_some_friends
request = @search_hashtags.join(" OR ")
if can_search?
results = @client.search(request, {lang: "fr"}).take(100)
results.each do |tweet|
return unless mark_search
unless follow_tweet(tweet)
unless star_tweet(tweet)
return
end
end
end
end
end
# EVER AND EVER !
def make_ever_more_friends
if can_search? and can_new_friendship? and can_crawl_user?
mark_crawl_user
@following_me.each do |uid|
mark_crawl_user
(@client.follower_ids(uid).to_h[:ids] - DO_NOT_TRACK).each do |to_follow|
return unless follow_user(to_follow) and can_crawl_user?
end
end
else
end
end
# Time to go my friend, see you on the other side !
def break_friendships
if can_search? and can_destroy_friendship? and can_crawl_user? and @user.friends_count > FRIEND_MAX_COUNT
mark_crawl_user
@can_be_removed.each do |uid|
return unless destroy_friendship(uid) and can_crawl_user?
end
else
end
end
# Will try to follow the tweet, if it has to be
def follow_user user_id
return false unless can_new_friendship?
if !@seen_users.include?(user_id)
mark_new_friendship
@client.follow!(user_id) unless DEBUG
@followed_ids << user_id
@seen_users << user_id
end
true
end
# Will try to destroy the friendship, if it has to be
def destroy_friendship user_id
return false unless can_destroy_friendship?
mark_destroy_friendship
@client.unfollow(user_id) unless DEBUG
@followed_ids -= [user_id]
@can_be_removed -= [user_id]
true
end
# Will try to follow the tweet, if it has to be
def follow_tweet tweet
return false unless can_new_friendship?
follow_user(tweet.user.id)
true
end
# Will fetch if this tweet has to be starred
def star_tweet tweet
return false unless can_starring?
user = tweet.user
if !@seen_users.include?(user.id) and !@starred_ids.include?(tweet.id) and can_starring?
begin
mark_starring
@client.favorite!(tweet) unless DEBUG
rescue Twitter::Error::AlreadyFavorited => error
@starred_ids << tweet.id
@l.error("Favorite") { error }
end
end
true
end
# We set an unique retweet time, in order to avoid spamming
def time_to_retweet?
PUBLISH_HOURS.include?(Time.now.hour) and Time.now.min < 20
end
# Will try to publish something nice
def say_interesting_things
if time_to_retweet?
results = @client.search(@tweet_hashtags.join(" OR ")).take(100)
tweets = select_tweets(results)
tweets = tweets.reject{|t| @rt_ids.include? t.id}
tweets.sort! { |x,y| y.retweet_count <=> x.retweet_count }
begin
mark_retweet && @client.retweet(tweets.first) unless DEBUG
rescue Twitter::Error::Forbidden => error
@l.error error
return false
end
else
false
end
end
def 📷
if @account
@account.profile_image = @user.profile_image_url
@account.profile_banner = @user.profile_banner_url
@account.display_name = @user.name
@account.save
end
end
def 💾
if @account
@account.sessions.create({
followers_count: @user.followers_count,
favorites_count: @user.favorites_count,
friends_count: @user.friends_count
})
end
end
# Will fetch most favorited / retweeted tweets
def select_tweets results
tweets_podium = []
results.each do |tweet|
if !tweet.user_mentions? and tweet.retweet_count > RETWEET_MIN and tweet.favorite_count > FAVORITE_MIN
tweets_podium << tweet
end
# We keep 20 tweets, not more
if tweets_podium.length >= 20
return tweets_podium
end
end
# If we are here, we failed in the mission of choosing nice tweets.
return tweets_podium
end
# All begin here
def ⚡️
debug = Rails.env.development?
if !debug and @account and @account.is_complete?
begin
break_friendships
make_some_friends
make_ever_more_friends
say_interesting_things
adding_count = (@followed_ids - @followed_cache).length
@followed_users.users = @seen_users.uniq
rescue Twitter::Error::TooManyRequests => error
return
end
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment