├── .gitignore ├── LICENSE ├── README.textile ├── Rakefile ├── lib ├── twitter_oauth.rb └── twitter_oauth │ ├── account.rb │ ├── blocks.rb │ ├── client.rb │ ├── direct_messages.rb │ ├── favorites.rb │ ├── friendships.rb │ ├── geo.rb │ ├── help.rb │ ├── lists.rb │ ├── notifications.rb │ ├── saved_searches.rb │ ├── search.rb │ ├── spam.rb │ ├── status.rb │ ├── timeline.rb │ ├── trends.rb │ ├── user.rb │ └── utils.rb ├── test ├── client_test.rb └── test_helper.rb └── twitter_oauth.gemspec /.gitignore: -------------------------------------------------------------------------------- 1 | mytest.rb 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) Richard Taylor 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.textile: -------------------------------------------------------------------------------- 1 | h1. Twitter OAuth REST API client library for Ruby 2 | 3 | h2. Current Status 4 | 5 | Twitter turned off access to the v1 API so this project is currently migrating to v1.1. Unfortunately everything is not backward-compatible. 6 | 7 | The v.0.4.9x series of this gem is work in progress getting the library compatible with Twitter v1.1 - when complete we will push v0.5.0 of this library. 8 | 9 | h2. Install the gem 10 | 11 | gem 'twitter_oauth' 12 | 13 | h2. Using the gem 14 | 15 | To make authorized requests with the client library you'll need to "create a Twitter OAuth Application":http://twitter.com/oauth_clients/new. 16 | 17 | See "sinitter":http://github.com/moomerman/sinitter/tree/master for a full website integration example. 18 | 19 | h2. Unauthorized request example 20 | 21 | Since Twitter API v1.1 all requests are required to be authorized. 22 | 23 | h2. Authorized request example 24 | 25 | To use the full power of the Twitter API you need to authorize your application and a valid Twitter user via OAuth. 26 | An example showing how to update the status of an authorized user is below. 27 | 28 | Firstly we need to create an instance of the client with your application client credentials you have been given by Twitter 29 | when you set up your application. 30 | 31 |
client = TwitterOAuth::Client.new(
 32 |     :consumer_key => 'YOUR_APP_CONSUMER_KEY',
 33 |     :consumer_secret => 'YOUR_APP_CONSUMER_SECRET'
 34 | )
 35 | request_token = client.request_token(:oauth_callback => oauth_confirm_url)
 36 | #:oauth_callback required for web apps, since oauth gem by default force PIN-based flow
 37 | #( see http://groups.google.com/group/twitter-development-talk/browse_thread/thread/472500cfe9e7cdb9/848f834227d3e64d )
 38 | 
 39 | 
 40 | request_token.authorize_url
 41 | => http://twitter.com/oauth/authorize?oauth_token=TOKEN
 42 | 
43 | 44 | In your application your user would be redirected to Twitter to authorize the application at this point. You'll need to store 45 | the request token (usually in the session) for later. The code continues below assuming the user has authorized your application. 46 | 47 | NOTE: Above we called the client.request_token(...) method, this authorizes the application on every call. You can also use the client.authentication_request_token(...) method which automatically redirects back to your application if the user has previously authorized the app. 48 | 49 |
access_token = client.authorize(
 50 |   request_token.token,
 51 |   request_token.secret,
 52 |   :oauth_verifier => params[:oauth_verifier]
 53 | )
 54 | 
 55 | client.authorized?
 56 | => true
 57 | 
 58 | client.update('checking out the twitter_oauth library') # sends a twitter status update
 59 | 
60 | 61 | Now if you keep hold of the access_token (usually in the database) for this user you won't need to re-authorize them next time. When you create an instance of the client you can just pass in the access token and secret that you have stored. 62 | 63 |
access_token = @user.access_token # assuming @user
 64 | client = TwitterOAuth::Client.new(
 65 |     :consumer_key => 'YOUR_CONSUMER_KEY',
 66 |     :consumer_secret => 'YOUR_APP_CONSUMER_SECRET',
 67 |     :token => access_token.token,
 68 |     :secret => access_token.secret
 69 | )
 70 | 
 71 | client.authorized?
 72 | => true
 73 | 
74 | 75 | h2. PIN-based flow 76 | 77 | If you're writing a command line application or desktop app, you will probably want to use the PIN-based authorization method rather than the website redirect method. 78 | 79 |
client = TwitterOAuth::Client.new(
 80 |   :consumer_key => 'YOUR_CONSUMER_KEY',
 81 |   :consumer_secret => 'YOUR_APP_CONSUMER_SECRET'
 82 | )
 83 | 
 84 | request_token = client.authentication_request_token(
 85 |   :oauth_callback => 'oob'
 86 | )
 87 | 
 88 | puts request_token.authorize_url
 89 | 
 90 | print 'Please visit the URL and enter the code: '
 91 | code = gets.strip
 92 | 
 93 | access_token = client.authorize(
 94 |   request_token.token,
 95 |   request_token.secret,
 96 |   :oauth_verifier => code
 97 | )
 98 | 
 99 | client.authorized?
100 | => true
101 | 
102 | 103 | The special oauth callback value of oob tells Twitter you want to do the PIN-based authentication. The user goes to the authorization URL to get their unique code and they paste that into your application. Finally we authorize the user with this code. 104 | 105 | h2. Working with a Proxy 106 | 107 | Services such as "Apigee Analytics and API Management":http://apigee.com/ require you to proxy your API requests through their servers. The workflow is as follows. 108 | 109 | First you need to authorize the Twitter user via OAuth directly via the Twitter API (this part cannot be proxied) 110 | 111 |
client = TwitterOAuth::Client.new(
112 |     :consumer_key => 'YOUR_APP_CONSUMER_KEY',
113 |     :consumer_secret => 'YOUR_APP_CONSUMER_SECRET'
114 | )
115 | request_token = client.request_token(:oauth_callback => 'YOUR_CALLBACK_URL')
116 | 
117 | request_token.authorize_url
118 | => http://twitter.com/oauth/authorize?oauth_token=TOKEN
119 | 
120 | 121 | The user is sent to Twitter to allow the application to access their account and then you need to obtain the access token for the user 122 | 123 |
access_token = client.authorize(
124 |   request_token.token,
125 |   request_token.secret,
126 |   :oauth_verifier => params[:oauth_verifier]
127 | )
128 | 
129 | client.authorized?
130 | => true
131 | 
132 | 133 | Now you can make all further API calls through the proxy of your choice: 134 | 135 |
access_token = @user.access_token # assuming @user
136 | client = TwitterOAuth::Client.new(
137 |     :proxy => 'http://XXX.YYY.apigee.com',
138 |     :consumer_key => 'YOUR_CONSUMER_KEY',
139 |     :consumer_secret => 'YOUR-CONSUMER-SECRET',
140 |     :token => access_token.token,
141 |     :secret => access_token.secret
142 | )
143 | 
144 | client.authorized?
145 | => true
146 | 
147 | client.update('Proxy via Apigee is working')
148 | 
149 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake/testtask' 2 | require 'shoulda/tasks' 3 | 4 | 5 | task :default => ["test:units"] 6 | 7 | desc "Run basic tests" 8 | Rake::TestTask.new("test:units") { |t| 9 | t.pattern = 'test/**/*_test.rb' 10 | t.verbose = true 11 | t.warning = true 12 | } 13 | -------------------------------------------------------------------------------- /lib/twitter_oauth.rb: -------------------------------------------------------------------------------- 1 | require 'oauth' 2 | require 'json' 3 | require 'mime/types' 4 | 5 | require 'twitter_oauth/client' 6 | 7 | module TwitterOAuth 8 | VERSION = '0.4.94' 9 | end 10 | -------------------------------------------------------------------------------- /lib/twitter_oauth/account.rb: -------------------------------------------------------------------------------- 1 | module TwitterOAuth 2 | class Client 3 | 4 | # Returns an HTTP 200 OK response code and a representation of the requesting user if authentication was successful; 5 | # returns a 401 status code and an error message if not. 6 | def authorized? 7 | puts "[Client] GET /account/verify_credentials.json" if @debug 8 | oauth_response = access_token.get("/#{@api_version}/account/verify_credentials.json") 9 | return oauth_response.class == Net::HTTPOK 10 | end 11 | 12 | def info 13 | get('/account/verify_credentials.json') 14 | end 15 | 16 | # Updates profile background image. Takes a File object and optional tile argument. 17 | # Returns extended user info object. 18 | def update_profile_background_image(image, tile = false) 19 | body, headers = http_multipart_data({:image => image, :tile => tile}) 20 | post('/account/update_profile_background_image.json', body, headers) 21 | end 22 | 23 | # Updates profile avatar image. Takes a File object which should be an image. 24 | # Returns extended user info object. 25 | def update_profile_image(image) 26 | body, headers = http_multipart_data({:image => image}) 27 | post('/account/update_profile_image.json', body, headers) 28 | end 29 | 30 | # colors hash must contain at least one or more of the following keys :profile_background_color, :profile_text_color, :profile_link_color, :profile_sidebar_fill_color, :profile_sidebar_border_color 31 | # returns extended user info object. 32 | def update_profile_colors(colors) 33 | post('/account/update_profile_colors.json', colors) 34 | end 35 | 36 | # Sets values that users are able to set under the "Account" tab of their settings page. 37 | # Valid parameters are: 38 | # :name Full name associated with the profile. Maximum of 20 characters. 39 | # :url URL associated with the profile. Will be prepended with "http://" if not present. Maximum of 100 characters. 40 | # :location The city or country describing where the user of the account is located. The contents are not normalized 41 | # or geocoded in any way. Maximum of 30 characters. 42 | # :description A description of the user owning the account. Maximum of 160 characters. 43 | def update_profile(params) 44 | post('/account/update_profile', params) 45 | end 46 | 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /lib/twitter_oauth/blocks.rb: -------------------------------------------------------------------------------- 1 | module TwitterOAuth 2 | class Client 3 | 4 | # Blocks the user specified in the ID parameter as the authenticating user. 5 | # Destroys a friendship to the blocked user if it exists. 6 | # Returns the blocked user in the requested format when successful. 7 | def block(id) 8 | post("/blocks/create/#{id}.json") 9 | end 10 | 11 | # Un-blocks the user specified in the ID parameter for the authenticating user. 12 | # Returns the un-blocked user in the requested format when successful. 13 | def unblock(id) 14 | post("/blocks/destroy/#{id}.json") 15 | end 16 | 17 | # Returns if the authenticating user is blocking a target user. 18 | def blocked?(id) 19 | get("/blocks/exists/#{id}.json") 20 | end 21 | 22 | # Returns an array of user objects that the authenticating user is blocking. 23 | def blocking 24 | get("/blocks/blocking.json") 25 | end 26 | 27 | # Returns an array of numeric user ids the authenticating user is blocking. 28 | def blocking_ids 29 | get("/blocks/blocking/ids.json") 30 | end 31 | 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/twitter_oauth/client.rb: -------------------------------------------------------------------------------- 1 | require 'twitter_oauth/timeline' 2 | require 'twitter_oauth/status' 3 | require 'twitter_oauth/account' 4 | require 'twitter_oauth/direct_messages' 5 | require 'twitter_oauth/search' 6 | require 'twitter_oauth/notifications' 7 | require 'twitter_oauth/blocks' 8 | require 'twitter_oauth/friendships' 9 | require 'twitter_oauth/user' 10 | require 'twitter_oauth/favorites' 11 | require 'twitter_oauth/utils' 12 | require 'twitter_oauth/trends' 13 | require 'twitter_oauth/lists' 14 | require 'twitter_oauth/saved_searches' 15 | require 'twitter_oauth/spam' 16 | require 'twitter_oauth/geo' 17 | require 'twitter_oauth/help' 18 | 19 | module TwitterOAuth 20 | class Client 21 | 22 | def initialize(options = {}) 23 | @consumer_key = options[:consumer_key] 24 | @consumer_secret = options[:consumer_secret] 25 | @token = options[:token] 26 | @secret = options[:secret] 27 | @proxy = options[:proxy] 28 | @debug = options[:debug] 29 | @api_version = options[:api_version] || '1.1' 30 | @api_host = options[:api_host] || 'api.twitter.com' 31 | end 32 | 33 | def authorize(token, secret, options = {}) 34 | request_token = OAuth::RequestToken.new( 35 | consumer, token, secret 36 | ) 37 | @access_token = request_token.get_access_token(options) 38 | @token = @access_token.token 39 | @secret = @access_token.secret 40 | @access_token 41 | end 42 | 43 | def show(username) 44 | get("/users/show/#{username}.json") 45 | end 46 | 47 | def request_token(options={}) 48 | consumer.get_request_token(options) 49 | end 50 | 51 | def authentication_request_token(options={}) 52 | consumer.options[:authorize_path] = '/oauth/authenticate' 53 | request_token(options) 54 | end 55 | 56 | private 57 | 58 | def consumer(options={}) 59 | @consumer ||= OAuth::Consumer.new( 60 | @consumer_key, 61 | @consumer_secret, 62 | { :site => "https://#{@api_host}", :request_endpoint => @proxy } 63 | ) 64 | end 65 | 66 | def access_token 67 | @access_token ||= OAuth::AccessToken.new(consumer, @token, @secret) 68 | end 69 | 70 | def get(path, headers={}) 71 | puts "[Client] GET #{path}" if @debug 72 | headers.merge!("User-Agent" => "twitter_oauth gem v#{TwitterOAuth::VERSION}") 73 | oauth_response = access_token.get("/#{@api_version}#{path}", headers) 74 | parse(oauth_response.body) 75 | end 76 | 77 | def post(path, body='', headers={}) 78 | puts "[Client] POST #{path}" if @debug 79 | headers.merge!("User-Agent" => "twitter_oauth gem v#{TwitterOAuth::VERSION}") 80 | headers.merge!("Content-Type" => "application/x-www-form-urlencoded\r\n") 81 | oauth_response = access_token.post("/#{@api_version}#{path}", body, headers) 82 | parse(oauth_response.body) 83 | end 84 | 85 | def delete(path, headers={}) 86 | puts "[Client] DELETE #{path}" if @debug 87 | headers.merge!("User-Agent" => "twitter_oauth gem v#{TwitterOAuth::VERSION}") 88 | oauth_response = access_token.delete("/#{@api_version}#{path}", headers) 89 | parse(oauth_response.body) 90 | end 91 | 92 | def parse(response_body) 93 | begin 94 | JSON.parse(response_body) 95 | rescue JSON::ParserError 96 | {:response => response_body}.to_json 97 | end 98 | end 99 | end 100 | end 101 | 102 | -------------------------------------------------------------------------------- /lib/twitter_oauth/direct_messages.rb: -------------------------------------------------------------------------------- 1 | module TwitterOAuth 2 | class Client 3 | 4 | # Return the most recent direct messages sent to the authenticating user. 5 | # By default, returns the last 20. See http://apiwiki.twitter.com/Twitter-REST-API-Method:-direct_messages 6 | # for other options 7 | def messages(options={}) 8 | args = options.map{|k,v| "#{k}=#{v}"}.join('&') 9 | get("/direct_messages.json?#{args}") 10 | end 11 | 12 | # By default, returns a list of the 20 most recent direct messages sent by the authenticating user. 13 | def sent_messages(options={}) 14 | args = options.map{|k,v| "#{k}=#{v}"}.join('&') 15 | get("/direct_messages/sent.json?#{args}") 16 | end 17 | 18 | # Sends a new direct message to the specified user from the authenticating user. 19 | def message(user, text) 20 | post('/direct_messages/new.json', :user => user, :text => text) 21 | end 22 | 23 | # Destroys the direct message specified in the required ID parameter. 24 | def message_destroy(id) 25 | post("/direct_messages/destroy/#{id}.json") 26 | end 27 | 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/twitter_oauth/favorites.rb: -------------------------------------------------------------------------------- 1 | module TwitterOAuth 2 | class Client 3 | 4 | # Returns the 20 most recent favorite statuses for the authenticating user or user specified by the ID parameter. 5 | def favorites(page=1) 6 | get("/favorites.json?page=#{page}") 7 | end 8 | 9 | # Favorites the status specified in the ID parameter as the authenticating user. 10 | # Returns the favorite status when successful. 11 | def favorite(id) 12 | post("/favorites/create/#{id}.json") 13 | end 14 | 15 | # Un-favorites the status specified in the ID parameter as the authenticating user. 16 | # Returns the un-favorited status when successful. 17 | def unfavorite(id) 18 | post("/favorites/destroy/#{id}.json") 19 | end 20 | 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/twitter_oauth/friendships.rb: -------------------------------------------------------------------------------- 1 | module TwitterOAuth 2 | class Client 3 | 4 | # Returns an array of numeric IDs for every user the specified user is following. 5 | def friends_ids(options={}) 6 | args = options.map{|k,v| "#{k}=#{v}"}.join('&') 7 | get("/friends/ids.json?#{args}") 8 | end 9 | 10 | # Returns an array of numeric IDs for every user following the specified user. 11 | def followers_ids(options={}) 12 | args = options.map{|k,v| "#{k}=#{v}"}.join('&') 13 | get("/followers/ids.json?#{args}") 14 | end 15 | 16 | # Allows the authenticating user to follow the specified user. Returns the befriended user when successful. 17 | def friend(id) 18 | post("/friendships/create/#{id}.json") 19 | end 20 | 21 | # Allows the authenticating users to unfollow the specified user. Returns the unfollowed user when successful. 22 | def unfriend(id) 23 | post("/friendships/destroy/#{id}.json") 24 | end 25 | 26 | # Tests for the existence of friendship between two users. Will return true if user_a follows user_b, otherwise will return false. 27 | # You are better off using get_friendship as it returns more extended information. 28 | def friends?(a, b) 29 | oauth_response = access_token.get("/friendships/exists.json?user_a=#{a}&user_b=#{b}") 30 | oauth_response.body.strip == 'true' 31 | end 32 | 33 | # Returns detailed information about the relationship between two users. 34 | def get_friendship(a, b) 35 | get("/friendships/show.json?source_screen_name=#{a}&target_screen_name=#{b}") 36 | end 37 | 38 | # Returns a cursored collection of user objects for every user the specified 39 | # user is following (otherwise known as their "friends") 40 | def friends(cursor=-1) 41 | get("/friends/list.json?cursor=#{cursor}") 42 | end 43 | 44 | # Helper to retrun all friends via multiple requests 45 | def all_friends(cursor=-1) 46 | users = [] 47 | while cursor != 0 do 48 | json = friends(cursor) 49 | cursor = json["next_cursor"] 50 | users += json["users"] 51 | end 52 | users 53 | end 54 | 55 | # Returns a cursored collection of user objects for users following the 56 | # specified user. 57 | def followers(cursor=-1) 58 | get("/followers/list.json?cursor=#{cursor}") 59 | end 60 | 61 | # Helper to retrun all followers via multiple requests 62 | def all_followers(cursor=-1) 63 | users = [] 64 | while cursor != 0 do 65 | json = followers(cursor) 66 | cursor = json["next_cursor"] 67 | users += json["users"] 68 | end 69 | users 70 | end 71 | 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /lib/twitter_oauth/geo.rb: -------------------------------------------------------------------------------- 1 | module TwitterOAuth 2 | class Client 3 | 4 | # Search for places (cities and neighborhoods) that can be attached to a statuses/update. 5 | # Given a latitude and a longitude, return a list of all the valid places that can be used as a place_id when updating a status 6 | def reverse_geocode(lat, lng, options={}) 7 | options[:lat] = lat; options[:lng] = lng 8 | args = options.map{|k,v| "#{k}=#{v}"}.join('&') 9 | get "/geo/reverse_geocode.json?#{args}" 10 | end 11 | 12 | # Search for places (cities and neighborhoods) that can be attached to a statuses/update. 13 | # Given a latitude and a longitude pair, or an IP address, return a list of all the valid cities 14 | # and neighborhoods that can be used as a place_id when updating a status. 15 | def geo_search(options={}) 16 | options[:query] = URI.escape(options[:query]) if options[:query] 17 | args = options.map{|k,v| "#{k}=#{v}"}.join('&') 18 | get "/geo/search.json?#{args}" 19 | end 20 | 21 | # Find out more details of a place that was returned from the geo/reverse_geocode method. 22 | def geo(id) 23 | get "/geo/id/#{id}.json" 24 | end 25 | 26 | end 27 | end -------------------------------------------------------------------------------- /lib/twitter_oauth/help.rb: -------------------------------------------------------------------------------- 1 | module TwitterOAuth 2 | class Client 3 | 4 | # Returns the current rate limits for methods belonging to the specified 5 | # resource families. Each 1.1 API resource belongs to a "resource family" 6 | # which is indicated in its method documentation. You can typically 7 | # determine a method's resource family from the first component of the path 8 | # after the... 9 | def rate_limit_status 10 | get('/application/rate_limit_status.json') 11 | end 12 | 13 | end 14 | end -------------------------------------------------------------------------------- /lib/twitter_oauth/lists.rb: -------------------------------------------------------------------------------- 1 | module TwitterOAuth 2 | class Client 3 | 4 | # 5 | # List methods 6 | # 7 | 8 | # Creates a new list for the authenticated user. Accounts are limited to 20 lists. 9 | def create_list(user, list, options={}) 10 | post("/#{user}/lists.json", options.merge(:name => list)) 11 | end 12 | 13 | # Updates the specified list. 14 | def update_list(user, list, options={}) 15 | post("/#{user}/lists/#{list}.json", options) 16 | end 17 | 18 | # List the lists of the specified user. 19 | # Private lists will be included if the authenticated user is the same as the user whose lists are being returned. 20 | def get_lists(user) 21 | get("/#{user}/lists.json") 22 | end 23 | 24 | # Show the specified list. Private lists will only be shown if the authenticated user owns the specified list. 25 | def get_list(user, list) 26 | get("/#{user}/lists/#{list}.json") 27 | end 28 | 29 | # Deletes the specified list. Must be owned by the authenticated user. 30 | def delete_list(user, list) 31 | delete("/#{user}/lists/#{list}.json") 32 | end 33 | 34 | # Show tweet timeline for members of the specified list. 35 | def list_statuses(user, list) 36 | get("/#{user}/lists/#{list}/statuses.json") 37 | end 38 | 39 | # List the lists the specified user has been added to. 40 | def list_memberships(user) 41 | get("/#{user}/lists/memberships.json") 42 | end 43 | 44 | # List the lists the specified user follows. 45 | def list_subscriptions(user) 46 | get("/#{user}/lists/subscriptions.json") 47 | end 48 | 49 | # 50 | # List Members Methods 51 | # 52 | 53 | # Returns the members of the specified list. 54 | def list_members(user, list) 55 | get("/#{user}/#{list}/members.json") 56 | end 57 | 58 | # Add a member to a list. The authenticated user must own the list to be able to add members to it. 59 | # Lists are limited to having 500 members. 60 | def add_member_to_list(user, list, member_id, options={}) 61 | post("/#{user}/#{list}/members.json", options.merge(:id => member_id)) 62 | end 63 | 64 | # Removes the specified member from the list. 65 | # The authenticated user must be the list's owner to remove members from the list. 66 | def remove_member_from_list(user, list, member_id) 67 | delete("/#{user}/#{list}/members.json?id=#{member_id}") 68 | end 69 | 70 | # Check if a user is a member of the specified list. 71 | def get_member_of_list(user, list, member_id) 72 | get("/#{user}/#{list}/members/#{member_id}.json") 73 | end 74 | 75 | # 76 | # List Subscribers Methods 77 | # 78 | 79 | # Returns the subscribers of the specified list. 80 | def list_subscribers(user, list) 81 | get("/#{user}/#{list}/subscribers.json") 82 | end 83 | 84 | # Make the authenticated user follow the specified list. 85 | def subscribe_to_list(user, list, options={}) 86 | post("/#{user}/#{list}/subscribers.json") 87 | end 88 | 89 | # Unsubscribes the authenticated user form the specified list. 90 | def unsubscribe_from_list(user, list) 91 | delete("/#{user}/#{list}/subscribers.json") 92 | end 93 | 94 | # Check if the specified user is a subscriber of the specified list. 95 | def get_subscriber_of_list(user, list, subscriber_id) 96 | get("/#{user}/#{list}/subscribers/#{subscriber_id}.json") 97 | end 98 | 99 | end 100 | end -------------------------------------------------------------------------------- /lib/twitter_oauth/notifications.rb: -------------------------------------------------------------------------------- 1 | module TwitterOAuth 2 | class Client 3 | 4 | # Enables device notifications for updates from the specified user. 5 | # Returns the specified user when successful. 6 | def follow(id) 7 | post("/notifications/follow/#{id}.json") 8 | end 9 | 10 | # Disables notifications for updates from the specified user to the authenticating user. 11 | # Returns the specified user when successful. 12 | def leave(id) 13 | post("/notifications/leave/#{id}.json") 14 | end 15 | 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/twitter_oauth/saved_searches.rb: -------------------------------------------------------------------------------- 1 | module TwitterOAuth 2 | class Client 3 | 4 | # Returns the authenticated user's saved search queries. 5 | def saved_searches 6 | get("/saved_searches.json") 7 | end 8 | 9 | # Retrieve the data for a saved search owned by the authenticating user specified by the given id. 10 | def get_saved_search(search_id) 11 | get("/saved_searches/show/#{search_id}.json") 12 | end 13 | 14 | # Creates a saved search for the authenticated user. 15 | def create_saved_search(query) 16 | post("/saved_searches/create.json", :query => query) 17 | end 18 | 19 | def delete_saved_search(search_id) 20 | post("/saved_searches/destroy/#{search_id}.json") 21 | end 22 | 23 | end 24 | end -------------------------------------------------------------------------------- /lib/twitter_oauth/search.rb: -------------------------------------------------------------------------------- 1 | require 'open-uri' 2 | 3 | module TwitterOAuth 4 | class Client 5 | 6 | def search(q, options={}) 7 | options[:count] ||= 20 8 | options[:q] = URI.escape(q) 9 | args = options.map{|k,v| "#{k}=#{v}"}.join('&') 10 | get("/search/tweets.json?#{args}") 11 | end 12 | 13 | end 14 | end -------------------------------------------------------------------------------- /lib/twitter_oauth/spam.rb: -------------------------------------------------------------------------------- 1 | module TwitterOAuth 2 | class Client 3 | 4 | # The user specified in the id is blocked by the authenticated user and reported as a spammer. 5 | def report_spam(user) 6 | post("/report_spam.json", :id => user) 7 | end 8 | 9 | end 10 | end -------------------------------------------------------------------------------- /lib/twitter_oauth/status.rb: -------------------------------------------------------------------------------- 1 | module TwitterOAuth 2 | class Client 3 | 4 | # Returns a single status, specified by the id parameter below. 5 | def status(id) 6 | get("/statuses/show/#{id}.json") 7 | end 8 | 9 | # Updates the authenticating user's status. 10 | def update(message, options={}) 11 | post('/statuses/update.json', options.merge(:status => message)) 12 | end 13 | 14 | # Destroys the status specified by the required ID parameter 15 | def status_destroy(id) 16 | post("/statuses/destroy/#{id}.json") 17 | end 18 | 19 | # Retweets the tweet specified by the id parameter. Returns the original tweet with retweet details embedded. 20 | def retweet(id) 21 | post("/statuses/retweet/#{id}.json") 22 | end 23 | 24 | # Returns the 100 most recent retweets of the tweet. 25 | def retweets(id) 26 | get("/statuses/retweets/#{id}.json") 27 | end 28 | 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/twitter_oauth/timeline.rb: -------------------------------------------------------------------------------- 1 | module TwitterOAuth 2 | class Client 3 | 4 | # Returns the 20 most recent statuses from non-protected users who have set a custom user icon. 5 | def public_timeline(options={}) 6 | args = options.map{|k,v| "#{k}=#{v}"}.join('&') 7 | get("/statuses/public_timeline.json?#{args}") 8 | end 9 | 10 | # Returns the 20 most recent statuses, including retweets, posted by the authenticating user and that user's friends. 11 | # This is the equivalent of /timeline/home on the Web. 12 | def home_timeline(options={}) 13 | args = options.map{|k,v| "#{k}=#{v}"}.join('&') 14 | get("/statuses/home_timeline.json?#{args}") 15 | end 16 | 17 | # Returns the 20 most recent statuses posted by the authenticating user and that user's friends. 18 | def friends_timeline(options={}) 19 | args = options.map{|k,v| "#{k}=#{v}"}.join('&') 20 | get("/statuses/friends_timeline.json?#{args}") 21 | end 22 | 23 | # Returns the 20 most recent statuses posted from the authenticating user. 24 | def user_timeline(options={}) 25 | args = options.map{|k,v| "#{k}=#{v}"}.join('&') 26 | get("/statuses/user_timeline.json?#{args}") 27 | end 28 | alias :user :user_timeline 29 | 30 | # Returns the 20 most recent @replies (status updates prefixed with @username) for the authenticating user. 31 | def mentions(options={}) 32 | args = options.map{|k,v| "#{k}=#{v}"}.join('&') 33 | get("/statuses/mentions_timeline.json?#{args}") 34 | end 35 | alias :replies :mentions 36 | 37 | # Returns the 20 most recent retweets posted by the authenticating user 38 | def retweeted_by_me(options={}) 39 | args = options.map{|k,v| "#{k}=#{v}"}.join('&') 40 | get("/statuses/retweeted_by_me.json?#{args}") 41 | end 42 | 43 | # Returns the 20 most recent retweets posted by the authenticating user's friends. 44 | def retweeted_to_me(options={}) 45 | args = options.map{|k,v| "#{k}=#{v}"}.join('&') 46 | get("/statuses/retweeted_to_me.json?#{args}") 47 | end 48 | 49 | # Returns the 20 most recent tweets of the authenticated user that have been retweeted by others. 50 | def retweets_of_me(options={}) 51 | args = options.map{|k,v| "#{k}=#{v}"}.join('&') 52 | get("/statuses/retweets_of_me.json?#{args}") 53 | end 54 | 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /lib/twitter_oauth/trends.rb: -------------------------------------------------------------------------------- 1 | module TwitterOAuth 2 | class Client 3 | 4 | # Returns the top 10 trending topics for a specific WOEID, if trending 5 | # information is available for it. The response is an array of "trend" 6 | # objects that encode the name of the trending topic, the query parameter 7 | # that can be used to search for the topic on Twitter Search, and the 8 | # Twitter Search URL.... 9 | def place_trends 10 | get("/trends/place.json") 11 | end 12 | 13 | # Returns the locations that Twitter has trending topic information for. The 14 | # response is an array of "locations" that encode the location's WOEID and 15 | # some other human-readable information such as a canonical name and country 16 | # the location belongs in. A WOEID is a Yahoo! Where On Earth ID. 17 | def available_trends 18 | get("/trends/available.json") 19 | end 20 | 21 | # Returns the locations that Twitter has trending topic information for, 22 | # closest to a specified location. The response is an array of "locations" 23 | # that encode the location's WOEID and some other human-readable information 24 | # such as a canonical name and country the location belongs in. 25 | # A WOEID is a Yahoo... 26 | def closest_trends 27 | get("/trends/closest.json") 28 | end 29 | 30 | end 31 | end -------------------------------------------------------------------------------- /lib/twitter_oauth/user.rb: -------------------------------------------------------------------------------- 1 | module TwitterOAuth 2 | class Client 3 | 4 | # Returns settings (including current trend, geo and sleep time information) 5 | # for the authenticating user. 6 | def settings 7 | get('/account/settings.json') 8 | end 9 | 10 | # Returns an HTTP 200 OK response code and a representation of the 11 | # requesting user if authentication was successful; returns a 401 status 12 | # code and an error message if not. Use this method to test if supplied user 13 | # credentials are valid. 14 | def verify_credentials 15 | get('/account/verify_credentials.json') 16 | end 17 | 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/twitter_oauth/utils.rb: -------------------------------------------------------------------------------- 1 | module TwitterOAuth 2 | class Client 3 | CRLF = "\r\n" 4 | 5 | private 6 | # Properly encodes images in form/multipart specification for upload via OAuth. 7 | def http_multipart_data(params) 8 | body = "" 9 | headers = {} 10 | 11 | boundary = Time.now.to_i.to_s(16) 12 | 13 | headers["Content-Type"] = "multipart/form-data; boundary=#{boundary}" 14 | params.each do |key,value| 15 | esc_key = OAuth::Helper.escape(key.to_s) 16 | body << "--#{boundary}#{CRLF}" 17 | 18 | if value.respond_to?(:read) 19 | mime_type = MIME::Types.type_for(value.path)[0] || MIME::Types["application/octet-stream"][0] 20 | body << "Content-Disposition: form-data; name=\"#{esc_key}\"; filename=\"#{File.basename(value.path)}\"#{CRLF}" 21 | body << "Content-Type: #{mime_type.simplified}#{CRLF*2}" 22 | body << value.read 23 | else 24 | body << "Content-Disposition: form-data; name=\"#{esc_key}\"#{CRLF*2}#{value}" 25 | end 26 | end 27 | 28 | body << "--#{boundary}--#{CRLF*2}" 29 | headers["Content-Length"] = body.size.to_s 30 | 31 | return [ body, headers ] 32 | end 33 | end 34 | end -------------------------------------------------------------------------------- /test/client_test.rb: -------------------------------------------------------------------------------- 1 | require File.join(File.dirname(__FILE__), "/test_helper") 2 | 3 | class ClientTest < Test::Unit::TestCase 4 | include TwitterOAuth 5 | 6 | context "A client instance" do 7 | 8 | setup do 9 | @client = Client.new 10 | end 11 | 12 | should "be kind of TwitterOAuth" do 13 | assert_kind_of TwitterOAuth::Client, @client 14 | end 15 | 16 | context "authentication_request_token" do 17 | setup do 18 | @consumer = stub("oauth consumer", :options => {}) 19 | @client.stubs(:request_token) 20 | @client.stubs(:consumer).returns(@consumer) 21 | end 22 | 23 | should "sets consumers authorize path" do 24 | @client.authentication_request_token 25 | assert_equal @client.consumer.options[:authorize_path], '/oauth/authenticate' 26 | end 27 | end 28 | 29 | context "when authorizing" do 30 | setup do 31 | @request_token = stub('a request token') 32 | @access_token = stub_everything('access token') 33 | @request_token.stubs(:get_access_token).returns(@access_token) 34 | OAuth::RequestToken.stubs(:new).returns(@request_token) 35 | end 36 | 37 | should "return an access token" do 38 | assert_equal @client.authorize("haha","haha"), @access_token 39 | end 40 | end 41 | 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'test/unit' 3 | require 'shoulda' 4 | require 'mocha' 5 | 6 | lib_files = File.join(File.dirname(__FILE__), "..", "lib") 7 | 8 | Dir.glob(File.join(lib_files, "**")).each do |file| 9 | require file 10 | end 11 | -------------------------------------------------------------------------------- /twitter_oauth.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | 3 | Gem::Specification.new do |s| 4 | s.name = %q{twitter_oauth} 5 | s.version = "0.4.94" 6 | 7 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= 8 | s.authors = ["Richard Taylor"] 9 | s.date = %q{2013-06-12} 10 | s.description = %q{twitter_oauth is a Ruby client for the Twitter API using OAuth.} 11 | s.email = %q{moomerman@gmail.com} 12 | s.files = ["LICENSE", "README.textile","lib/twitter_oauth.rb"] + Dir.glob('lib/twitter_oauth/*.rb') 13 | s.has_rdoc = false 14 | s.homepage = %q{http://github.com/moomerman/twitter_oauth} 15 | s.rdoc_options = ["--inline-source", "--charset=UTF-8"] 16 | s.require_paths = ["lib"] 17 | s.rubyforge_project = %q{twitter_oauth} 18 | s.rubygems_version = %q{1.3.1} 19 | s.summary = %q{twitter_oauth is a Ruby client for the Twitter API using OAuth.} 20 | 21 | if s.respond_to? :specification_version then 22 | current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION 23 | s.specification_version = 2 24 | 25 | if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then 26 | s.add_runtime_dependency(%q, [">= 0.4.7"]) 27 | s.add_runtime_dependency(%q, [">= 1.8.0"]) 28 | s.add_runtime_dependency(%q, [">= 1.16"]) 29 | %w(shoulda mocha).each do |dep| 30 | s.add_development_dependency dep 31 | end 32 | else 33 | s.add_dependency(%q, [">= 0.4.7"]) 34 | s.add_dependency(%q, [">= 1.8.0"]) 35 | s.add_dependency(%q, [">= 1.23"]) 36 | end 37 | else 38 | s.add_dependency(%q, [">= 0.4.7"]) 39 | s.add_dependency(%q, [">= 1.8.0"]) 40 | s.add_dependency(%q, [">= 1.23"]) 41 | end 42 | end 43 | --------------------------------------------------------------------------------