dschn-twitter 0.3.7.1
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.txt +106 -0
- data/License.txt +19 -0
- data/Manifest.txt +71 -0
- data/README.txt +84 -0
- data/Rakefile +4 -0
- data/bin/twitter +15 -0
- data/config/hoe.rb +74 -0
- data/config/requirements.rb +17 -0
- data/examples/blocks.rb +15 -0
- data/examples/direct_messages.rb +28 -0
- data/examples/favorites.rb +20 -0
- data/examples/friends_followers.rb +25 -0
- data/examples/friendships.rb +13 -0
- data/examples/identica_timeline.rb +7 -0
- data/examples/location.rb +8 -0
- data/examples/posting.rb +9 -0
- data/examples/replies.rb +26 -0
- data/examples/search.rb +17 -0
- data/examples/sent_messages.rb +26 -0
- data/examples/timeline.rb +33 -0
- data/examples/twitter.rb +27 -0
- data/examples/verify_credentials.rb +13 -0
- data/lib/twitter.rb +21 -0
- data/lib/twitter/base.rb +260 -0
- data/lib/twitter/cli.rb +328 -0
- data/lib/twitter/cli/config.rb +9 -0
- data/lib/twitter/cli/helpers.rb +97 -0
- data/lib/twitter/cli/migrations/20080722194500_create_accounts.rb +13 -0
- data/lib/twitter/cli/migrations/20080722194508_create_tweets.rb +16 -0
- data/lib/twitter/cli/migrations/20080722214605_add_account_id_to_tweets.rb +9 -0
- data/lib/twitter/cli/migrations/20080722214606_create_configurations.rb +13 -0
- data/lib/twitter/cli/models/account.rb +33 -0
- data/lib/twitter/cli/models/configuration.rb +13 -0
- data/lib/twitter/cli/models/tweet.rb +20 -0
- data/lib/twitter/direct_message.rb +22 -0
- data/lib/twitter/easy_class_maker.rb +43 -0
- data/lib/twitter/rate_limit_status.rb +19 -0
- data/lib/twitter/search.rb +94 -0
- data/lib/twitter/status.rb +22 -0
- data/lib/twitter/user.rb +37 -0
- data/lib/twitter/version.rb +9 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/script/txt2html +74 -0
- data/setup.rb +1585 -0
- data/spec/base_spec.rb +109 -0
- data/spec/cli/helper_spec.rb +35 -0
- data/spec/direct_message_spec.rb +35 -0
- data/spec/fixtures/followers.xml +706 -0
- data/spec/fixtures/friends.xml +609 -0
- data/spec/fixtures/friends_for.xml +584 -0
- data/spec/fixtures/friends_lite.xml +192 -0
- data/spec/fixtures/friends_timeline.xml +66 -0
- data/spec/fixtures/public_timeline.xml +148 -0
- data/spec/fixtures/rate_limit_status.xml +7 -0
- data/spec/fixtures/search_results.json +1 -0
- data/spec/fixtures/status.xml +25 -0
- data/spec/fixtures/user.xml +38 -0
- data/spec/fixtures/user_timeline.xml +465 -0
- data/spec/search_spec.rb +89 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/status_spec.rb +40 -0
- data/spec/user_spec.rb +42 -0
- data/tasks/deployment.rake +50 -0
- data/tasks/environment.rake +7 -0
- data/tasks/website.rake +17 -0
- data/twitter.gemspec +49 -0
- data/website/css/common.css +47 -0
- data/website/images/terminal_output.png +0 -0
- data/website/index.html +156 -0
- metadata +180 -0
data/lib/twitter/cli.rb
ADDED
@@ -0,0 +1,328 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
gem 'main', '>= 2.8.2'
|
3
|
+
gem 'highline', '>= 1.4.0'
|
4
|
+
gem 'activerecord', '>= 2.1'
|
5
|
+
gem 'sqlite3-ruby', '>= 1.2.1'
|
6
|
+
require 'main'
|
7
|
+
require 'highline/import'
|
8
|
+
require 'activerecord'
|
9
|
+
require 'sqlite3'
|
10
|
+
|
11
|
+
HighLine.track_eof = false
|
12
|
+
CLI_ROOT = File.expand_path(File.join(File.dirname(__FILE__), 'cli'))
|
13
|
+
require CLI_ROOT + '/config'
|
14
|
+
require CLI_ROOT + '/helpers'
|
15
|
+
Dir[CLI_ROOT + '/models/*.rb'].each { |m| require m }
|
16
|
+
|
17
|
+
include Twitter::CLI::Helpers
|
18
|
+
|
19
|
+
Main {
|
20
|
+
def run
|
21
|
+
puts "twitter [command] --help for usage instructions."
|
22
|
+
puts "The available commands are: \n install, uninstall, add, remove, list, change, post, befriend, defriend, follow, leave, d and timeline."
|
23
|
+
end
|
24
|
+
|
25
|
+
mode 'install' do
|
26
|
+
description 'Creates the sqlite3 database and runs the migrations.'
|
27
|
+
def run
|
28
|
+
migrate
|
29
|
+
attempt_import
|
30
|
+
say 'Twitter installed.'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
mode 'uninstall' do
|
35
|
+
description 'Removes the sqlite3 database. There is no undo for this.'
|
36
|
+
def run
|
37
|
+
FileUtils.rm(Twitter::CLI::Config[:database]) if File.exists?(Twitter::CLI::Config[:database])
|
38
|
+
say 'Twitter gem uninstalled.'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
mode 'add' do
|
43
|
+
description 'Adds a new twitter account to the database. Prompts for username and password.'
|
44
|
+
argument('username', 'u') {
|
45
|
+
optional
|
46
|
+
description 'optional username'
|
47
|
+
}
|
48
|
+
argument('password', 'p') {
|
49
|
+
optional
|
50
|
+
description 'optional password'
|
51
|
+
}
|
52
|
+
|
53
|
+
def run
|
54
|
+
account = Hash.new
|
55
|
+
say "Add New Account:"
|
56
|
+
|
57
|
+
# allows optional username arg
|
58
|
+
if params['username'].given?
|
59
|
+
account[:username] = params['username'].value
|
60
|
+
else
|
61
|
+
account[:username] = ask('Username: ') do |q|
|
62
|
+
q.validate = /\S+/
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# allows optional password arg
|
67
|
+
if params['password'].given?
|
68
|
+
account[:password] = params['password'].value
|
69
|
+
else
|
70
|
+
account[:password] = ask("Password (won't be displayed): ") do |q|
|
71
|
+
q.echo = false
|
72
|
+
q.validate = /\S+/
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
do_work do
|
77
|
+
base(account[:username], account[:password]).verify_credentials
|
78
|
+
Account.add(account)
|
79
|
+
say 'Account added.'
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
mode 'remove' do
|
85
|
+
description 'Removes a twitter account from the database. If username provided it removes that username else it prompts with list and asks for which one you would like to remove.'
|
86
|
+
argument( 'username' ) {
|
87
|
+
optional
|
88
|
+
description 'username of account you would like to remove'
|
89
|
+
}
|
90
|
+
|
91
|
+
def run
|
92
|
+
do_work do
|
93
|
+
if params['username'].given?
|
94
|
+
account = Account.find_by_username(params['username'].value)
|
95
|
+
else
|
96
|
+
Account.find(:all, :order => 'username').each do |a|
|
97
|
+
say "#{a.id}. #{a}"
|
98
|
+
end
|
99
|
+
account_id = ask 'Account to remove (enter number): ' do |q|
|
100
|
+
q.validate = /\d+/
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
begin
|
105
|
+
account = account_id ? Account.find(account_id) : account
|
106
|
+
account_name = account.username
|
107
|
+
account.destroy
|
108
|
+
Account.set_current(Account.first) if Account.new_active_needed?
|
109
|
+
say "#{account_name} has been removed.\n"
|
110
|
+
rescue ActiveRecord::RecordNotFound
|
111
|
+
say "ERROR: Account could not be found. Try again. \n"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
mode 'list' do
|
118
|
+
description 'Lists all the accounts that have been added and puts a * by the current one that is used for posting, etc.'
|
119
|
+
def run
|
120
|
+
do_work do
|
121
|
+
if Account.count == 0
|
122
|
+
say 'No accounts have been added.'
|
123
|
+
else
|
124
|
+
say 'Account List'
|
125
|
+
Account.find(:all, :order => 'username').each do |a|
|
126
|
+
say a
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
mode 'change' do
|
134
|
+
description 'Changes the current account being used for posting etc. to the username provided. If no username is provided, a list is presented and you can choose the account from there.'
|
135
|
+
argument( 'username' ) {
|
136
|
+
optional
|
137
|
+
description 'username of account you would like to switched to'
|
138
|
+
}
|
139
|
+
|
140
|
+
def run
|
141
|
+
do_work do
|
142
|
+
if params['username'].given?
|
143
|
+
new_current = Account.find_by_username(params['username'].value)
|
144
|
+
else
|
145
|
+
Account.find(:all, :order => 'username').each do |a|
|
146
|
+
say "#{a.id}. #{a}"
|
147
|
+
end
|
148
|
+
new_current = ask 'Change current account to (enter number): ' do |q|
|
149
|
+
q.validate = /\d+/
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
begin
|
154
|
+
current = Account.set_current(new_current)
|
155
|
+
say "#{current} is now the current account.\n"
|
156
|
+
rescue ActiveRecord::RecordNotFound
|
157
|
+
say "ERROR: Account could not be found. Try again. \n"
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
mode 'post' do
|
164
|
+
description "Posts a message to twitter using the current account. The following are all valid examples from the command line:
|
165
|
+
$ twitter post 'my update'
|
166
|
+
$ twitter post my update with quotes
|
167
|
+
$ echo 'my update from stdin' | twitter post"
|
168
|
+
def run
|
169
|
+
do_work do
|
170
|
+
post = ARGV.size > 1 ? ARGV.join(" ") : ARGV.shift
|
171
|
+
say "Sending twitter update"
|
172
|
+
finished, status = false, nil
|
173
|
+
progress_thread = Thread.new { until finished; print "."; $stdout.flush; sleep 0.5; end; }
|
174
|
+
post_thread = Thread.new(binding()) do |b|
|
175
|
+
status = base.post(post, :source => Twitter::SourceName)
|
176
|
+
finished = true
|
177
|
+
end
|
178
|
+
post_thread.join
|
179
|
+
progress_thread.join
|
180
|
+
say "Got it! New twitter created at: #{status.created_at}\n"
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
mode 'befriend' do
|
186
|
+
description "Allows you to add a user as a friend"
|
187
|
+
argument('username') {
|
188
|
+
required
|
189
|
+
description 'username or id of twitterrer to befriend'
|
190
|
+
}
|
191
|
+
|
192
|
+
def run
|
193
|
+
do_work do
|
194
|
+
username = params['username'].value
|
195
|
+
base.create_friendship(username)
|
196
|
+
say "#{username} has been added as a friend. follow notifications with 'twitter follow #{username}'"
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
mode 'defriend' do
|
202
|
+
description "Allows you to remove a user from being a friend"
|
203
|
+
argument('username') {
|
204
|
+
required
|
205
|
+
description 'username or id of twitterrer to defriend'
|
206
|
+
}
|
207
|
+
|
208
|
+
def run
|
209
|
+
do_work do
|
210
|
+
username = params['username'].value
|
211
|
+
base.destroy_friendship(username)
|
212
|
+
say "#{username} has been removed from your friends"
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
mode 'follow' do
|
218
|
+
description "Allows you to turn on notifications for a user"
|
219
|
+
argument('username') {
|
220
|
+
required
|
221
|
+
description 'username or id of twitterrer to follow'
|
222
|
+
}
|
223
|
+
|
224
|
+
def run
|
225
|
+
do_work do
|
226
|
+
username = params['username'].value
|
227
|
+
base.follow(username)
|
228
|
+
say "You are now following notifications from #{username}"
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
mode 'leave' do
|
234
|
+
description "Allows you to turn off notifications for a user"
|
235
|
+
argument('username') {
|
236
|
+
required
|
237
|
+
description 'username or id of twitterrer to leave'
|
238
|
+
}
|
239
|
+
|
240
|
+
def run
|
241
|
+
do_work do
|
242
|
+
username = params['username'].value
|
243
|
+
base.leave(username)
|
244
|
+
say "You are no longer following notifications from #{username}"
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
mode 'd' do
|
250
|
+
description "Allows you to direct message a user. The following are all valid examples from the command line:
|
251
|
+
$ twitter d jnunemaker 'yo homeboy'
|
252
|
+
$ twitter d jnunemaker yo homeboy
|
253
|
+
$ echo 'yo homeboy' | twitter d jnunemaker"
|
254
|
+
argument('username') {
|
255
|
+
required
|
256
|
+
description 'username or id of twitterrer to direct message'
|
257
|
+
}
|
258
|
+
|
259
|
+
def run
|
260
|
+
do_work do
|
261
|
+
username = params['username'].value
|
262
|
+
post = ARGV.size > 1 ? ARGV.join(" ") : ARGV.shift
|
263
|
+
base.d(username, post)
|
264
|
+
say "Direct message sent to #{username}"
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
mode 'timeline' do
|
270
|
+
description "Allows you to view your timeline, your friends or the public one"
|
271
|
+
argument( 'timeline' ) {
|
272
|
+
description 'the timeline you wish to see (friends, public, me)'
|
273
|
+
default 'friends'
|
274
|
+
}
|
275
|
+
option('force', 'f') {
|
276
|
+
description "Ignore since_id and show first page of results even if there aren't new ones"
|
277
|
+
}
|
278
|
+
|
279
|
+
def run
|
280
|
+
do_work do
|
281
|
+
timeline = params['timeline'].value == 'me' ? 'user' : params['timeline'].value
|
282
|
+
options, since_id = {}, Configuration["#{timeline}_since_id"]
|
283
|
+
options[:since_id] = since_id if !since_id.blank? && !params['force'].given?
|
284
|
+
cache = [:friends, :user].include?(timeline)
|
285
|
+
collection = base.timeline(timeline.to_sym, options)
|
286
|
+
output_tweets(collection, {:cache => cache, :since_prefix => timeline})
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
mode 'replies' do
|
292
|
+
description 'Allows you to view all @replies at you'
|
293
|
+
option('force', 'f') {
|
294
|
+
description "Ignore since_id and show first page of replies even if there aren't new ones"
|
295
|
+
}
|
296
|
+
|
297
|
+
def run
|
298
|
+
do_work do
|
299
|
+
options, since_id = {}, Configuration["replies_since_id"]
|
300
|
+
options[:since_id] = since_id if !since_id.blank? && !params['force'].given?
|
301
|
+
collection = base.replies(options)
|
302
|
+
output_tweets(collection, {:since_prefix => 'replies'})
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
mode 'clear_config' do
|
308
|
+
def run
|
309
|
+
do_work do
|
310
|
+
count = Configuration.count
|
311
|
+
Configuration.destroy_all
|
312
|
+
say("#{count} configuration entries cleared.")
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
mode 'open' do
|
318
|
+
description 'Opens the given twitter user in a browser window'
|
319
|
+
argument('username') {
|
320
|
+
required
|
321
|
+
description "username or id of twitterrer who's page you would like to see"
|
322
|
+
}
|
323
|
+
|
324
|
+
def run
|
325
|
+
`open http://twitter.com/#{params['username'].value}`
|
326
|
+
end
|
327
|
+
end
|
328
|
+
}
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module Twitter
|
2
|
+
module CLI
|
3
|
+
module Helpers
|
4
|
+
class NoActiveAccount < StandardError; end
|
5
|
+
class NoAccounts < StandardError; end
|
6
|
+
|
7
|
+
def output_tweets(collection, options={})
|
8
|
+
options.reverse_merge!({
|
9
|
+
:cache => false,
|
10
|
+
:since_prefix => '',
|
11
|
+
:empty_msg => 'Nothing new since your last check.'
|
12
|
+
})
|
13
|
+
if collection.size > 0
|
14
|
+
justify = collection.collect { |s| s.user.screen_name }.max { |a,b| a.length <=> b.length }.length rescue 0
|
15
|
+
indention = ' ' * (justify + 3)
|
16
|
+
say("\n#{indention}#{collection.size} new tweet(s) found.\n\n")
|
17
|
+
collection.each do |s|
|
18
|
+
Tweet.create_from_tweet(current_account, s) if options[:cache]
|
19
|
+
occurred_at = Time.parse(s.created_at).strftime('On %b %d at %l:%M%P')
|
20
|
+
formatted_time = '-' * occurred_at.length + "\n#{indention}#{occurred_at}"
|
21
|
+
formatted_name = s.user.screen_name.rjust(justify + 1)
|
22
|
+
formatted_msg = ''
|
23
|
+
s.text.split(' ').in_groups_of(6, false) { |row| formatted_msg += row.join(' ') + "\n#{indention}" }
|
24
|
+
say "#{CGI::unescapeHTML(formatted_name)}: #{CGI::unescapeHTML(formatted_msg)}#{formatted_time}\n\n"
|
25
|
+
end
|
26
|
+
Configuration["#{options[:since_prefix]}_since_id"] = collection.first.id
|
27
|
+
else
|
28
|
+
say(options[:empty_msg])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def base(username=current_account.username, password=current_account.password)
|
33
|
+
@base ||= Twitter::Base.new(username, password)
|
34
|
+
end
|
35
|
+
|
36
|
+
def current_account
|
37
|
+
@current_account ||= Account.active
|
38
|
+
raise Account.count == 0 ? NoAccounts : NoActiveAccount if @current_account.blank?
|
39
|
+
@current_account
|
40
|
+
end
|
41
|
+
|
42
|
+
def attempt_import(&block)
|
43
|
+
tweet_file = File.join(ENV['HOME'], '.twitter')
|
44
|
+
if File.exists?(tweet_file)
|
45
|
+
say '.twitter file found, attempting import...'
|
46
|
+
config = YAML::load(File.read(tweet_file))
|
47
|
+
if !config['email'].blank? && !config['password'].blank?
|
48
|
+
Account.add(:username => config['email'], :password => config['password'])
|
49
|
+
say 'Account imported'
|
50
|
+
block.call if block_given?
|
51
|
+
true
|
52
|
+
else
|
53
|
+
say "Either your username or password were blank in your .twitter file so I could not import. Use 'twitter add' to add an account."
|
54
|
+
false
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def do_work(&block)
|
60
|
+
connect
|
61
|
+
begin
|
62
|
+
block.call
|
63
|
+
rescue Twitter::RateExceeded
|
64
|
+
say("Twitter says you've been making too many requests. Wait for a bit and try again.")
|
65
|
+
rescue Twitter::Unavailable
|
66
|
+
say("Twitter is unavailable right now. Try again later.")
|
67
|
+
rescue Twitter::CantConnect => msg
|
68
|
+
say("Can't connect to twitter because: #{msg}")
|
69
|
+
rescue Twitter::CLI::Helpers::NoActiveAccount
|
70
|
+
say("You have not set an active account. Use 'twitter change' to set one now.")
|
71
|
+
rescue Twitter::CLI::Helpers::NoAccounts
|
72
|
+
unless attempt_import { block.call }
|
73
|
+
say("You have not created any accounts. Use 'twitter add' to create one now.")
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def connect
|
79
|
+
ActiveRecord::Base.logger = Logger.new('/tmp/twitter_ar_logger.log')
|
80
|
+
ActiveRecord::Base.establish_connection(Twitter::CLI::Config)
|
81
|
+
ActiveRecord::Base.connection
|
82
|
+
end
|
83
|
+
|
84
|
+
def migrate
|
85
|
+
connect
|
86
|
+
ActiveRecord::Migrator.migrate("#{CLI_ROOT}/migrations/")
|
87
|
+
end
|
88
|
+
|
89
|
+
def connect_and_migrate
|
90
|
+
say('Attempting to establish connection...')
|
91
|
+
connect
|
92
|
+
say('Connection established...migrating database...')
|
93
|
+
migrate
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|