Skip to content

Instantly share code, notes, and snippets.

@FluffyPira
Last active August 29, 2015 14:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save FluffyPira/93dcaa198f8793a37e96 to your computer and use it in GitHub Desktop.
Save FluffyPira/93dcaa198f8793a37e96 to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
require 'twitter_ebooks'
require 'set'
include Ebooks
CONSUMER_KEY = ""
CONSUMER_SECRET = ""
OATH_TOKEN = "" # oauth token for ebooks account
OAUTH_TOKEN_SECRET = "" # oauth secret for ebooks account
ROBOT_ID = "books" # Avoid infinite reply chains
TWITTER_USERNAME = "" # Ebooks account username
TEXT_MODEL_NAME = "" # This should be the name of the text model
REPLY_MODEL = "" # Different model for DMs
DELAY = 2..30 # Simulated human reply delay range in seconds
BLACKLIST = [''] # Grumpy users to avoid interaction with
SPECIAL_WORDS = ['']
TRIGGER_WORDS = [''] # Block users who say these words
# Track who we've randomly interacted with globally
$have_talked = {}
class GenBot
def initialize(bot, modelname)
@bot = bot
@model = nil
bot.consumer_key = CONSUMER_KEY
bot.consumer_secret = CONSUMER_SECRET
bot.on_startup do
@model = Model.load("model/#{modelname}.model")
@model2 = Model.load("model/#{REPLY_MODEL}.model") # Whistles innocently.
@top100 = @model.keywords.top(100).map(&:to_s).map(&:downcase)
@top20 = @model.keywords.top(20).map(&:to_s).map(&:downcase)
@pics = Dir.entries("pictures/") - %w[.. . .DS_Store].sort
bot.log @pics.take(5) # poll for consistency and tracking purposes.
@status_count = @bot.twitter.user.statuses_count
prune_following()
post_tweet
end
bot.on_message do |dm|
bot.delay DELAY do
bot.reply dm, @model2.make_response(dm[:text])
# Yes, if you DM my bot she does talk like Bailey Jay... >_<
end
end
bot.on_follow do |user|
bot.delay DELAY do
bot.follow user[:screen_name]
end
end
bot.on_mention do |tweet, meta|
# Avoid infinite reply chains (very small chance of crosstalk)
next if tweet[:user][:screen_name].include?(ROBOT_ID) && rand > 0.05
next if tweet[:user][:screen_name].include?('bot') && rand > 0.20
next if tweet[:user][:screen_name].include?('generateacat') && rand > 0.10
tokens = NLP.tokenize(tweet[:text])
very_interesting = tokens.find_all { |t| @top20.include?(t.downcase) }.length > 2
special = tokens.find { |t| SPECIAL_WORDS.include?(t.downcase) }
trigger = tokens.find { |t| TRIGGER_WORDS.include?(t.downcase) }
if trigger
block(tweet)
end
if very_interesting || special
favourite(tweet)
retweet(tweet) if rand < 0.1
end
reply(tweet, meta)
end
bot.on_timeline do |tweet, meta|
next if tweet[:retweeted_status] || tweet[:text].start_with?('RT')
next if BLACKLIST.include?(tweet[:user][:screen_name])
tokens = NLP.tokenize(tweet[:text])
# We calculate unprompted interaction probability by how well a
# tweet matches our keywords
interesting = tokens.find { |t| @top100.include?(t.downcase) }
very_interesting = tokens.find_all { |t| @top20.include?(t.downcase) }.length > 2
special = tokens.find { |t| SPECIAL_WORDS.include?(t.downcase) }
trigger = tokens.find { |t| TRIGGER_WORDS.include?(t.downcase) }
if trigger
block(tweet) if rand < 0.25
end
if special
favourite(tweet) if rand < 0.25
favd = true # Mark this tweet as favorited
end
# Any given user will receive at most one random interaction per day
# (barring special cases)
# Raised the reply elements up a bit since I figured out have_talked.
next if $have_talked[tweet[:user][:screen_name]]
$have_talked[tweet[:user][:screen_name]] = true
if very_interesting || special
favourite(tweet) if (rand < 0.1 && !favd) # Don't fav the tweet if we did earlier
reply(tweet, meta) if rand < 0.2
elsif interesting
reply(tweet, meta) if rand < 0.2
end
end
# Schedule a tweet for every 30 minutes
bot.scheduler.every '1800' do
post_tweet
end
# Clears the have_talked variable daily at midnight.
bot.scheduler.cron '0 0 * * *' do
$have_talked = {}
# This is just for fun and to make her post like a porn star at midnight (lewd).
bot.tweet @model2.make_statement
end
end
def reply(tweet, meta)
resp = @model.make_response(meta[:mentionless], meta[:limit])
@bot.delay DELAY do
@bot.reply tweet, meta[:reply_prefix] + resp
end
end
def favourite(tweet)
@bot.log "Favoriting @#{tweet[:user][:screen_name]}: #{tweet[:text]}"
@bot.twitter.favorite(tweet[:id])
end
def retweet(tweet)
@bot.log "Retweeting @#{tweet[:user][:screen_name]}: #{tweet[:text]}"
@bot.delay DELAY do
@bot.twitter.retweet(tweet[:id])
end
end
def block(tweet)
@bot.log "Blocking and reporting @#{tweet[:user][:screen_name]}"
@bot.twitter.block(tweet[:user][:screen_name])
@bot.twitter.report_spam(tweet[:user][:screen_name])
end
def next_index()
seq = (0..(@pics.size - 1)).to_a
seed = @status_count / @pics.size
r = Random.new(seed)
seq.shuffle!(random: r)
res = seq[@status_count % @pics.size]
@status_count = @status_count + 1
return res
end
def prune_following
following = Set.new(@bot.twitter.friend_ids.to_a)
followers = Set.new(@bot.twitter.follower_ids.to_a)
to_unfollow = (following - followers).to_a
@bot.log("Unfollowing user ids: #{to_unfollow}")
@bot.twitter.unfollow(to_unfollow)
end
def post_tweet
txt = @model.make_statement
pic = @pics[next_index]
# this has a 20% chance of tweeting a picture from a specified folder, otherwise it will tweet normally.
if rand < 0.20
@bot.twitter.update_with_media("#{txt}", File.new("pictures/#{pic}"))
@bot.log "#{txt} - #{pic}"
else
@bot.twitter.update("#{txt}")
@bot.log "#{txt}"
end
end
end
def make_bot(bot, modelname)
GenBot.new(bot, modelname)
end
Ebooks::Bot.new(TWITTER_USERNAME) do |bot|
bot.oauth_token = OATH_TOKEN
bot.oauth_token_secret = OAUTH_TOKEN_SECRET
make_bot(bot, TEXT_MODEL_NAME)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment