require 'twitter_ebooks' require_relative 'boodoo' require 'dotenv' include Ebooks::Boodoo # Read .env file and set values: SETTINGS = Dotenv.load # Information about a particular Twitter user we know class UserInfo attr_reader :username # @return [Integer] how many times we can pester this user unprompted attr_accessor :pesters_left # @param username [String] def initialize(username) @username = username @pesters_left = parse_num(SETTINGS['PESTER_COUNT']) || 1 end end class CloneBot < BoodooBot attr_accessor :original, :model, :model_path, :auth_name # alias_method :oauth_token, :access_token # alias_method :oauth_token_secret, :access_token_secret def configure # create attr_accessors for all SETTINGS fields SETTINGS.keys.map(&:to_s).map(&:downcase).each(&Ebooks::Bot.method(:attr_accessor)) # String fields taken as-is: @consumer_key = SETTINGS['CONSUMER_KEY'] @consumer_secret = SETTINGS['CONSUMER_SECRET'] @access_token = SETTINGS['ACCESS_TOKEN'] @access_token_secret =SETTINGS['ACCESS_TOKEN_SECRET'] @tweet_interval = SETTINGS['TWEET_INTERVAL'] # @pester_period = SETTINGS['PESTER_PERIOD'] # String fields forced to downcase: @bot_name = SETTINGS['BOT_NAME'].downcase @original = SETTINGS['SOURCE_USERNAME'].downcase # Array fields are CSV or SSV @blacklist = parse_array(SETTINGS['BLACKLIST']) @banned_terms = parse_array(SETTINGS['BANNED_TERMS']) @special_terms = parse_array(SETTINGS['SPECIAL_TERMS']) # Fields parsed as Fixnum, Float, or Range: @default_delay = parse_range(SETTINGS['DEFAULT_DELAY']) @dm_delay = parse_range(SETTINGS['DM_DELAY']) || parse_range(SETTINGS['DEFAULT_DELAY']) @mention_delay = parse_range(SETTINGS['MENTION_DELAY']) || parse_range(SETTINGS['DEFAULT_DELAY']) @timeline_delay = parse_range(SETTINGS['TIMELINE_DELAY']) || parse_range(SETTINGS['DEFAULT_DELAY']) @tweet_chance = parse_num(SETTINGS['TWEET_CHANCE']) # @pester_count = parse_num(SETTINGS['PESTER_COUNT']) @timeout_sleep = parse_num(SETTINGS['TIMEOUT_SLEEP']) # from example @userinfo = {} # added for BooDoo variant @attempts = 0 @followers = [] @following = [] # @have_talked = {} # load model file load_model! end def top100; @top100 ||= model.keywords.take(100); end def top20; @top20 ||= model.keywords.take(20); end def delay(d, &b) d ||= default_delay sleep (d || [0]).to_a.sample b.call end def on_startup log "I started up!" scheduler.interval @tweet_interval do if rand < @tweet_chance tweet(model.make_statement) end end scheduler.interval @update_follows_interval do follow_parity end # TODO: This throws a weird error. # Probably don't need it anyway? # @auth_name ||= twitter.user.screen_name # log "Logged in as #{auth_name}" end def on_direct_message(dm) # TODO: Add controls here! Especially "tweet" delay(dm_delay) do reply(dm, model.make_response(dm.text)) end end def on_mention(tweet) # Become more inclined to pester a user when they talk to us userinfo(tweet.user.screen_name).pesters_left += 1 delay(mention_delay) do reply(tweet, model.make_response(meta(tweet).mentionless, meta(tweet).limit)) end end def on_timeline(tweet) return if tweet.retweeted_status? return unless can_pester?(tweet.user.screen_name) tokens = Ebooks::NLP.tokenize(tweet.text) interesting = tokens.find { |t| top100.include?(t.downcase) } very_interesting = tokens.find_all { |t| top20.include?(t.downcase) }.length > 2 delay(timeline_delay) do if very_interesting favorite(tweet) if rand < 0.5 retweet(tweet) if rand < 0.1 reply(tweet, model.make_response(meta(tweet).mentionless, meta(tweet).limit)) if rand < 0.05 elsif interesting favorite(tweet) if rand < 0.05 reply(tweet, model.make_response(meta(tweet).mentionless, meta(tweet).limit)) if rand < 0.01 end end end # Find information we've collected about a user # @param username [String] # @return [Ebooks::UserInfo] def userinfo(username) @userinfo[username] ||= UserInfo.new(username) end # Check if we're allowed to send unprompted tweets to a user # @param username [String] # @return [Boolean] def can_pester?(username) userinfo(username).pesters_left > 0 end # Only follow our original user or people who are following our original user # @param user [Twitter::User] def can_follow?(username) @original.nil? || username == @original || twitter.friendship?(username, @original) || twitter.friendship?(username, @original) || twitter.friendship?(username, auth_name) end def favorite(tweet) if can_follow?(tweet.user.screen_name) super(tweet) else log "Unfollowing @#{tweet.user.screen_name}" twitter.unfollow(tweet.user.screen_name) end end def on_follow(user) if can_follow?(user.screen_name) follow(user.screen_name) else log "Not following @#{user.screen_name}" end end private def load_model! return if @model @model_path ||= "model/#{original}.model" log "Loading model #{model_path}" @model = Ebooks::Model.load(model_path) end end CloneBot.new(SETTINGS['BOT_NAME']) do |bot| # CloneBot#configure does everything! bot end