Last night at makerlab we were talking about dating sites a bit – we had some fun thinking about the different ways that we could imagine designing a simple tool for twitter. We decided we wanted it to be really really simple.
Like REALLY REALLY simple. Like this:
Kinda like a kiss. Yeah, just like that. Like a kissing booth! JUST like a Kissing Booth!
The way this all works is that when you send a message to @kissmehere on twitter and you include the name of some people, it will send a kiss to all those people. For example:
@kissmehere go kiss @paigesaez @zephoria @soycamo @semaphoria @anselm
Kissmehere is no prude, you can kiss more than one person at the same time – or kiss only one person – it is up to you.
Kissmehere maps your kisses too!
How did I build it? This is just a riff on the same twitter code I have been using before. There are a few twists. First we have some pre-amble and we have a geocoding engine – thanks MetaCarta!!!:
require 'rubygems' require 'dm-core' require 'twitter' require 'net/smtp' require 'net/http' require 'uri' require 'json' require 'dm-core' # # passwords # TWITTER_USER_NAME = "kissmehere" TWITTER_PASSWORD = "" METACARTA_USERID = "" METACARTA_PASSWORD = "" METACARTA_KEY = "" # # a very very nice metacarta utility to brute force discover location in text # def geolocate(location) location = URI.escape(location, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]")) # location = URI.escape(location) host = "ondemand.metacarta.com" path = "/webservices/GeoTagger/JSON/basic?version=1.0.0" path = "#{path}&doc=#{location}" data = {} begin req = Net::HTTP::Get.new(path) req.basic_auth METACARTA_USERID, METACARTA_PASSWORD http = Net::HTTP.start(host) #if response.is_a?(Net::HTTPSuccess) response = http.request(req) puts response.body data = JSON.parse(response.body) #end rescue Timeout::Error # DO SOMETHING WISER return 0,0 rescue return 0,0 end begin lat = data["Locations"][0]["Centroid"]["Latitude"] lon = data["Locations"][0]["Centroid"]["Longitude"] return lat,lon rescue end return 0,0 end
We have a simple data model as usual to track our activity… again using datamapper which is a favorite of mine.
# # Only send out 10 tweets at a time # twittercap = 10 # # Grab a database # DataMapper.setup(:default, { :adapter => 'postgres', :database => "kissmehere", :username => '', :password => '', :host => 'localhost' }) # # here is our schema # class Kiss include DataMapper::Resource property :id, Integer, :serial => true property :provenance, Text property :uuid, Text property :title, Text property :link, Text property :description, Text property :screenname, Text property :userid, Text property :location, Text property :lat, Float property :lon, Float property :secret, Integer, :default => 0 property :friended, Integer, :default => 0 property :kissed_at, DateTime property :created_at, DateTime end
We have the usual twitter gem code to peek at the twitter state. I am really starting to wonder how the heck twitter even stays up with the amount of traffic it is getting… In any case mine is not to worry but to do!
# # Remember kiss requests # twitter = Twitter::Base.new(TWITTER_USER_NAME, TWITTER_PASSWORD ) twitter.replies().each do |twit| uuid = "#{twit.id}" kiss = Kiss.first(:provenance => "twitter", :uuid => uuid) next if kiss secret = 0 secret = 1 if twit.text[/ secret/] != nil lat = 0 lon = 0 if twit.user.location && twit.user.location.length > 1 lat,lon = geolocate(twit.user.location) end kiss = Kiss.create( :provenance => "twitter", :uuid => uuid, :title => twit.text, :link => nil, :description => nil, :screenname => twit.user.screen_name, :userid => twit.user.id, :location => twit.user.location, :lon => lon, :lat => lat, :secret => secret ) kiss.save puts "Saved a kiss on twitter! #{kiss.userid} #{kiss.title} #{kiss.lat} #{kiss.lon}" end
Next we want to respond to kisses in an intelligent way; telling everybody, friending new friends and all that kind of fun stuff.
# # Pass new kisses onwards ( only do twittercaps worth ) # @kisses = Kiss.all(:order => [:created_at.desc], :limit => twittercap, :kissed_at => nil ).each do |kiss| # tease each kiss apart for multiple receivers kisses = kiss.title.scan(/\@\w+/) kisses.each do |luckyduck| next if luckyduck == "@kissmehere" if kiss.secret == 0 kiss.link = "http://twitter.com/#{kiss.screenname}/statuses/#{kiss.uuid}" gossip = "#{luckyduck} got a kiss from @#{kiss.screenname} - see #{kiss.link} " # if kiss.lat != 0 && kiss.lon != 0 # gossip = " - #{gossip} near #{kiss.location}" # end else kiss.link = nil gossip = "@#{luckyduck} got a kiss from an anonymous admirer!" end kiss.description = gossip result = twitter.post(gossip) puts "Told everybody #{result} of #{gossip}" end if kisses.length == 0 puts "No love from #{kiss.screenname}" end kiss.kissed_at = DateTime.now kiss.save # friend everybody - could improve this begin twitter.create_friendship(kiss.screenname) rescue end kisses.each do |luckyduck| begin #if twitter.friendship_exists(TWITTER_USER_NAME,luckyduck) twitter.create_friendship(luckyduck) rescue end end end
Finally we write out an RSS feed for Google Maps – thanks @ajturner for the quick tip. I wasn’t able to get ruby rss maker to do anything useful such as allow me to specify custom namespaces for the geo:lat and geo:long attributes so I wrote everything by hand! By doing this we can then make a map page which has all the kisses on it just for fun. I guess I won’t show this blob because it breaks the layout engine in wordpress… I will link to the original file however at agent.txt
That’s it. Have fun out there in the twitter verse!
2 Comments
Yay! Anselm rules! Awesomesauce as Amber Case would say.
Everyone come to the Twitter Kissing Booth and lets kiss our worries away.
-paige
(smooch)
(smooch) (smoooooch)