teejayvanslyke-twitter 0.6.11
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 +206 -0
- data/License +20 -0
- data/Notes +33 -0
- data/README.rdoc +19 -0
- data/Rakefile +103 -0
- data/VERSION.yml +4 -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/search.rb +15 -0
- data/examples/timeline.rb +19 -0
- data/examples/unauthorized.rb +16 -0
- data/examples/update.rb +11 -0
- data/examples/user.rb +5 -0
- data/lib/twitter.rb +63 -0
- data/lib/twitter/base.rb +216 -0
- data/lib/twitter/httpauth.rb +27 -0
- data/lib/twitter/oauth.rb +41 -0
- data/lib/twitter/request.rb +102 -0
- data/lib/twitter/search.rb +106 -0
- data/lib/twitter/trends.rb +29 -0
- data/test/fixtures/firehose.json +1 -0
- data/test/fixtures/follower_ids.json +1 -0
- data/test/fixtures/friend_ids.json +1 -0
- data/test/fixtures/friends_timeline.json +1 -0
- data/test/fixtures/rate_limit_exceeded.json +1 -0
- data/test/fixtures/replies.json +1 -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_current.json +1 -0
- data/test/fixtures/trends_current_exclude.json +1 -0
- data/test/fixtures/trends_daily.json +1 -0
- data/test/fixtures/trends_daily_date.json +1 -0
- data/test/fixtures/trends_daily_exclude.json +1 -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/user.json +1 -0
- data/test/fixtures/user_timeline.json +1 -0
- data/test/test_helper.rb +36 -0
- data/test/twitter/base_test.rb +95 -0
- data/test/twitter/httpauth_test.rb +76 -0
- data/test/twitter/oauth_test.rb +81 -0
- data/test/twitter/request_test.rb +217 -0
- data/test/twitter/search_test.rb +144 -0
- data/test/twitter/trends_test.rb +95 -0
- data/test/twitter_test.rb +38 -0
- metadata +202 -0
@@ -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/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
|
@@ -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,63 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'rubygems'
|
3
|
+
|
4
|
+
gem 'oauth', '0.3.4'
|
5
|
+
require 'oauth'
|
6
|
+
|
7
|
+
gem 'mash', '0.0.3'
|
8
|
+
require 'mash'
|
9
|
+
|
10
|
+
gem 'httparty', '0.4.3'
|
11
|
+
require 'httparty'
|
12
|
+
|
13
|
+
module Twitter
|
14
|
+
class TwitterError < StandardError
|
15
|
+
attr_reader :data
|
16
|
+
|
17
|
+
def initialize(data)
|
18
|
+
@data = data
|
19
|
+
super
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class RateLimitExceeded < TwitterError; end
|
24
|
+
class Unauthorized < TwitterError; end
|
25
|
+
class General < TwitterError; end
|
26
|
+
|
27
|
+
class Unavailable < StandardError; end
|
28
|
+
class InformTwitter < StandardError; end
|
29
|
+
class NotFound < StandardError; end
|
30
|
+
|
31
|
+
|
32
|
+
def self.firehose
|
33
|
+
response = HTTParty.get('http://twitter.com/statuses/public_timeline.json', :format => :json)
|
34
|
+
response.map { |tweet| Mash.new(tweet) }
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.user(id)
|
38
|
+
response = HTTParty.get("http://twitter.com/users/show/#{id}.json", :format => :json)
|
39
|
+
Mash.new(response)
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.status(id)
|
43
|
+
response = HTTParty.get("http://twitter.com/statuses/show/#{id}.json", :format => :json)
|
44
|
+
Mash.new(response)
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.friend_ids(id)
|
48
|
+
HTTParty.get("http://twitter.com/friends/ids/#{id}.json", :format => :json)
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.follower_ids(id)
|
52
|
+
HTTParty.get("http://twitter.com/followers/ids/#{id}.json", :format => :json)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
directory = File.expand_path(File.dirname(__FILE__))
|
57
|
+
|
58
|
+
require File.join(directory, 'twitter', 'oauth')
|
59
|
+
require File.join(directory, 'twitter', 'httpauth')
|
60
|
+
require File.join(directory, 'twitter', 'request')
|
61
|
+
require File.join(directory, 'twitter', 'base')
|
62
|
+
require File.join(directory, 'twitter', 'search')
|
63
|
+
require File.join(directory, 'twitter', 'trends')
|
data/lib/twitter/base.rb
ADDED
@@ -0,0 +1,216 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
|
3
|
+
module Twitter
|
4
|
+
class Base
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
def_delegators :client, :get, :post
|
8
|
+
|
9
|
+
attr_reader :client
|
10
|
+
|
11
|
+
def initialize(client)
|
12
|
+
@client = client
|
13
|
+
end
|
14
|
+
|
15
|
+
# Options: since_id, max_id, count, page, since
|
16
|
+
def friends_timeline(query={})
|
17
|
+
perform_get('/statuses/friends_timeline.json', :query => query)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Options: id, user_id, screen_name, since_id, max_id, page, since
|
21
|
+
def user_timeline(query={})
|
22
|
+
perform_get('/statuses/user_timeline.json', :query => query)
|
23
|
+
end
|
24
|
+
|
25
|
+
def status(id)
|
26
|
+
perform_get("/statuses/show/#{id}.json")
|
27
|
+
end
|
28
|
+
|
29
|
+
# Options: in_reply_to_status_id
|
30
|
+
def update(status, query={})
|
31
|
+
perform_post("/statuses/update.json", :body => {:status => status}.merge(query))
|
32
|
+
end
|
33
|
+
|
34
|
+
# Options: since_id, max_id, since, page
|
35
|
+
def replies(query={})
|
36
|
+
perform_get('/statuses/replies.json', :query => query)
|
37
|
+
end
|
38
|
+
|
39
|
+
def status_destroy(id)
|
40
|
+
perform_post("/statuses/destroy/#{id}.json")
|
41
|
+
end
|
42
|
+
|
43
|
+
# Options: id, user_id, screen_name, page
|
44
|
+
def friends(query={})
|
45
|
+
perform_get('/statuses/friends.json', :query => query)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Options: id, user_id, screen_name, page
|
49
|
+
def followers(query={})
|
50
|
+
perform_get('/statuses/followers.json', :query => query)
|
51
|
+
end
|
52
|
+
|
53
|
+
def user(id, query={})
|
54
|
+
perform_get("/users/show/#{id}.json", :query => query)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Options: since, since_id, page
|
58
|
+
def direct_messages(query={})
|
59
|
+
perform_get("/direct_messages.json", :query => query)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Options: since, since_id, page
|
63
|
+
def direct_messages_sent(query={})
|
64
|
+
perform_get("/direct_messages/sent.json", :query => query)
|
65
|
+
end
|
66
|
+
|
67
|
+
def direct_message_create(user, text)
|
68
|
+
perform_post("/direct_messages/new.json", :body => {:user => user, :text => text})
|
69
|
+
end
|
70
|
+
|
71
|
+
def direct_message_destroy(id)
|
72
|
+
perform_post("/direct_messages/destroy/#{id}.json")
|
73
|
+
end
|
74
|
+
|
75
|
+
def friendship_create(id, follow=false)
|
76
|
+
body = {}
|
77
|
+
body.merge!(:follow => follow) if follow
|
78
|
+
perform_post("/friendships/create/#{id}.json", :body => body)
|
79
|
+
end
|
80
|
+
|
81
|
+
def friendship_destroy(id)
|
82
|
+
perform_post("/friendships/destroy/#{id}.json")
|
83
|
+
end
|
84
|
+
|
85
|
+
def friendship_exists?(a, b)
|
86
|
+
perform_get("/friendships/exists.json", :query => {:user_a => a, :user_b => b})
|
87
|
+
end
|
88
|
+
|
89
|
+
# Options: id, user_id, screen_name
|
90
|
+
def friend_ids(query={})
|
91
|
+
perform_get("/friends/ids.json", :query => query, :mash => false)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Options: id, user_id, screen_name
|
95
|
+
def follower_ids(query={})
|
96
|
+
perform_get("/followers/ids.json", :query => query, :mash => false)
|
97
|
+
end
|
98
|
+
|
99
|
+
def verify_credentials
|
100
|
+
perform_get("/account/verify_credentials.json")
|
101
|
+
end
|
102
|
+
|
103
|
+
# Device must be sms, im or none
|
104
|
+
def update_delivery_device(device)
|
105
|
+
perform_post('/account/update_delivery_device.json', :body => {:device => device})
|
106
|
+
end
|
107
|
+
|
108
|
+
# One or more of the following must be present:
|
109
|
+
# profile_background_color, profile_text_color, profile_link_color,
|
110
|
+
# profile_sidebar_fill_color, profile_sidebar_border_color
|
111
|
+
def update_profile_colors(colors={})
|
112
|
+
perform_post('/account/update_profile_colors.json', :body => colors)
|
113
|
+
end
|
114
|
+
|
115
|
+
def update_profile_background_image(path_or_url_to_image)
|
116
|
+
file = open(path_or_url_to_image)
|
117
|
+
url = URI.parse('http://twitter.com/account/update_profile_background_image.json')
|
118
|
+
Net::HTTP.new(url.host, url.port).start do |http|
|
119
|
+
req = Net::HTTP::Post.new(url.request_uri)
|
120
|
+
add_multipart_data(req, :image => file)
|
121
|
+
add_oauth(req)
|
122
|
+
res = http.request(req)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def rate_limit_status
|
127
|
+
perform_get('/account/rate_limit_status.json')
|
128
|
+
end
|
129
|
+
|
130
|
+
# One or more of the following must be present:
|
131
|
+
# name, email, url, location, description
|
132
|
+
def update_profile(body={})
|
133
|
+
perform_post('/account/update_profile.json', :body => body)
|
134
|
+
end
|
135
|
+
|
136
|
+
# Options: id, page
|
137
|
+
def favorites(query={})
|
138
|
+
perform_get('/favorites.json', :query => query)
|
139
|
+
end
|
140
|
+
|
141
|
+
def favorite_create(id)
|
142
|
+
perform_post("/favorites/create/#{id}.json")
|
143
|
+
end
|
144
|
+
|
145
|
+
def favorite_destroy(id)
|
146
|
+
perform_post("/favorites/destroy/#{id}.json")
|
147
|
+
end
|
148
|
+
|
149
|
+
def enable_notifications(id)
|
150
|
+
perform_post("/notifications/follow/#{id}.json")
|
151
|
+
end
|
152
|
+
|
153
|
+
def disable_notifications(id)
|
154
|
+
perform_post("/notifications/leave/#{id}.json")
|
155
|
+
end
|
156
|
+
|
157
|
+
def block(id)
|
158
|
+
perform_post("/blocks/create/#{id}.json")
|
159
|
+
end
|
160
|
+
|
161
|
+
def unblock(id)
|
162
|
+
perform_post("/blocks/destroy/#{id}.json")
|
163
|
+
end
|
164
|
+
|
165
|
+
def help
|
166
|
+
perform_get('/help/test.json')
|
167
|
+
end
|
168
|
+
|
169
|
+
private
|
170
|
+
def perform_get(path, options={})
|
171
|
+
Twitter::Request.get(self, path, options)
|
172
|
+
end
|
173
|
+
|
174
|
+
def perform_post(path, options={})
|
175
|
+
Twitter::Request.post(self, path, options)
|
176
|
+
end
|
177
|
+
|
178
|
+
# Quick and dirty method for determining mime type of uploaded file
|
179
|
+
def mime_type(file)
|
180
|
+
case
|
181
|
+
when file =~ /\.jpg/ then 'image/jpg'
|
182
|
+
when file =~ /\.gif$/ then 'image/gif'
|
183
|
+
when file =~ /\.png$/ then 'image/png'
|
184
|
+
else 'application/octet-stream'
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
CRLF = "\r\n"
|
189
|
+
# Encodes the request as multipart
|
190
|
+
def add_multipart_data(req,params)
|
191
|
+
boundary = Time.now.to_i.to_s(16)
|
192
|
+
req["Content-Type"] = "multipart/form-data; boundary=#{boundary}"
|
193
|
+
body = ""
|
194
|
+
params.each do |key,value|
|
195
|
+
esc_key = CGI.escape(key.to_s)
|
196
|
+
body << "--#{boundary}#{CRLF}"
|
197
|
+
if value.respond_to?(:read)
|
198
|
+
body << "Content-Disposition: form-data; name=\"#{esc_key}\"; filename=\"#{File.basename(value.path)}\"#{CRLF}"
|
199
|
+
body << "Content-Type: #{mime_type(value.path)}#{CRLF*2}"
|
200
|
+
body << value.read
|
201
|
+
else
|
202
|
+
body << "Content-Disposition: form-data; name=\"#{esc_key}\"#{CRLF*2}#{value}"
|
203
|
+
end
|
204
|
+
body << CRLF
|
205
|
+
end
|
206
|
+
body << "--#{boundary}--#{CRLF*2}"
|
207
|
+
req.body = body
|
208
|
+
req["Content-Length"] = req.body.size
|
209
|
+
end
|
210
|
+
|
211
|
+
#Uses the OAuth gem to add the signed Authorization header
|
212
|
+
def add_oauth(req)
|
213
|
+
self.client.consumer.sign!(req, self.client.access_token)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Twitter
|
2
|
+
class HTTPAuth
|
3
|
+
include HTTParty
|
4
|
+
format :plain
|
5
|
+
|
6
|
+
attr_reader :username, :password, :options
|
7
|
+
|
8
|
+
def initialize(username, password, options={})
|
9
|
+
@username, @password = username, password
|
10
|
+
@options = {:ssl => false}.merge(options)
|
11
|
+
self.class.base_uri "http#{'s' if options[:ssl]}://twitter.com"
|
12
|
+
end
|
13
|
+
|
14
|
+
def get(uri, headers={})
|
15
|
+
self.class.get(uri, :headers => headers, :basic_auth => basic_auth)
|
16
|
+
end
|
17
|
+
|
18
|
+
def post(uri, body={}, headers={})
|
19
|
+
self.class.post(uri, :body => body, :headers => headers, :basic_auth => basic_auth)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
def basic_auth
|
24
|
+
@basic_auth ||= {:username => @username, :password => @password}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Twitter
|
2
|
+
class OAuth
|
3
|
+
extend Forwardable
|
4
|
+
def_delegators :access_token, :get, :post
|
5
|
+
|
6
|
+
attr_reader :ctoken, :csecret, :consumer_options
|
7
|
+
|
8
|
+
# Options
|
9
|
+
# :sign_in => true to just sign in with twitter instead of doing oauth authorization
|
10
|
+
# (http://apiwiki.twitter.com/Sign-in-with-Twitter)
|
11
|
+
def initialize(ctoken, csecret, options={})
|
12
|
+
@ctoken, @csecret, @consumer_options = ctoken, csecret, {}
|
13
|
+
|
14
|
+
if options[:sign_in]
|
15
|
+
@consumer_options[:authorize_path] = '/oauth/authenticate'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def consumer
|
20
|
+
@consumer ||= ::OAuth::Consumer.new(@ctoken, @csecret, {:site => 'http://twitter.com'}.merge(consumer_options))
|
21
|
+
end
|
22
|
+
|
23
|
+
def request_token
|
24
|
+
@request_token ||= consumer.get_request_token
|
25
|
+
end
|
26
|
+
|
27
|
+
def authorize_from_request(rtoken, rsecret)
|
28
|
+
request_token = ::OAuth::RequestToken.new(consumer, rtoken, rsecret)
|
29
|
+
access_token = request_token.get_access_token
|
30
|
+
@atoken, @asecret = access_token.token, access_token.secret
|
31
|
+
end
|
32
|
+
|
33
|
+
def access_token
|
34
|
+
@access_token ||= ::OAuth::AccessToken.new(consumer, @atoken, @asecret)
|
35
|
+
end
|
36
|
+
|
37
|
+
def authorize_from_access(atoken, asecret)
|
38
|
+
@atoken, @asecret = atoken, asecret
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|