Skip to content

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
You can’t perform that action at this time.