twitter-jruby 0.9.5.2010050701
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History +282 -0
- data/License +20 -0
- data/Notes +33 -0
- data/README.rdoc +27 -0
- data/Rakefile +39 -0
- data/VERSION.yml +5 -0
- data/examples/connect.rb +30 -0
- data/examples/friendship_existance.rb +13 -0
- data/examples/helpers/config_store.rb +38 -0
- data/examples/httpauth.rb +11 -0
- data/examples/ids.rb +13 -0
- data/examples/lists.rb +11 -0
- data/examples/oauth.rb +27 -0
- data/examples/search.rb +15 -0
- data/examples/timeline.rb +19 -0
- data/examples/tumblr.rb +9 -0
- data/examples/unauthorized.rb +16 -0
- data/examples/update.rb +11 -0
- data/examples/user.rb +5 -0
- data/lib/twitter.rb +146 -0
- data/lib/twitter/base.rb +390 -0
- data/lib/twitter/httpauth.rb +39 -0
- data/lib/twitter/local_trends.rb +15 -0
- data/lib/twitter/oauth.rb +58 -0
- data/lib/twitter/request.rb +71 -0
- data/lib/twitter/search.rb +159 -0
- data/lib/twitter/trends.rb +41 -0
- data/test/fixtures/blocking.json +1632 -0
- data/test/fixtures/firehose.json +1 -0
- data/test/fixtures/follower_ids.json +1 -0
- data/test/fixtures/followers.json +1 -0
- data/test/fixtures/friend_ids.json +1 -0
- data/test/fixtures/friends_timeline.json +1 -0
- data/test/fixtures/friendship.json +1 -0
- data/test/fixtures/friendship_exists.json +1 -0
- data/test/fixtures/home_timeline.json +1 -0
- data/test/fixtures/ids.json +1 -0
- data/test/fixtures/list.json +1 -0
- data/test/fixtures/list_statuses.json +1 -0
- data/test/fixtures/list_statuses_1_1.json +1 -0
- data/test/fixtures/list_statuses_2_1.json +1 -0
- data/test/fixtures/list_subscriptions.json +1 -0
- data/test/fixtures/list_users.json +1 -0
- data/test/fixtures/lists.json +1 -0
- data/test/fixtures/memberships.json +1 -0
- data/test/fixtures/mentions.json +1 -0
- data/test/fixtures/not_found.json +1 -0
- data/test/fixtures/people_search.json +39 -0
- data/test/fixtures/rate_limit_exceeded.json +1 -0
- data/test/fixtures/report_spam.json +41 -0
- data/test/fixtures/retweet.json +1 -0
- data/test/fixtures/retweeted_by_me.json +1 -0
- data/test/fixtures/retweeted_to_me.json +1 -0
- data/test/fixtures/retweeters_of_tweet.json +166 -0
- data/test/fixtures/retweets.json +1 -0
- data/test/fixtures/retweets_of_me.json +1 -0
- data/test/fixtures/sample-image.png +0 -0
- data/test/fixtures/saved_search.json +7 -0
- data/test/fixtures/saved_searches.json +16 -0
- data/test/fixtures/search.json +1 -0
- data/test/fixtures/search_from_jnunemaker.json +1 -0
- data/test/fixtures/status.json +1 -0
- data/test/fixtures/status_show.json +1 -0
- data/test/fixtures/trends_available.json +253 -0
- data/test/fixtures/trends_current.json +1 -0
- data/test/fixtures/trends_current_exclude.json +1 -0
- data/test/fixtures/trends_daily.json +1925 -0
- data/test/fixtures/trends_daily_date.json +1 -0
- data/test/fixtures/trends_daily_exclude.json +1 -0
- data/test/fixtures/trends_location.json +57 -0
- data/test/fixtures/trends_weekly.json +1 -0
- data/test/fixtures/trends_weekly_date.json +1 -0
- data/test/fixtures/trends_weekly_exclude.json +1 -0
- data/test/fixtures/unauthorized.json +1 -0
- data/test/fixtures/update_profile_background_image.json +1 -0
- data/test/fixtures/update_profile_image.json +1 -0
- data/test/fixtures/user.json +1 -0
- data/test/fixtures/user_timeline.json +710 -0
- data/test/fixtures/users.json +1 -0
- data/test/test_helper.rb +46 -0
- data/test/twitter/base_test.rb +426 -0
- data/test/twitter/httpauth_test.rb +76 -0
- data/test/twitter/oauth_test.rb +108 -0
- data/test/twitter/request_test.rb +217 -0
- data/test/twitter/search_test.rb +208 -0
- data/test/twitter/trends_test.rb +112 -0
- data/test/twitter_test.rb +106 -0
- metadata +280 -0
@@ -0,0 +1,38 @@
|
|
1
|
+
class ConfigStore
|
2
|
+
attr_reader :file
|
3
|
+
|
4
|
+
def initialize(file)
|
5
|
+
@file = file
|
6
|
+
end
|
7
|
+
|
8
|
+
def load
|
9
|
+
@config ||= YAML::load(open(file))
|
10
|
+
self
|
11
|
+
end
|
12
|
+
|
13
|
+
def [](key)
|
14
|
+
load
|
15
|
+
@config[key]
|
16
|
+
end
|
17
|
+
|
18
|
+
def []=(key, value)
|
19
|
+
@config[key] = value
|
20
|
+
end
|
21
|
+
|
22
|
+
def delete(*keys)
|
23
|
+
keys.each { |key| @config.delete(key) }
|
24
|
+
save
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
def update(c={})
|
29
|
+
@config.merge!(c)
|
30
|
+
save
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
def save
|
35
|
+
File.open(file, 'w') { |f| f.write(YAML.dump(@config)) }
|
36
|
+
self
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
|
2
|
+
require File.join(File.dirname(__FILE__), 'helpers', 'config_store')
|
3
|
+
require 'pp'
|
4
|
+
|
5
|
+
config = ConfigStore.new("#{ENV['HOME']}/.twitter")
|
6
|
+
|
7
|
+
httpauth = Twitter::HTTPAuth.new(config['email'], config['password'])
|
8
|
+
base = Twitter::Base.new(httpauth)
|
9
|
+
|
10
|
+
pp base.user_timeline
|
11
|
+
pp base.verify_credentials
|
data/examples/ids.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
|
2
|
+
require File.join(File.dirname(__FILE__), 'helpers', 'config_store')
|
3
|
+
require 'pp'
|
4
|
+
|
5
|
+
config = ConfigStore.new("#{ENV['HOME']}/.twitter")
|
6
|
+
|
7
|
+
oauth = Twitter::OAuth.new(config['token'], config['secret'])
|
8
|
+
oauth.authorize_from_access(config['atoken'], config['asecret'])
|
9
|
+
|
10
|
+
client = Twitter::Base.new(oauth)
|
11
|
+
|
12
|
+
puts client.friend_ids
|
13
|
+
puts client.follower_ids
|
data/examples/lists.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
|
2
|
+
require File.join(File.dirname(__FILE__), 'helpers', 'config_store')
|
3
|
+
require 'pp'
|
4
|
+
|
5
|
+
config = ConfigStore.new("#{ENV['HOME']}/.twitter")
|
6
|
+
|
7
|
+
httpauth = Twitter::HTTPAuth.new(config['email'], config['password'])
|
8
|
+
base = Twitter::Base.new(httpauth)
|
9
|
+
|
10
|
+
pp base.lists('pengwynn')
|
11
|
+
pp base.list_members('pengwynn', 'rubyists')
|
data/examples/oauth.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require 'pathname'
|
3
|
+
dir = Pathname(__FILE__).dirname.expand_path
|
4
|
+
require (dir + '..' + 'lib' + 'twitter').expand_path
|
5
|
+
require dir + 'helpers' + 'config_store'
|
6
|
+
|
7
|
+
config = ConfigStore.new("#{ENV['HOME']}/.twitter")
|
8
|
+
oauth = Twitter::OAuth.new(config['token'], config['secret'])
|
9
|
+
rtoken = oauth.request_token.token
|
10
|
+
rsecret = oauth.request_token.secret
|
11
|
+
|
12
|
+
puts "> redirecting you to twitter to authorize..."
|
13
|
+
%x(open #{oauth.request_token.authorize_url})
|
14
|
+
|
15
|
+
print "> what was the PIN twitter provided you with? "
|
16
|
+
pin = gets.chomp
|
17
|
+
|
18
|
+
begin
|
19
|
+
oauth.authorize_from_request(rtoken, rsecret, pin)
|
20
|
+
|
21
|
+
twitter = Twitter::Base.new(oauth)
|
22
|
+
twitter.user_timeline.each do |tweet|
|
23
|
+
puts "#{tweet.user.screen_name}: #{tweet.text}"
|
24
|
+
end
|
25
|
+
rescue OAuth::Unauthorized
|
26
|
+
puts "> FAIL!"
|
27
|
+
end
|
data/examples/search.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
|
2
|
+
require 'pp'
|
3
|
+
|
4
|
+
search = Twitter::Search.new.from('jnunemaker')
|
5
|
+
|
6
|
+
puts '*'*50, 'First Run', '*'*50
|
7
|
+
search.each { |result| pp result }
|
8
|
+
|
9
|
+
puts '*'*50, 'Second Run', '*'*50
|
10
|
+
search.each { |result| pp result }
|
11
|
+
|
12
|
+
puts '*'*50, 'Parameter Check', '*'*50
|
13
|
+
pp Twitter::Search.new('#austineats').fetch().results.first
|
14
|
+
pp Twitter::Search.new('#austineats').page(2).fetch().results.first
|
15
|
+
pp Twitter::Search.new('#austineats').since(1412737343).fetch().results.first
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
|
2
|
+
require File.join(File.dirname(__FILE__), 'helpers', 'config_store')
|
3
|
+
require 'pp'
|
4
|
+
|
5
|
+
config = ConfigStore.new("#{ENV['HOME']}/.twitter")
|
6
|
+
|
7
|
+
oauth = Twitter::OAuth.new(config['token'], config['secret'])
|
8
|
+
oauth.authorize_from_access(config['atoken'], config['asecret'])
|
9
|
+
|
10
|
+
client = Twitter::Base.new(oauth)
|
11
|
+
|
12
|
+
pp client.friends_timeline
|
13
|
+
puts '*'*50
|
14
|
+
|
15
|
+
pp client.user_timeline
|
16
|
+
puts '*'*50
|
17
|
+
|
18
|
+
pp client.replies
|
19
|
+
puts '*'*50
|
data/examples/tumblr.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
|
2
|
+
require 'pp'
|
3
|
+
|
4
|
+
puts 'User', '*'*50
|
5
|
+
pp Twitter.user('jnunemaker')
|
6
|
+
pp Twitter.user('snitch_test')
|
7
|
+
|
8
|
+
puts 'Status', '*'*50
|
9
|
+
pp Twitter.status(1533815199)
|
10
|
+
|
11
|
+
puts 'Friend Ids', '*'*50
|
12
|
+
pp Twitter.friend_ids('jnunemaker')
|
13
|
+
|
14
|
+
puts 'Follower Ids', '*'*50
|
15
|
+
pp Twitter.follower_ids('jnunemaker')
|
16
|
+
|
data/examples/update.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
|
2
|
+
require File.join(File.dirname(__FILE__), 'helpers', 'config_store')
|
3
|
+
require 'pp'
|
4
|
+
|
5
|
+
config = ConfigStore.new("#{ENV['HOME']}/.twitter")
|
6
|
+
|
7
|
+
oauth = Twitter::OAuth.new(config['token'], config['secret'])
|
8
|
+
oauth.authorize_from_access(config['atoken'], config['asecret'])
|
9
|
+
|
10
|
+
client = Twitter::Base.new(oauth)
|
11
|
+
pp client.update('This is an update from the twitter gem')
|
data/examples/user.rb
ADDED
data/lib/twitter.rb
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
require "oauth"
|
3
|
+
require "hashie"
|
4
|
+
require "httparty"
|
5
|
+
|
6
|
+
module Twitter
|
7
|
+
include HTTParty
|
8
|
+
API_VERSION = "1".freeze
|
9
|
+
base_uri "api.twitter.com/#{API_VERSION}"
|
10
|
+
format :json
|
11
|
+
|
12
|
+
class TwitterError < StandardError
|
13
|
+
attr_reader :data
|
14
|
+
|
15
|
+
def initialize(data)
|
16
|
+
@data = data
|
17
|
+
super
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class RateLimitExceeded < TwitterError; end
|
22
|
+
class Unauthorized < TwitterError; end
|
23
|
+
class General < TwitterError; end
|
24
|
+
|
25
|
+
class Unavailable < StandardError; end
|
26
|
+
class InformTwitter < StandardError; end
|
27
|
+
class NotFound < StandardError; end
|
28
|
+
|
29
|
+
def self.firehose
|
30
|
+
perform_get("/statuses/public_timeline.json")
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.user(id)
|
34
|
+
perform_get("/users/show/#{id}.json")
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.status(id)
|
38
|
+
perform_get("/statuses/show/#{id}.json")
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.friend_ids(id)
|
42
|
+
perform_get("/friends/ids/#{id}.json")
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.follower_ids(id)
|
46
|
+
perform_get("/followers/ids/#{id}.json")
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.timeline(id, options={})
|
50
|
+
perform_get("/statuses/user_timeline/#{id}.json", :query => options)
|
51
|
+
end
|
52
|
+
|
53
|
+
# :per_page = max number of statues to get at once
|
54
|
+
# :page = which page of tweets you wish to get
|
55
|
+
def self.list_timeline(list_owner_username, slug, query = {})
|
56
|
+
perform_get("/#{list_owner_username}/lists/#{slug}/statuses.json", :query => query)
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def self.perform_get(uri, options = {})
|
62
|
+
make_friendly(get(uri, options))
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.make_friendly(response)
|
66
|
+
raise_errors(response)
|
67
|
+
data = parse(response)
|
68
|
+
# Don't mash arrays of integers
|
69
|
+
if data && data.is_a?(Array) && data.first.is_a?(Integer)
|
70
|
+
data
|
71
|
+
else
|
72
|
+
mash(data)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.raise_errors(response)
|
77
|
+
case response.code.to_i
|
78
|
+
when 400
|
79
|
+
data = parse(response)
|
80
|
+
raise RateLimitExceeded.new(data), "(#{response.code}): #{response.message} - #{data['error'] if data}"
|
81
|
+
when 401
|
82
|
+
data = parse(response)
|
83
|
+
raise Unauthorized.new(data), "(#{response.code}): #{response.message} - #{data['error'] if data}"
|
84
|
+
when 403
|
85
|
+
data = parse(response)
|
86
|
+
raise General.new(data), "(#{response.code}): #{response.message} - #{data['error'] if data}"
|
87
|
+
when 404
|
88
|
+
raise NotFound, "(#{response.code}): #{response.message}"
|
89
|
+
when 500
|
90
|
+
raise InformTwitter, "Twitter had an internal error. Please let them know in the group. (#{response.code}): #{response.message}"
|
91
|
+
when 502..503
|
92
|
+
raise Unavailable, "(#{response.code}): #{response.message}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.parse(response)
|
97
|
+
JSON.parse(response.body)
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.mash(obj)
|
101
|
+
if obj.is_a?(Array)
|
102
|
+
obj.map{|item| make_mash_with_consistent_hash(item)}
|
103
|
+
elsif obj.is_a?(Hash)
|
104
|
+
make_mash_with_consistent_hash(obj)
|
105
|
+
else
|
106
|
+
obj
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Lame workaround for the fact that mash doesn't hash correctly
|
111
|
+
def self.make_mash_with_consistent_hash(obj)
|
112
|
+
m = Hashie::Mash.new(obj)
|
113
|
+
def m.hash
|
114
|
+
inspect.hash
|
115
|
+
end
|
116
|
+
return m
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
module Hashie
|
122
|
+
class Mash
|
123
|
+
|
124
|
+
# Converts all of the keys to strings, optionally formatting key name
|
125
|
+
def rubyify_keys!
|
126
|
+
keys.each{|k|
|
127
|
+
v = delete(k)
|
128
|
+
new_key = k.to_s.underscore
|
129
|
+
self[new_key] = v
|
130
|
+
v.rubyify_keys! if v.is_a?(Hash)
|
131
|
+
v.each{|p| p.rubyify_keys! if p.is_a?(Hash)} if v.is_a?(Array)
|
132
|
+
}
|
133
|
+
self
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
directory = File.expand_path(File.dirname(__FILE__))
|
140
|
+
|
141
|
+
require File.join(directory, "twitter", "oauth")
|
142
|
+
require File.join(directory, "twitter", "httpauth")
|
143
|
+
require File.join(directory, "twitter", "request")
|
144
|
+
require File.join(directory, "twitter", "base")
|
145
|
+
require File.join(directory, "twitter", "search")
|
146
|
+
require File.join(directory, "twitter", "trends")
|
data/lib/twitter/base.rb
ADDED
@@ -0,0 +1,390 @@
|
|
1
|
+
module Twitter
|
2
|
+
class Base
|
3
|
+
extend Forwardable
|
4
|
+
|
5
|
+
def_delegators :client, :get, :post, :put, :delete
|
6
|
+
|
7
|
+
attr_reader :client
|
8
|
+
|
9
|
+
def initialize(client)
|
10
|
+
@client = client
|
11
|
+
end
|
12
|
+
|
13
|
+
# Options: since_id, max_id, count, page
|
14
|
+
def home_timeline(query={})
|
15
|
+
perform_get("/#{API_VERSION}/statuses/home_timeline.json", :query => query)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Options: since_id, max_id, count, page, since
|
19
|
+
def friends_timeline(query={})
|
20
|
+
perform_get("/#{API_VERSION}/statuses/friends_timeline.json", :query => query)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Options: id, user_id, screen_name, since_id, max_id, page, since, count
|
24
|
+
def user_timeline(query={})
|
25
|
+
perform_get("/#{API_VERSION}/statuses/user_timeline.json", :query => query)
|
26
|
+
end
|
27
|
+
|
28
|
+
def status(id)
|
29
|
+
perform_get("/#{API_VERSION}/statuses/show/#{id}.json")
|
30
|
+
end
|
31
|
+
|
32
|
+
# Options: count
|
33
|
+
def retweets(id, query={})
|
34
|
+
perform_get("/#{API_VERSION}/statuses/retweets/#{id}.json", :query => query)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Options: in_reply_to_status_id
|
38
|
+
def update(status, query={})
|
39
|
+
perform_post("/#{API_VERSION}/statuses/update.json", :body => {:status => status}.merge(query))
|
40
|
+
end
|
41
|
+
|
42
|
+
# DEPRECATED: Use #mentions instead
|
43
|
+
#
|
44
|
+
# Options: since_id, max_id, since, page
|
45
|
+
def replies(query={})
|
46
|
+
warn("DEPRECATED: #replies is deprecated by Twitter; use #mentions instead")
|
47
|
+
perform_get("/#{API_VERSION}/statuses/replies.json", :query => query)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Options: since_id, max_id, count, page
|
51
|
+
def mentions(query={})
|
52
|
+
perform_get("/#{API_VERSION}/statuses/mentions.json", :query => query)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Options: since_id, max_id, count, page
|
56
|
+
def retweeted_by_me(query={})
|
57
|
+
perform_get("/#{API_VERSION}/statuses/retweeted_by_me.json", :query => query)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Options: since_id, max_id, count, page
|
61
|
+
def retweeted_to_me(query={})
|
62
|
+
perform_get("/#{API_VERSION}/statuses/retweeted_to_me.json", :query => query)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Options: since_id, max_id, count, page
|
66
|
+
def retweets_of_me(query={})
|
67
|
+
perform_get("/#{API_VERSION}/statuses/retweets_of_me.json", :query => query)
|
68
|
+
end
|
69
|
+
|
70
|
+
# options: count, page, ids_only
|
71
|
+
def retweeters_of(id, options={})
|
72
|
+
ids_only = !!(options.delete(:ids_only))
|
73
|
+
perform_get("/#{API_VERSION}/statuses/#{id}/retweeted_by#{"/ids" if ids_only}.json", :query => options)
|
74
|
+
end
|
75
|
+
|
76
|
+
def status_destroy(id)
|
77
|
+
perform_post("/#{API_VERSION}/statuses/destroy/#{id}.json")
|
78
|
+
end
|
79
|
+
|
80
|
+
def retweet(id)
|
81
|
+
perform_post("/#{API_VERSION}/statuses/retweet/#{id}.json")
|
82
|
+
end
|
83
|
+
|
84
|
+
# Options: id, user_id, screen_name, page
|
85
|
+
def friends(query={})
|
86
|
+
perform_get("/#{API_VERSION}/statuses/friends.json", :query => query)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Options: id, user_id, screen_name, page
|
90
|
+
def followers(query={})
|
91
|
+
perform_get("/#{API_VERSION}/statuses/followers.json", :query => query)
|
92
|
+
end
|
93
|
+
|
94
|
+
def user(id, query={})
|
95
|
+
perform_get("/#{API_VERSION}/users/show/#{id}.json", :query => query)
|
96
|
+
end
|
97
|
+
|
98
|
+
def users(*ids_or_usernames)
|
99
|
+
ids, usernames = [], []
|
100
|
+
ids_or_usernames.each do |id_or_username|
|
101
|
+
if id_or_username.is_a?(Integer)
|
102
|
+
ids << id_or_username
|
103
|
+
elsif id_or_username.is_a?(String)
|
104
|
+
usernames << id_or_username
|
105
|
+
end
|
106
|
+
end
|
107
|
+
query = {}
|
108
|
+
query[:user_id] = ids.join(",") unless ids.empty?
|
109
|
+
query[:screen_name] = usernames.join(",") unless usernames.empty?
|
110
|
+
perform_get("/#{API_VERSION}/users/lookup.json", :query => query)
|
111
|
+
end
|
112
|
+
|
113
|
+
# Options: page, per_page
|
114
|
+
def user_search(q, query={})
|
115
|
+
q = URI.escape(q)
|
116
|
+
perform_get("/#{API_VERSION}/users/search.json", :query => ({:q => q}.merge(query)))
|
117
|
+
end
|
118
|
+
|
119
|
+
# Options: since, since_id, page
|
120
|
+
def direct_messages(query={})
|
121
|
+
perform_get("/#{API_VERSION}/direct_messages.json", :query => query)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Options: since, since_id, page
|
125
|
+
def direct_messages_sent(query={})
|
126
|
+
perform_get("/#{API_VERSION}/direct_messages/sent.json", :query => query)
|
127
|
+
end
|
128
|
+
|
129
|
+
def direct_message_create(user, text)
|
130
|
+
perform_post("/#{API_VERSION}/direct_messages/new.json", :body => {:user => user, :text => text})
|
131
|
+
end
|
132
|
+
|
133
|
+
def direct_message_destroy(id)
|
134
|
+
perform_post("/#{API_VERSION}/direct_messages/destroy/#{id}.json")
|
135
|
+
end
|
136
|
+
|
137
|
+
def friendship_create(id, follow=false)
|
138
|
+
body = {}
|
139
|
+
body.merge!(:follow => follow) if follow
|
140
|
+
perform_post("/#{API_VERSION}/friendships/create/#{id}.json", :body => body)
|
141
|
+
end
|
142
|
+
|
143
|
+
def friendship_destroy(id)
|
144
|
+
perform_post("/#{API_VERSION}/friendships/destroy/#{id}.json")
|
145
|
+
end
|
146
|
+
|
147
|
+
def friendship_exists?(a, b)
|
148
|
+
perform_get("/#{API_VERSION}/friendships/exists.json", :query => {:user_a => a, :user_b => b})
|
149
|
+
end
|
150
|
+
|
151
|
+
def friendship_show(query)
|
152
|
+
perform_get("/#{API_VERSION}/friendships/show.json", :query => query)
|
153
|
+
end
|
154
|
+
|
155
|
+
# Options: id, user_id, screen_name
|
156
|
+
def friend_ids(query={})
|
157
|
+
perform_get("/#{API_VERSION}/friends/ids.json", :query => query)
|
158
|
+
end
|
159
|
+
|
160
|
+
# Options: id, user_id, screen_name
|
161
|
+
def follower_ids(query={})
|
162
|
+
perform_get("/#{API_VERSION}/followers/ids.json", :query => query)
|
163
|
+
end
|
164
|
+
|
165
|
+
def verify_credentials
|
166
|
+
perform_get("/#{API_VERSION}/account/verify_credentials.json")
|
167
|
+
end
|
168
|
+
|
169
|
+
# Device must be sms, im or none
|
170
|
+
def update_delivery_device(device)
|
171
|
+
perform_post("/#{API_VERSION}/account/update_delivery_device.json", :body => {:device => device})
|
172
|
+
end
|
173
|
+
|
174
|
+
# One or more of the following must be present:
|
175
|
+
# profile_background_color, profile_text_color, profile_link_color,
|
176
|
+
# profile_sidebar_fill_color, profile_sidebar_border_color
|
177
|
+
def update_profile_colors(colors={})
|
178
|
+
perform_post("/#{API_VERSION}/account/update_profile_colors.json", :body => colors)
|
179
|
+
end
|
180
|
+
|
181
|
+
# file should respond to #read and #path
|
182
|
+
def update_profile_image(file)
|
183
|
+
perform_post("/#{API_VERSION}/account/update_profile_image.json", build_multipart_bodies(:image => file))
|
184
|
+
end
|
185
|
+
|
186
|
+
# file should respond to #read and #path
|
187
|
+
def update_profile_background(file, tile = false)
|
188
|
+
perform_post("/#{API_VERSION}/account/update_profile_background_image.json", build_multipart_bodies(:image => file).merge(:tile => tile))
|
189
|
+
end
|
190
|
+
|
191
|
+
def rate_limit_status
|
192
|
+
perform_get("/#{API_VERSION}/account/rate_limit_status.json")
|
193
|
+
end
|
194
|
+
|
195
|
+
# One or more of the following must be present:
|
196
|
+
# name, email, url, location, description
|
197
|
+
def update_profile(body={})
|
198
|
+
perform_post("/#{API_VERSION}/account/update_profile.json", :body => body)
|
199
|
+
end
|
200
|
+
|
201
|
+
# Options: id, page
|
202
|
+
def favorites(query={})
|
203
|
+
perform_get("/#{API_VERSION}/favorites.json", :query => query)
|
204
|
+
end
|
205
|
+
|
206
|
+
def favorite_create(id)
|
207
|
+
perform_post("/#{API_VERSION}/favorites/create/#{id}.json")
|
208
|
+
end
|
209
|
+
|
210
|
+
def favorite_destroy(id)
|
211
|
+
perform_post("/#{API_VERSION}/favorites/destroy/#{id}.json")
|
212
|
+
end
|
213
|
+
|
214
|
+
def enable_notifications(id)
|
215
|
+
perform_post("/#{API_VERSION}/notifications/follow/#{id}.json")
|
216
|
+
end
|
217
|
+
|
218
|
+
def disable_notifications(id)
|
219
|
+
perform_post("/#{API_VERSION}/notifications/leave/#{id}.json")
|
220
|
+
end
|
221
|
+
|
222
|
+
def block(id)
|
223
|
+
perform_post("/#{API_VERSION}/blocks/create/#{id}.json")
|
224
|
+
end
|
225
|
+
|
226
|
+
def unblock(id)
|
227
|
+
perform_post("/#{API_VERSION}/blocks/destroy/#{id}.json")
|
228
|
+
end
|
229
|
+
|
230
|
+
# When reporting a user for spam, specify one or more of id, screen_name, or user_id
|
231
|
+
def report_spam(options)
|
232
|
+
perform_post("/#{API_VERSION}/report_spam.json", :body => options)
|
233
|
+
end
|
234
|
+
|
235
|
+
def help
|
236
|
+
perform_get("/#{API_VERSION}/help/test.json")
|
237
|
+
end
|
238
|
+
|
239
|
+
def list_create(list_owner_username, options)
|
240
|
+
perform_post("/#{API_VERSION}/#{list_owner_username}/lists.json", :body => {:user => list_owner_username}.merge(options))
|
241
|
+
end
|
242
|
+
|
243
|
+
def list_update(list_owner_username, slug, options)
|
244
|
+
perform_put("/#{API_VERSION}/#{list_owner_username}/lists/#{slug}.json", :body => options)
|
245
|
+
end
|
246
|
+
|
247
|
+
def list_delete(list_owner_username, slug)
|
248
|
+
perform_delete("/#{API_VERSION}/#{list_owner_username}/lists/#{slug}.json")
|
249
|
+
end
|
250
|
+
|
251
|
+
def lists(list_owner_username = nil, query = {})
|
252
|
+
path = case list_owner_username
|
253
|
+
when nil, Hash
|
254
|
+
query = list_owner_username
|
255
|
+
"/#{API_VERSION}/lists.json"
|
256
|
+
else
|
257
|
+
"/#{API_VERSION}/#{list_owner_username}/lists.json"
|
258
|
+
end
|
259
|
+
perform_get(path, :query => query)
|
260
|
+
end
|
261
|
+
|
262
|
+
def list(list_owner_username, slug)
|
263
|
+
perform_get("/#{API_VERSION}/#{list_owner_username}/lists/#{slug}.json")
|
264
|
+
end
|
265
|
+
|
266
|
+
# :per_page = max number of statues to get at once
|
267
|
+
# :page = which page of tweets you wish to get
|
268
|
+
def list_timeline(list_owner_username, slug, query = {})
|
269
|
+
perform_get("/#{API_VERSION}/#{list_owner_username}/lists/#{slug}/statuses.json", :query => query)
|
270
|
+
end
|
271
|
+
|
272
|
+
def memberships(list_owner_username, query={})
|
273
|
+
perform_get("/#{API_VERSION}/#{list_owner_username}/lists/memberships.json", :query => query)
|
274
|
+
end
|
275
|
+
|
276
|
+
def subscriptions(list_owner_username, query = {})
|
277
|
+
perform_get("/#{API_VERSION}/#{list_owner_username}/lists/subscriptions.json", :query => query)
|
278
|
+
end
|
279
|
+
|
280
|
+
def list_members(list_owner_username, slug, query = {})
|
281
|
+
perform_get("/#{API_VERSION}/#{list_owner_username}/#{slug}/members.json", :query => query)
|
282
|
+
end
|
283
|
+
|
284
|
+
def list_add_member(list_owner_username, slug, new_id)
|
285
|
+
perform_post("/#{API_VERSION}/#{list_owner_username}/#{slug}/members.json", :body => {:id => new_id})
|
286
|
+
end
|
287
|
+
|
288
|
+
def list_remove_member(list_owner_username, slug, id)
|
289
|
+
perform_delete("/#{API_VERSION}/#{list_owner_username}/#{slug}/members.json", :query => {:id => id})
|
290
|
+
end
|
291
|
+
|
292
|
+
def is_list_member?(list_owner_username, slug, id)
|
293
|
+
perform_get("/#{API_VERSION}/#{list_owner_username}/#{slug}/members/#{id}.json").error.nil?
|
294
|
+
end
|
295
|
+
|
296
|
+
def list_subscribers(list_owner_username, slug)
|
297
|
+
perform_get("/#{API_VERSION}/#{list_owner_username}/#{slug}/subscribers.json")
|
298
|
+
end
|
299
|
+
|
300
|
+
def list_subscribe(list_owner_username, slug)
|
301
|
+
perform_post("/#{API_VERSION}/#{list_owner_username}/#{slug}/subscribers.json")
|
302
|
+
end
|
303
|
+
|
304
|
+
def list_unsubscribe(list_owner_username, slug)
|
305
|
+
perform_delete("/#{API_VERSION}/#{list_owner_username}/#{slug}/subscribers.json")
|
306
|
+
end
|
307
|
+
|
308
|
+
def blocked_ids
|
309
|
+
perform_get("/#{API_VERSION}/blocks/blocking/ids.json", :mash => false)
|
310
|
+
end
|
311
|
+
|
312
|
+
def blocking(options={})
|
313
|
+
perform_get("/#{API_VERSION}/blocks/blocking.json", options)
|
314
|
+
end
|
315
|
+
|
316
|
+
def saved_searches
|
317
|
+
perform_get("/#{API_VERSION}/saved_searches.json")
|
318
|
+
end
|
319
|
+
|
320
|
+
def saved_search(id)
|
321
|
+
perform_get("/#{API_VERSION}/saved_searches/show/#{id}.json")
|
322
|
+
end
|
323
|
+
|
324
|
+
def saved_search_create(query)
|
325
|
+
perform_post("/#{API_VERSION}/saved_searches/create.json", :body => {:query => query})
|
326
|
+
end
|
327
|
+
|
328
|
+
def saved_search_destroy(id)
|
329
|
+
perform_delete("/#{API_VERSION}/saved_searches/destroy/#{id}.json")
|
330
|
+
end
|
331
|
+
|
332
|
+
protected
|
333
|
+
|
334
|
+
def self.mime_type(file)
|
335
|
+
case
|
336
|
+
when file =~ /\.jpg/ then 'image/jpg'
|
337
|
+
when file =~ /\.gif$/ then 'image/gif'
|
338
|
+
when file =~ /\.png$/ then 'image/png'
|
339
|
+
else 'application/octet-stream'
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
def mime_type(f) self.class.mime_type(f) end
|
344
|
+
|
345
|
+
CRLF = "\r\n"
|
346
|
+
|
347
|
+
def self.build_multipart_bodies(parts)
|
348
|
+
boundary = Time.now.to_i.to_s(16)
|
349
|
+
body = ""
|
350
|
+
parts.each do |key, value|
|
351
|
+
esc_key = CGI.escape(key.to_s)
|
352
|
+
body << "--#{boundary}#{CRLF}"
|
353
|
+
if value.respond_to?(:read)
|
354
|
+
body << "Content-Disposition: form-data; name=\"#{esc_key}\"; filename=\"#{File.basename(value.path)}\"#{CRLF}"
|
355
|
+
body << "Content-Type: #{mime_type(value.path)}#{CRLF*2}"
|
356
|
+
body << value.read
|
357
|
+
else
|
358
|
+
body << "Content-Disposition: form-data; name=\"#{esc_key}\"#{CRLF*2}#{value}"
|
359
|
+
end
|
360
|
+
body << CRLF
|
361
|
+
end
|
362
|
+
body << "--#{boundary}--#{CRLF*2}"
|
363
|
+
{
|
364
|
+
:body => body,
|
365
|
+
:headers => {"Content-Type" => "multipart/form-data; boundary=#{boundary}"}
|
366
|
+
}
|
367
|
+
end
|
368
|
+
|
369
|
+
def build_multipart_bodies(parts) self.class.build_multipart_bodies(parts) end
|
370
|
+
|
371
|
+
private
|
372
|
+
|
373
|
+
def perform_get(path, options={})
|
374
|
+
Twitter::Request.get(self, path, options)
|
375
|
+
end
|
376
|
+
|
377
|
+
def perform_post(path, options={})
|
378
|
+
Twitter::Request.post(self, path, options)
|
379
|
+
end
|
380
|
+
|
381
|
+
def perform_put(path, options={})
|
382
|
+
Twitter::Request.put(self, path, options)
|
383
|
+
end
|
384
|
+
|
385
|
+
def perform_delete(path, options={})
|
386
|
+
Twitter::Request.delete(self, path, options)
|
387
|
+
end
|
388
|
+
|
389
|
+
end
|
390
|
+
end
|