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)