Last active
August 29, 2015 14:10
-
-
Save FluffyPira/93dcaa198f8793a37e96 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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