Class: T::CLI

Inherits:
Thor
  • Object
show all
Includes:
ActionView::Helpers::DateHelper, ActionView::Helpers::NumberHelper, ActionView::Helpers::TextHelper, Collectable, Printable, Requestable
Defined in:
lib/t/cli.rb

Constant Summary collapse

DEFAULT_NUM_RESULTS =
20
MAX_SCREEN_NAME_SIZE =
20
MAX_USERS_PER_REQUEST =
100

Constants included from Requestable

Requestable::DEFAULT_HOST, Requestable::DEFAULT_PROTOCOL

Instance Method Summary collapse

Methods included from Requestable

#base_url, #client, #host, included, #protocol

Methods included from Printable

included, #print_in_columns, #print_status_list, #print_user_list

Methods included from Collectable

#collect_with_cursor

Constructor Details

#initializeCLI

Returns a new instance of CLI.



44
45
46
47
# File 'lib/t/cli.rb', line 44

def initialize(*)
  super
  @rcfile = RCFile.instance
end

Instance Method Details

#accountsObject



50
51
52
53
54
55
56
57
58
# File 'lib/t/cli.rb', line 50

def accounts
  @rcfile.path = options['profile'] if options['profile']
  @rcfile.profiles.each do |profile|
    say profile[0]
    profile[1].keys.each do |key|
      say "  #{key}#{@rcfile.default_profile[0] == profile[0] && @rcfile.default_profile[1] == key ? " (default)" : nil}"
    end
  end
end

#authorizeObject



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/t/cli.rb', line 65

def authorize
  request_token = consumer.get_request_token
  url = generate_authorize_url(request_token)
  if options['prompt']
    say "In a moment, your web browser will open to the Twitter app authorization page."
    say "Perform the following steps to complete the authorization process:"
    say "  1. Sign in to Twitter"
    say "  2. Press \"Authorize app\""
    say "  3. Copy or memorize the supplied PIN"
    say "  4. Return to the terminal to enter the PIN"
    say
    ask "Press [Enter] to open the Twitter app authorization page."
    say
  end
  Launchy.open(url, :dry_run => options.fetch('dry_run', false))
  pin = ask "Paste in the supplied PIN:"
  access_token = request_token.get_access_token(:oauth_verifier => pin.chomp)
  oauth_response = access_token.get('/1/account/verify_credentials.json')
  screen_name = oauth_response.body.match(/"screen_name"\s*:\s*"(.*?)"/).captures.first
  @rcfile.path = options['profile'] if options['profile']
  @rcfile[screen_name] = {
    options['consumer_key'] => {
      'username' => screen_name,
      'consumer_key' => options['consumer_key'],
      'consumer_secret' => options['consumer_secret'],
      'token' => access_token.token,
      'secret' => access_token.secret,
    }
  }
  @rcfile.default_profile = {'username' => screen_name, 'consumer_key' => options['consumer_key']}
  say "Authorization successful."
end

#block(screen_name, *screen_names) ⇒ Object



99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/t/cli.rb', line 99

def block(screen_name, *screen_names)
  screen_names.unshift(screen_name)
  screen_names.threaded_each do |screen_name|
    screen_name.strip_ats
    retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
      client.block(screen_name, :include_entities => false)
    end
  end
  say "@#{@rcfile.default_profile[0]} blocked @#{screen_names.join(' ')}."
  say
  say "Run `#{File.basename($0)} delete block #{screen_names.join(' ')}` to unblock."
end

#direct_messagesObject



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/t/cli.rb', line 116

def direct_messages
  count = options['number'] || DEFAULT_NUM_RESULTS
  direct_messages = client.direct_messages(:count => count, :include_entities => false)
  direct_messages.reverse! if options['reverse']
  if options['long']
    array = direct_messages.map do |direct_message|
      created_at = direct_message.created_at > 6.months.ago ? direct_message.created_at.strftime("%b %e %H:%M") : direct_message.created_at.strftime("%b %e  %Y")
      [number_with_delimiter(direct_message.id), created_at, "@#{direct_message.sender.screen_name}", direct_message.text.gsub(/\n+/, ' ')]
    end
    if STDOUT.tty?
      headings = ["ID", "Posted at", "Screen name", "Text"]
      array.unshift(headings) unless direct_messages.empty?
    end
    print_table(array)
  else
    direct_messages.each do |direct_message|
      say "#{direct_message.sender.screen_name.rjust(MAX_SCREEN_NAME_SIZE)}: #{direct_message.text.gsub(/\n+/, ' ')} (#{time_ago_in_words(direct_message.created_at)} ago)"
    end
  end
end

#direct_messages_sentObject



142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/t/cli.rb', line 142

def direct_messages_sent
  count = options['number'] || DEFAULT_NUM_RESULTS
  direct_messages = client.direct_messages_sent(:count => count, :include_entities => false)
  direct_messages.reverse! if options['reverse']
  if options['long']
    array = direct_messages.map do |direct_message|
      created_at = direct_message.created_at > 6.months.ago ? direct_message.created_at.strftime("%b %e %H:%M") : direct_message.created_at.strftime("%b %e  %Y")
      [number_with_delimiter(direct_message.id), created_at, "@#{direct_message.recipient.screen_name}", direct_message.text.gsub(/\n+/, ' ')]
    end
    if STDOUT.tty?
      headings = ["ID", "Posted at", "Screen name", "Text"]
      array.unshift(headings) unless direct_messages.empty?
    end
    print_table(array)
  else
    direct_messages.each do |direct_message|
      say "#{direct_message.recipient.screen_name.rjust(MAX_SCREEN_NAME_SIZE)}: #{direct_message.text.gsub(/\n+/, ' ')} (#{time_ago_in_words(direct_message.created_at)} ago)"
    end
  end
end

#dm(screen_name, message) ⇒ Object



165
166
167
168
169
# File 'lib/t/cli.rb', line 165

def dm(screen_name, message)
  screen_name = screen_name.strip_ats
  direct_message = client.direct_message_create(screen_name, message, :include_entities => false)
  say "Direct Message sent from @#{@rcfile.default_profile[0]} to @#{direct_message.recipient.screen_name} (#{time_ago_in_words(direct_message.created_at)} ago)."
end

#favorite(status_id, *status_ids) ⇒ Object



173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/t/cli.rb', line 173

def favorite(status_id, *status_ids)
  status_ids.unshift(status_id)
  status_ids.map!(&:strip_commas).map!(&:to_i)
  favorites = status_ids.threaded_map do |status_id|
    retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
      client.favorite(status_id, :include_entities => false)
    end
  end
  favorites.each do |status|
    say "@#{@rcfile.default_profile[0]} favorited @#{status.user.screen_name}'s status: \"#{status.text.gsub(/\n+/, ' ')}\""
  end
  say
  say "Run `#{File.basename($0)} delete favorite #{status_ids.join(' ')}` to unfavorite."
end

#favoritesObject



193
194
195
196
197
# File 'lib/t/cli.rb', line 193

def favorites
  count = options['number'] || DEFAULT_NUM_RESULTS
  statuses = client.favorites(:count => count, :include_entities => false)
  print_status_list(statuses)
end

#follow(screen_name, *screen_names) ⇒ Object



201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/t/cli.rb', line 201

def follow(screen_name, *screen_names)
  screen_names.unshift(screen_name)
  screen_names.threaded_each do |screen_name|
    retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
      client.follow(screen_name, :include_entities => false)
    end
  end
  number = screen_names.length
  say "@#{@rcfile.default_profile[0]} is now following #{number} more #{number == 1 ? 'user' : 'users'}."
  say
  say "Run `#{File.basename($0)} unfollow users #{screen_names.join(' ')}` to stop."
end

#followersObject



246
247
248
249
250
251
252
253
254
255
256
# File 'lib/t/cli.rb', line 246

def followers
  follower_ids = collect_with_cursor do |cursor|
    client.follower_ids(:cursor => cursor)
  end
  users = follower_ids.in_groups_of(MAX_USERS_PER_REQUEST, false).threaded_map do |follower_id_group|
    retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
      client.users(follower_id_group, :include_entities => false)
    end
  end.flatten
  print_user_list(users)
end

#followingsObject



224
225
226
227
228
229
230
231
232
233
234
# File 'lib/t/cli.rb', line 224

def followings
  following_ids = collect_with_cursor do |cursor|
    client.friend_ids(:cursor => cursor)
  end
  users = following_ids.in_groups_of(MAX_USERS_PER_REQUEST, false).threaded_map do |following_id_group|
    retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
      client.users(following_id_group, :include_entities => false)
    end
  end.flatten
  print_user_list(users)
end

#friendsObject



268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
# File 'lib/t/cli.rb', line 268

def friends
  following_ids = collect_with_cursor do |cursor|
    client.friend_ids(:cursor => cursor)
  end
  follower_ids = collect_with_cursor do |cursor|
    client.follower_ids(:cursor => cursor)
  end
  friend_ids = (following_ids & follower_ids)
  users = friend_ids.in_groups_of(MAX_USERS_PER_REQUEST, false).threaded_map do |friend_id_group|
    retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
      client.users(friend_id_group, :include_entities => false)
    end
  end.flatten
  print_user_list(users)
end

#leadersObject



294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
# File 'lib/t/cli.rb', line 294

def leaders
  following_ids = collect_with_cursor do |cursor|
    client.friend_ids(:cursor => cursor)
  end
  follower_ids = collect_with_cursor do |cursor|
    client.follower_ids(:cursor => cursor)
  end
  leader_ids = (following_ids - follower_ids)
  users = leader_ids.in_groups_of(MAX_USERS_PER_REQUEST, false).threaded_map do |leader_id_group|
    retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
      client.users(leader_id_group, :include_entities => false)
    end
  end.flatten
  print_user_list(users)
end

#mentionsObject



314
315
316
317
318
# File 'lib/t/cli.rb', line 314

def mentions
  count = options['number'] || DEFAULT_NUM_RESULTS
  statuses = client.mentions(:count => count, :include_entities => false)
  print_status_list(statuses)
end

#open(screen_name) ⇒ Object



323
324
325
326
# File 'lib/t/cli.rb', line 323

def open(screen_name)
  screen_name = screen_name.strip_ats
  Launchy.open("https://twitter.com/#{screen_name}", :dry_run => options.fetch('dry_run', false))
end

#reply(status_id, message) ⇒ Object



330
331
332
333
334
335
336
337
338
339
# File 'lib/t/cli.rb', line 330

def reply(status_id, message)
  status_id = status_id.strip_commas
  status = client.status(status_id, :include_entities => false, :include_my_retweet => false, :trim_user => true)
  opts = {:in_reply_to_status_id => status.id, :include_entities => false, :trim_user => true}
  opts.merge!(:lat => location.lat, :long => location.lng) if options['location']
  reply = client.update("@#{status.user.screen_name} #{message}", opts)
  say "Reply created by @#{@rcfile.default_profile[0]} to @#{status.user.screen_name} (#{time_ago_in_words(reply.created_at)} ago)."
  say
  say "Run `#{File.basename($0)} delete status #{reply.id}` to delete."
end

#report_spam(screen_name, *screen_names) ⇒ Object



342
343
344
345
346
347
348
349
350
351
# File 'lib/t/cli.rb', line 342

def report_spam(screen_name, *screen_names)
  screen_names.unshift(screen_name)
  screen_names.threaded_each do |screen_name|
    screen_name.strip_ats
    retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
      client.report_spam(screen_name, :include_entities => false)
    end
  end
  say "@#{@rcfile.default_profile[0]} reported @#{screen_names.join(' ')}."
end

#retweet(status_id, *status_ids) ⇒ Object



355
356
357
358
359
360
361
362
363
364
365
366
367
368
# File 'lib/t/cli.rb', line 355

def retweet(status_id, *status_ids)
  status_ids.unshift(status_id)
  status_ids.map!(&:strip_commas).map!(&:to_i)
  retweets = status_ids.threaded_map do |status_id|
    retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
      client.retweet(status_id, :include_entities => false, :trim_user => true)
    end
  end
  retweets.each do |status|
    say "@#{@rcfile.default_profile[0]} retweeted @#{status.user.screen_name}'s status: \"#{status.text.gsub(/\n+/, ' ')}\""
  end
  say
  say "Run `#{File.basename($0)} delete status #{status_ids.join(' ')}` to undo."
end

#retweets(screen_name = nil) ⇒ Object



375
376
377
378
379
380
# File 'lib/t/cli.rb', line 375

def retweets(screen_name=nil)
  screen_name = screen_name.strip_ats if screen_name
  count = options['number'] || DEFAULT_NUM_RESULTS
  statuses = client.retweeted_by(screen_name, :count => count, :include_entities => false)
  print_status_list(statuses)
end

#status(status_id) ⇒ Object



384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
# File 'lib/t/cli.rb', line 384

def status(status_id)
  status_id = status_id.strip_commas
  status = client.status(status_id, :include_entities => false, :include_my_retweet => false)
  created_at = status.created_at > 6.months.ago ? status.created_at.strftime("%b %e %H:%M") : status.created_at.strftime("%b %e  %Y")
  array = []
  array << ["Text", status.text.gsub(/\n+/, ' ')]
  array << ["Screen name", "@#{status.user.screen_name}"]
  array << ["Posted at", created_at]
  unless status.geo.nil?
    location = Geokit::Geocoders::MultiGeocoder.reverse_geocode(status.geo.coordinates)
    if location.city && location.state && location.country
      array << ["Location", [location.city, location.state, location.country].join(", ")]
    elsif location.state && location.country
      array << ["Location", [location.state, location.country].join(", ")]
    elsif location.country
      array << ["Location", location.country]
    end
  end
  array << ["Retweets", number_with_delimiter(status.retweet_count)] unless status.retweet_count.zero?
  array << ["Source", strip_tags(status.source)]
  array << ["URL", "https://twitter.com/#{status.user.screen_name}/status/#{status.id}"]
  print_table(array)
end

#suggestObject



419
420
421
422
423
# File 'lib/t/cli.rb', line 419

def suggest
  limit = options['number'] || DEFAULT_NUM_RESULTS
  users = client.recommendations(:limit => limit, :include_entities => false)
  print_user_list(users)
end

#timeline(screen_name = nil) ⇒ Object



429
430
431
432
433
434
435
436
437
438
# File 'lib/t/cli.rb', line 429

def timeline(screen_name=nil)
  count = options['number'] || DEFAULT_NUM_RESULTS
  if screen_name
    screen_name = screen_name.strip_ats
    statuses = client.user_timeline(screen_name, :count => count, :include_entities => false)
  else
    statuses = client.home_timeline(:count => count, :include_entities => false)
  end
  print_status_list(statuses)
end

#unfollow(screen_name, *screen_names) ⇒ Object



442
443
444
445
446
447
448
449
450
451
452
453
# File 'lib/t/cli.rb', line 442

def unfollow(screen_name, *screen_names)
  screen_names.unshift(screen_name)
  screen_names.threaded_each do |screen_name|
    retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
      client.unfollow(screen_name, :include_entities => false)
    end
  end
  number = screen_names.length
  say "@#{@rcfile.default_profile[0]} is no longer following #{number} #{number == 1 ? 'user' : 'users'}."
  say
  say "Run `#{File.basename($0)} follow users #{screen_names.join(' ')}` to follow again."
end

#update(message) ⇒ Object



457
458
459
460
461
462
463
464
# File 'lib/t/cli.rb', line 457

def update(message)
  opts = {:include_entities => false, :trim_user => true}
  opts.merge!(:lat => location.lat, :long => location.lng) if options['location']
  status = client.update(message, opts)
  say "Tweet created by @#{@rcfile.default_profile[0]} (#{time_ago_in_words(status.created_at)} ago)."
  say
  say "Run `#{File.basename($0)} delete status #{status.id}` to delete."
end

#users(screen_name, *screen_names) ⇒ Object



477
478
479
480
481
# File 'lib/t/cli.rb', line 477

def users(screen_name, *screen_names)
  screen_names.unshift(screen_name)
  users = client.users(screen_names, :include_entities => false)
  print_user_list(users)
end

#versionObject



485
486
487
# File 'lib/t/cli.rb', line 485

def version
  say T::Version
end

#whois(screen_name) ⇒ Object



491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
# File 'lib/t/cli.rb', line 491

def whois(screen_name)
  screen_name = screen_name.strip_ats
  user = client.user(screen_name, :include_entities => false)
  array = []
  name_label = user.verified ? "Name (Verified)" : "Name"
  array << ["ID", number_with_delimiter(user.id)]
  array << [name_label, user.name] unless user.name.nil?
  array << ["Bio", user.description.gsub(/\n+/, ' ')] unless user.description.nil?
  array << ["Location", user.location] unless user.location.nil?
  array << ["URL", user.url] unless user.url.nil?
  following = user.following ? "Following" : "Not following"
  array << ["Status", following] unless user.following.nil?
  array << ["Last update", "#{user.status.text.gsub(/\n+/, ' ')} (#{time_ago_in_words(user.status.created_at)} ago)"] unless user.status.nil?
  created_at = user.created_at > 6.months.ago ? user.created_at.strftime("%b %e %H:%M") : user.created_at.strftime("%b %e  %Y")
  array << ["Since", created_at]
  array << ["Tweets", number_with_delimiter(user.statuses_count)]
  array << ["Favorites", number_with_delimiter(user.favourites_count)]
  array << ["Listed", number_with_delimiter(user.listed_count)]
  array << ["Following", number_with_delimiter(user.friends_count)]
  array << ["Followers", number_with_delimiter(user.followers_count)]
  print_table(array)
end