diff --git a/CHANGES b/CHANGES index a4468e1..13a1228 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,15 @@ +* 1.2.5 + +- Updated dependency of clj-oauth to 1.2.10. + +* 1.2.4 + +- Updated dependency of clj-oauth to 1.2.9. + +* 1.2.3 + +- Depending on clj-oauth 1.2.4. + * 1.2.2 - Fixed incorrect merging of optional params. Possible regression from 1.2. (found and fixed by Zehua Liu) - diff --git a/project.clj b/project.clj index 9327c80..8c8d12c 100644 --- a/project.clj +++ b/project.clj @@ -1,7 +1,6 @@ -(defproject clojure-twitter "1.2.1" +(defproject clojure-twitter "1.2.8" :description "Twitter Client API for Clojure" - :dependencies [[org.clojure/clojure "1.2.0"] - [org.clojure/clojure-contrib "1.2.0"] - [clj-oauth "1.2.1"] - [com.twinql.clojure/clj-apache-http "2.3.1"]] - :dev-dependencies [[swank-clojure "1.2.1"]]) + :dependencies [[org.clojure/clojure "1.5.1"] + [org.clojure/data.json "0.1.2"] + [clj-oauth "1.4.1"] + [org.clojars.tavisrudd/clj-apache-http "2.3.2-SNAPSHOT"]]) diff --git a/src/twitter.clj b/src/twitter.clj index 33d5d64..9446132 100644 --- a/src/twitter.clj +++ b/src/twitter.clj @@ -1,6 +1,5 @@ (ns twitter - (:use [clojure.contrib.json :only [read-json]] - [clojure.contrib.java-utils :only [as-str]]) + (:use [clojure.data.json :only [read-json]]) (:require [clojure.set :as set] [clojure.string :as string] [com.twinql.clojure.http :as http] @@ -13,12 +12,12 @@ (declare status-handler) -(def *oauth-consumer* nil) -(def *oauth-access-token* nil) -(def *oauth-access-token-secret* nil) -(def *protocol* "http") +(def ^:dynamic *oauth-consumer* nil) +(def ^:dynamic *oauth-access-token* nil) +(def ^:dynamic *oauth-access-token-secret* nil) +(def ^:dynamic *protocol* "http") -;; Get JSON from clj-apache-http +;; Get JSON from clj-apache-http (defmethod http/entity-as :json [entity as state] (read-json (http/entity-as entity :string state))) @@ -28,13 +27,13 @@ `(binding [*oauth-consumer* ~consumer *oauth-access-token* ~access-token *oauth-access-token-secret* ~access-token-secret] - (do + (do ~@body))) (defmacro with-https [ & body] `(binding [*protocol* "https"] - (do + (do ~@body))) (defmacro def-twitter-method @@ -51,32 +50,38 @@ take any required and optional arguments and call the associated Twitter method. rest-map# (apply hash-map rest#) provided-optional-params# (set/intersection (set ~optional-params) (set (keys rest-map#))) - required-query-param-names# (map (fn [x#] - (keyword (string/replace (name x#) #"-" "_" ))) - ~required-params) - optional-query-param-names-mapping# (map (fn [x#] - [x# (keyword (string/replace (name x#) #"-" "_"))]) - provided-optional-params#) - query-params# (merge (apply hash-map - (vec (interleave required-query-param-names# ~required-fn-params))) - (apply merge - (map (fn [x#] {(second x#) ((first x#) rest-map#)}) optional-query-param-names-mapping#))) - need-to-url-encode# (if (= :get ~req-method) - (into {} (map (fn [[k# v#]] [k# (oauth.signature/url-encode v#)]) query-params#)) - query-params#) - oauth-creds# (when (and *oauth-consumer* - *oauth-access-token*) + required-query-param-names# + (map (fn [x#] + (keyword (string/replace (name x#) #"-" "_" ))) + ~required-params) + + optional-query-param-names-mapping# + (map (fn [x#] + [x# (keyword (string/replace (name x#) #"-" "_"))]) + provided-optional-params#) + + query-params#(merge (apply hash-map + (vec (interleave + required-query-param-names# + ~required-fn-params))) + (apply merge + (map (fn [x#] {(second x#) + ((first x#) + rest-map#)}) + optional-query-param-names-mapping#))) + oauth-creds# (when (and *oauth-consumer* + *oauth-access-token*) (oauth/credentials *oauth-consumer* *oauth-access-token* *oauth-access-token-secret* ~req-method req-uri# - need-to-url-encode#))] + query-params#))] (~handler (~(symbol "http" (name req-method)) req-uri# :query (merge query-params# oauth-creds#) - :parameters (http/map->params + :parameters (http/map->params {:use-expect-continue false}) :as :json)))))) @@ -89,7 +94,7 @@ take any required and optional arguments and call the associated Twitter method. "api.twitter.com/1/statuses/public_timeline.json" [] [] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method friends-timeline :get @@ -99,7 +104,7 @@ take any required and optional arguments and call the associated Twitter method. :max-id :count :page] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method user-timeline :get @@ -112,7 +117,7 @@ take any required and optional arguments and call the associated Twitter method. :max-id :count :page] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method home-timeline :get @@ -124,7 +129,7 @@ take any required and optional arguments and call the associated Twitter method. :page :skip-user :include-entities] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method mentions :get @@ -134,56 +139,56 @@ take any required and optional arguments and call the associated Twitter method. :max-id :count :page] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method show-status :get "api.twitter.com/1/statuses/show.json" [:id] [] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method update-status :post "api.twitter.com/1/statuses/update.json" [:status] [:in-reply-to-status-id] - (comp #(:status (:content %)) status-handler)) + (comp :status :content status-handler)) (def-twitter-method destroy-status :post "api.twitter.com/1/statuses/destroy.json" [:id] [] - (comp #(:status (:content %)) status-handler)) + (comp :status :content status-handler)) (def-twitter-method show-user-by-id :get "api.twitter.com/1/users/show.json" [:user-id] [] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method show-user-by-name :get "api.twitter.com/1/users/show.json" [:screen-name] [] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method lookup-users-by-id :get "api.twitter.com/1/users/lookup.json" [:user-id] [] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method lookup-users-by-name :get "api.twitter.com/1/users/lookup.json" [:screen-name] [] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method direct-messages :get @@ -193,7 +198,7 @@ take any required and optional arguments and call the associated Twitter method. :max-id :count :page] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method sent-direct-messages :get @@ -203,7 +208,7 @@ take any required and optional arguments and call the associated Twitter method. :max-id :count :page] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method send-direct-message-to-id :post @@ -211,7 +216,7 @@ take any required and optional arguments and call the associated Twitter method. [:user-id :text] [] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method send-direct-message-to-name :post @@ -219,42 +224,42 @@ take any required and optional arguments and call the associated Twitter method. [:screen-name :text] [] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method destroy-direct-message :post "api.twitter.com/1/direct_messages/destroy.json" [:id] [] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method create-friendship-to-id :post "api.twitter.com/1/friendships/create.json" [:user-id] [:follow] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method create-friendship-to-name :post "api.twitter.com/1/friendships/create.json" [:screen-name] [:follow] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method destroy-friendship-to-id :post "api.twitter.com/1/friendships/destroy.json" [:user-id] [] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method destroy-friendship-to-name :post "api.twitter.com/1/friendships/destroy.json" [:screen-name] [] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method show-friendship-by-ids :get @@ -262,7 +267,7 @@ take any required and optional arguments and call the associated Twitter method. [:source-id :target-id] [] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method show-friendship-by-names :get @@ -270,63 +275,63 @@ take any required and optional arguments and call the associated Twitter method. [:source-screen-name :target-screen-name] [] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method friends-of-id :get "api.twitter.com/1/friends/ids.json" [:user-id] [] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method friends-of-name :get "api.twitter.com/1/friends/ids.json" - [:screen-name] + [:screen-name] [] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method followers-of-id :get "api.twitter.com/1/followers/ids.json" [:user-id] [] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method followers-of-name :get "api.twitter.com/1/followers/ids.json" [:screen-name] [] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method verify-credentials :get "api.twitter.com/1/account/verify_credentials.json" [] [] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method rate-limit-status :get "api.twitter.com/1/account/rate_limit_status.json" [] [] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method end-session :post "api.twitter.com/1/account/end_session.json" [] [] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method update-delivery-device :post "api.twitter.com/1/account/update_delivery_device.json" [:device] [] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method update-profile-colors :post @@ -337,7 +342,7 @@ take any required and optional arguments and call the associated Twitter method. :profile-link-color :profile-sidebar-fill-color :profile-sidebar-border-color] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (comment (def-twitter-method update-profile-image @@ -345,11 +350,11 @@ take any required and optional arguments and call the associated Twitter method. "api.twitter.com/1/account/update_profile_image.json" [:image] [] - (comp #(:content %) status-handler))) + (comp :content status-handler))) (defn update-profile-image [^String image] (let [req-uri__9408__auto__ "http://api.twitter.com/1/account/update_profile_image.json" - + oauth-creds__9414__auto__ (when (and *oauth-consumer* @@ -359,7 +364,7 @@ take any required and optional arguments and call the associated Twitter method. *oauth-access-token* :post req-uri__9408__auto__))] - ((comp #(:content %) status-handler) + ((comp :content status-handler) (http/post req-uri__9408__auto__ :query @@ -376,49 +381,51 @@ take any required and optional arguments and call the associated Twitter method. "api.twitter.com/1/account/update_profile_background_image.json" [:image] [:title] - (comp #(:content %) status-handler))) + (comp :content status-handler))) (defn update-profile-background-image [^String image & rest__2570__auto__] - (let [req-uri__2571__auto__ "http://api.twitter.com/1/account/update_profile_background_image.json" - rest-map__2572__auto__ (apply hash-map rest__2570__auto__) - provided-optional-params__2573__auto__ (set/intersection - (set [:title]) - (set - (keys - rest-map__2572__auto__))) - query-param-names__2574__auto__ (sort - (map - (fn - [x__2575__auto__] - (keyword - (string/replace - (name - x__2575__auto__) - #"-" - "_" - ))) - provided-optional-params__2573__auto__)) - query-params__2576__auto__ (apply - hash-map - (interleave - query-param-names__2574__auto__ - (vec - (vals - (sort - (select-keys - rest-map__2572__auto__ - provided-optional-params__2573__auto__)))))) - oauth-creds__2577__auto__ (when - (and - *oauth-consumer* - *oauth-access-token*) - (oauth/credentials - *oauth-consumer* - *oauth-access-token* - :post - req-uri__2571__auto__ - query-params__2576__auto__))] - ((comp #(:content %) status-handler) + (let [req-uri__2571__auto__ + "http://api.twitter.com/1/account/update_profile_background_image.json" + + rest-map__2572__auto__ (apply hash-map rest__2570__auto__) + provided-optional-params__2573__auto__ (set/intersection + (set [:title]) + (set + (keys + rest-map__2572__auto__))) + query-param-names__2574__auto__ (sort + (map + (fn + [x__2575__auto__] + (keyword + (string/replace + (name + x__2575__auto__) + #"-" + "_" + ))) + provided-optional-params__2573__auto__)) + query-params__2576__auto__ (apply + hash-map + (interleave + query-param-names__2574__auto__ + (vec + (vals + (sort + (select-keys + rest-map__2572__auto__ + provided-optional-params__2573__auto__)))))) + oauth-creds__2577__auto__ (when + (and + *oauth-consumer* + *oauth-access-token*) + (oauth/credentials + *oauth-consumer* + *oauth-access-token* + :post + req-uri__2571__auto__ + query-params__2576__auto__))] + ((comp :content status-handler) (http/post req-uri__2571__auto__ :query (merge query-params__2576__auto__ oauth-creds__2577__auto__) :parameters (http/map->params {:use-expect-continue false}) @@ -430,12 +437,12 @@ take any required and optional arguments and call the associated Twitter method. :post "api.twitter.com/1/account/update_profile.json" [] - [:name + [:name :email :url :location :description] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method favorites :get @@ -443,119 +450,128 @@ take any required and optional arguments and call the associated Twitter method. [] [:id :page] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method create-favorite :post "api.twitter.com/1/favorites/create.json" [:id] [] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method destroy-favorite :post "api.twitter.com/1/favorites/destroy.json" [:id] [] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method notifications-follow-by-id :post "api.twitter.com/1/notifications/follow.json" [:user-id] [] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method notifications-follow-by-name :post "api.twitter.com/1/notifications/follow.json" [:screen-name] [] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method notifications-leave-by-id :post "api.twitter.com/1/notifications/leave.json" [:user-id] [] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method notifications-leave-by-name :post "api.twitter.com/1/notifications/leave.json" [:screen-name] [] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method create-block :post "api.twitter.com/1/blocks/create.json" [:user-id-or-screen-name] [] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method destroy-block :post "api.twitter.com/1/blocks/destroy.json" [:user-id-or-screen-name] [] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method block-exists-for-id :get "api.twitter.com/1/blocks/exists.json" [:user-id] [] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method block-exists-for-name :get "api.twitter.com/1/blocks/exists.json" [:screen-name] [] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method blocking-users :get "api.twitter.com/1/blocks/blocking.json" [] [:page] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method blocking-user-ids :get "api.twitter.com/1/blocks/blocking/ids.json" [] [] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method saved-searches :get "api.twitter.com/1/saved_searches.json" [] [] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method show-saved-search :get "api.twitter.com/1/saved_searches/show.json" [:id] [] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method create-saved-search :post "api.twitter.com/1/saved_searches/create.json" [:query] [] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method destroy-saved-search :post "api.twitter.com/1/saved_searches/destroy.json" [:id] [] - (comp #(:content %) status-handler)) + (comp :content status-handler)) + +(def-twitter-method users-search + :get + "api.twitter.com/1/users/search.json" + [:q] + [:page + :per_page + :include_entities] + (comp :content status-handler)) (def-twitter-method search :get @@ -569,21 +585,21 @@ take any required and optional arguments and call the associated Twitter method. :max-id :geocode :show-user] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method trends :get "search.twitter.com/trends.json" [] [] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method current-trends :get "search.twitter.com/trends/current.json" [] [:exclude] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method daily-trends :get @@ -591,7 +607,7 @@ take any required and optional arguments and call the associated Twitter method. [] [:date :exclude] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (def-twitter-method weekly-trends :get @@ -599,27 +615,29 @@ take any required and optional arguments and call the associated Twitter method. [] [:date :exclude] - (comp #(:content %) status-handler)) + (comp :content status-handler)) (defn status-handler "Handle the various HTTP status codes that may be returned when accessing the Twitter API." [result] - (condp #(if (coll? %1) + (condp #(if (coll? %1) (first (filter (fn [x] (== x %2)) %1)) (== %2 %1)) (:code result) 200 result 304 nil - [400 401 403 404 406 500 502 503] (let [body (:content result) - headers (into {} (:headers result)) - error-msg (:error body) - error-code (:code result) - request-uri (:request body)] - (throw (proxy [Exception] [(str "[" error-code "] " error-msg ". [" request-uri "]")] - (request [] (body "request")) - (remaining-requests [] (headers "X-RateLimit-Remaining")) - (rate-limit-reset [] (java.util.Date. - (long (headers "X-RateLimit-Reset"))))))))) + [400 401 403 404 406 500 502 503] + (let [body (:content result) + headers (into {} (:headers result)) + error-msg (:error body) + error-code (:code result) + request-uri (:request body)] + (throw (proxy [Exception] + [(str "[" error-code "] " error-msg ". [" request-uri "]")] + (request [] (body "request")) + (remaining-requests [] (headers "X-RateLimit-Remaining")) + (rate-limit-reset [] (java.util.Date. + (long (headers "X-RateLimit-Reset"))))))))) (defn make-rate-limit-handler "Creates a handler that will only be called if the API rate limit has been exceeded."