Skip to content

Instantly share code, notes, and snippets.

@koffeinfrei
Forked from busterbenson/markov_tweets.rb
Last active August 29, 2015 14:06
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 koffeinfrei/a0ebc5c9295c067fe5ff to your computer and use it in GitHub Desktop.
Save koffeinfrei/a0ebc5c9295c067fe5ff to your computer and use it in GitHub Desktop.
Horse koffeinfrei (markov twitter bot)

Getting started

  1. Setup new horse twitter account
  • Create a new Twitter account that you'd like to have your auto-tweets posted to
  • Go to dev.twitter.com, create a new application with Read+Write permissions
  • Create an access token + secret for the account and copy that and the consumer key and secrets.
  1. Get your twitter archive
  • Go to Twitter.com -> Settings -> Download Archive.
  • The tweets.csv file is in the top directory. Put it in the same directory as this script.
  1. Export quotes
  • Export your quotes to quotes.txt, each quote enclosed by double quotes To export e.g. from a postgres db, use the following: \copy (select quote from quotes) TO quotes.txt WITH (FORMAT CSV, FORCE_QUOTE *)
#!/usr/bin/env ruby
require 'rubygems'
require 'optparse'
require 'thread'
require 'csv'
require 'cgi'
# Make sure you have these gems installed
require 'twitter'
require 'marky_markov'
CONSUMER_KEY = ''
CONSUMER_SECRET = ''
ACCESS_TOKEN = ''
ACCESS_TOKEN_SECRET = ''
PATH_TO_TWEETS_CSV = 'tweets.csv'
PATH_TO_TWEETS_CLEAN = 'tweets_clean.txt'
PATH_TO_QUOTES = 'quotes.txt'
PATH_TO_QUOTES_CLEAN = 'quotes_clean.txt'
class MarkovDictionary
def cleanup(text)
text = text.
# remove quotes
gsub('"', '').
# strip multiple whitespaces
gsub(/\s+/, ' ').strip.
downcase
text = CGI.unescapeHTML(text)
after_cleanup(text)
end
def after_cleanup(text)
text
end
end
class TwitterMarkovDictionary < MarkovDictionary
def parse
csv_text = CSV.parse(File.read(PATH_TO_TWEETS_CSV))
# Create a new clean file of text that acts as the seed for your Markov chains
File.open(PATH_TO_TWEETS_CLEAN, 'w') do |file|
csv_text.reverse.each do |row|
tweet_text = cleanup(row[5])
file.write("#{tweet_text}\n") unless tweet_text.empty?
end
end
end
def file_name
PATH_TO_TWEETS_CLEAN
end
def after_cleanup(text)
text.
# strip links
gsub(/(?:f|ht)tps?:\/[^\s]+/, '').
# strip newlines
gsub(/\n/,' ').
# strip usernames
gsub(/@[^ ]+/, '').
# strip keyword 'RT'
gsub(/\brt\b/i, '')
end
end
class QuotesMarkovDictionary < MarkovDictionary
def parse
quotes = File.read(PATH_TO_QUOTES).split('"')
File.open(PATH_TO_QUOTES_CLEAN, 'w') do |file|
quotes.each do |quote|
quote = cleanup(quote)
file.write("#{quote}\n") unless quote.empty?
end
end
end
def file_name
PATH_TO_QUOTES_CLEAN
end
def after_cleanup(text)
text.
# replace newlines by ;
gsub(/\n/,'; ').
# remove author info at the end
gsub(/ --.+$/, '').
# remove author info at the end
gsub(/ \(.+\)$/, '')
end
end
class Markov
def initialize(dictionary_classes)
@markov = MarkyMarkov::TemporaryDictionary.new
dictionary_classes.each do |dictionary_class|
dictionary = dictionary_class.new
dictionary.parse
@markov.parse_file(dictionary.file_name)
end
end
def generate
@markov.generate_1_sentence.chomp
end
end
class TwitterClient
def initialize
@client = Twitter::REST::Client.new do |config|
config.consumer_key = CONSUMER_KEY
config.consumer_secret = CONSUMER_SECRET
config.access_token = ACCESS_TOKEN
config.access_token_secret = ACCESS_TOKEN_SECRET
end
end
def tweet(text)
if text.length > 140
tweet_split(text)
else
@client.update(text)
end
end
def tweet_split(text)
length = text.length > 280 ? 134 : 137
splits = text.scan(/.{,#{length}}/).reject(&:empty?)
@client.update("#{splits.shift}...")
while splits.length > 1
@client.update("...#{splits.shift}...")
end
@client.update("...#{splits.shift}")
end
end
class Terminal
def initialize(markov, twitter)
@markov = markov
@twitter = twitter
end
def run
loop do
next_tweet
end
end
def next_tweet
tweet_text = @markov.generate
puts "\nHorse says:\n"
p tweet_text
if ask('Tweet this one?')
@twitter.tweet(tweet_text)
puts "Tweeted!"
end
end
def ask(question)
print "\n#{question} [y/n/q]\n> "
input = gets.chomp.downcase
if input == 'q'
puts "\nDone for now."
exit
end
input == 'y'
end
end
dictionaries = []
OptionParser.new do |opts|
opts.banner = "Usage: #{__FILE__} [options]"
opts.on("-t", "--twitter", "Include twitter dictionary") do |v|
puts "Using twitter dictionary.\n"
dictionaries << TwitterMarkovDictionary
end
opts.on("-q", "--quotes", "Include quotes dictionary") do |v|
puts "Using quotes dictionary.\n"
dictionaries << QuotesMarkovDictionary
end
end.parse!
if dictionaries.empty?
puts "No options provided, using twitter and quotes dictionaries.\n"
dictionaries = [TwitterMarkovDictionary, QuotesMarkovDictionary]
end
markov = Markov.new(dictionaries)
Terminal.new(markov, TwitterClient.new).run
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment