Justin Collins wrote: > Daniel Moore wrote: >> -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- >> >> The three rules of Ruby Quiz: >> >> 1. Please do not post any solutions or spoiler discussion for this >> quiz until 48 hours have elapsed from the time this message was >> sent. >> >> 2. Support Ruby Quiz by submitting ideas and responses >> as often as you can! >> Visit: http://rubyquiz.strd6.com/suggestions >> >> 3. Enjoy! >> >> Suggestion: A [QUIZ] in the subject of emails about the problem >> helps everyone on Ruby Talk follow the discussion. Please reply to >> the original quiz message, if you can. >> >> RSS Feed: http://rubyquiz.strd6.com/quizzes.rss >> >> -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- >> >> ## Twitter Personalities (#208) >> >> Merhaba Rubyists, >> >> This week's quiz is to create a program that will generate messages >> 140 characters in length. There primary use will be to create a >> Twitter "personality". At the end of the quiz period these >> "personalities" will be unleashed on the internet and we'll see how >> they do in the wild. >> >> The programs will consist of two parts: a component for interacting >> with Twitter, and a top secret "personality" module. >> >> For the Twitter interface component there will be no no-spoiler >> period. Please feel encouraged to discuss different libraries or >> methods on the mailing list. Let's all work together to find the best >> interface. >> >> The "personality" component can take any inputs and will produce a 140 >> character message when called. The "personality" may remember state. >> The no-spoiler period applies for the "personality" component; please >> save them until everyone has had a chance to consider their own >> implementations. >> >> >> Have Fun! >> > > Posting to Twitter, I found, was pretty straightforward using the > Twitter4r gem, although it only worked for me under Ruby 1.9: > > require 'twitter' > client = Twitter::Client.new :login => "myname", :password => > "mypassword" > client.status :post, "Working on Ruby Quiz." > > > I hope that helps anyone who might have been hesitant because of the > Twitter interface part. > > -Justin > So, here is my full solution, which just pulls transcriptions of Dijkstra's notes and tries to extract decent quotes. Also attempts to find relavent replies. Works most of the time. You can see some results at http://twitter.com/ewdbot and get a nicer-looking version from http://gist.github.com/125781 #A little Twitter thing to post random quotes from Edsger Dijkstra require 'yaml' require 'open-uri' require 'twitter' #twitter4r gem require 'hpricot' class DijkstraQuote class << self #Match the website's naming scheme def random_ewd (rand(1289) + 30).to_s.rjust(4, "0") end #Get a random quote def get_quote quote = nil while quote.nil? do quote = fetch random_ewd end quote end #Get a random quote from the specified set of notes def fetch ewd #Check if we've already retrieved this set of notes if File.exists? "ewd#{ewd}" quotes = YAML.load_file "ewd#{ewd}" quotes[rand quotes.length] else $stderr.puts "Fetching EWD#{ewd}..." if $DEBUG #Fetch a transcript of Dijkstra's notes begin file = open("http://www.cs.utexas.edu/~EWD/transcriptions/EWD#{ewd[0,2]}xx/EWD#{ewd}.html") { |f| Hpricot(f) } rescue OpenURI::HTTPError $stderr.puts "Not found" if $DEBUG return nil end text = file.to_plain_text #Pick some sentences that seem good lines = text.split(/\.(?:\s+|\n+)/).map do |l| l.gsub(/\t|\n|\r/, " ").squeeze(" ").strip end.select do |l| l.length > 40 and l.length < 140 and l[0,1] == l[0,1].upcase and l[-3, 3] != "viz" end #Cache them for later File.open "ewd#{ewd}", "w" do |f| YAML.dump lines, f end #Return a random sentence lines[rand lines.length] end end #(Very crudely) tries to find a quote that is related to the message. #Note that this only searches the cache. def find_related message #Get the longer words words = message.gsub(/[^a-zA-Z ]/, "").split.select { |w| w.length > 4 } quote = nil if words.length > 0 word = /#{Regexp.union(words)}/i #Check local files for the search word Dir.glob("ewd*").find do |f| text = File.read f if text =~ word #Get the list of quotes and grab one quotes = YAML.load_file f matches = quotes.grep word quote = matches[rand(matches.length)] else false end end else quote = get_quote end quote end end end #Post to Twitter class DijkstraTwitter def initialize @twitter = Twitter::Client.new :login => "?", :password => "?" end #Post a random quote. If _ask_ is true, asks for approval first def post_random ask = false quote = DijkstraQuote.get_quote if ask and ask_permission("Would you like to post this: \"#{quote}\"") or not ask @twitter.status :post, quote end end #Check unanswered @ewd messages and come up with responses def post_replies ask = false require 'set' #Keep track of what has been replied to already if File.exist? "ewdreplies" replied = YAML.load_file "ewdreplies" else replied = Set.new end replies = @twitter.status(:replies) replies.each do |status| status_id = status.id.to_s next if replied.include? status_id message = status.text user = status.user.screen_name if reply_to message, user, ask replied << status_id end end File.open "ewdreplies", "w" do |f| YAML.dump replied, f end end #Send a reply if we can find one def reply_to message, sender, ask = false puts "Responding to \"#{message}\"" quote = DijkstraQuote.find_related message if not quote puts "Nothing related. Skipping." return false elsif ask and ask_permission("Would you like to post this: \"#{quote}\"") or not ask response = "@#{sender} #{quote}" if response.length > 140 response = response[0,140] end @twitter.status(:post, "@#{sender} #{quote}") true else if ask and ask_permission "Ignore this reply?" true else false end end end #Ask if the user would like to post the quote which was found def ask_permission message puts message response = nil until response =~ /^(y|n)/i print "(Y/N): " response = gets end $1.downcase == "y" end end #Try it out puts DijkstraQuote.get_quote #dt = DijkstraTwitter.new #dt.post_random true #dt.post_replies true My favorite so far: "In this sense, Programming = Mathematics + Murphy's Law" -Justin