Skip to content

Instantly share code, notes, and snippets.

@hiredman
Created November 5, 2009 02:36
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 hiredman/226651 to your computer and use it in GitHub Desktop.
Save hiredman/226651 to your computer and use it in GitHub Desktop.
(ns socialmetrics.servlet
(:gen-class :extends javax.servlet.http.HttpServlet)
(:use compojure.html compojure.http)
(:import (java.util Properties Date)
(java.io File FileInputStream OutputStreamWriter
BufferedReader InputStreamReader)
(java.net URL)
(java.text SimpleDateFormat ParsePosition FieldPosition)
(org.apache.commons.codec.digest DigestUtils)))
(defmacro load-properties
"macroexpand/compile time loading of a properties file into a clojure map"
[filename]
(let [p (Properties.)
is (-> filename File. FileInputStream.)]
(.load p is)
(into {} p)))
(def facebook (load-properties "facebook.properties"))
(defn call-id
"many facebook api calls require a call-id and call-ids must be in ascending
order per session"
[]
(.getTime (Date.)))
(def now (Date.))
(defn name|str
"name on Named things and .toString otherwise"
[x]
(if (instance? clojure.lang.Named x)
(name x)
(.toString x)))
(defn post
"post stuff to url, clojure.xml/parse output"
[url stuff]
(let [con (doto (.openConnection (URL. url))
(.setDoInput true)
(.setDoOutput true))]
(with-open [wrt (-> con .getOutputStream OutputStreamWriter.)]
(.write wrt stuff))
(with-open [a (-> con .getInputStream)]
(clojure.xml/parse a))))
(defn md5 [string]
(DigestUtils/md5Hex string))
(defn sig
"facebook api calls require a signature (md5 hash) of the call the api secret"
[params]
(md5
(.concat
(reduce #(format "%s%s=%s" %1 (name|str (first %2)) (second %2))
""
(sort-by key params))
(facebook "api.secret"))))
(defn call
"takes a map representing a facebook api call and makes the call"
[bag]
(post "http://api.facebook.com/restserver.php"
(format "%s&sig=%s"
(reduce
#(format "%s%s=%s&" %1 (name|str (first %2)) (second %2))
""
bag)
(sig bag))))
(defn string->date
[string]
(.parse (SimpleDateFormat. "MM/dd/yyyy")
string
(ParsePosition. 0)))
(defn date->age [date]
(let [cy (.getYear now)
oy (.getYear date)]
(- cy oy)))
(defn select-birthday_date [foo]
(-> foo :content
((partial filter #(= :birthday_date (:tag %))))
first
:content
first
((fn [x]
(when x (when-let [t (string->date x)] (date->age t)))))))
(defn chart [map-]
(format "http://chart.apis.google.com/chart?chtt=%s&cht=bvg&chd=t:%s&chs=900x250&chl=%s"
"Your friends' ages:"
(reduce #(format "%s,%s" %1 %2) (vals map-))
(reduce #(format "%s|%s" (or %1 "unknown") (or %2 "unknown")) (keys map-))))
(defn main [request]
(let [session (-> request :params :fb_sig_session_key) ;facebook passes me a session key
friends (-> request :params :fb_sig_friends) ; and a list of friends
foo (call {:api_key (facebook "api.key") ;get the birthday_date profile field from all the friends
:method "Users.getInfo"
:call_id (call-id)
:v "1.0"
:uids friends
:fields "birthday_date"
:session_key session})
foo (-> foo :content
((partial map select-birthday_date))
((partial map #(array-map %1 1)))
((partial apply (partial merge-with +)))
((partial into (sorted-map)))
chart)]
(format
"
<fb:if-is-app-user>
<img style=\"width:750px;\" src=\"%s\" />
<fb:else>
<fb:redirect url=\"http://www.facebook.com/login.php?v=1.0&api_key=%s&next=%s&canvas=\"/></fb:else>
</fb:if-is-app-user>
"
foo
(facebook "api.key")
"http://apps.facebook.com/agegraph/")))
(defroutes socialmetrics
(ANY "/" main))
(defservice socialmetrics)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment