class T::CLI

Constants

DEFAULT_NUM_RESULTS
DIRECT_MESSAGE_HEADINGS
MAX_SEARCH_RESULTS
TREND_HEADINGS

Public Class Methods

new(*) click to toggle source
Calls superclass method
# File lib/t/cli.rb, line 37
def initialize(*)
  @rcfile = T::RCFile.instance
  super
end

Public Instance Methods

accounts() click to toggle source
# File lib/t/cli.rb, line 43
def accounts
  @rcfile.path = options["profile"] if options["profile"]
  @rcfile.profiles.each do |profile|
    say profile[0]
    profile[1].each_key do |key|
      say "  #{key}#{@rcfile.active_profile[0] == profile[0] && @rcfile.active_profile[1] == key ? ' (active)' : nil}"
    end
  end
end
authorize() click to toggle source
# File lib/t/cli.rb, line 55
def authorize
  @rcfile.path = options["profile"] if options["profile"]
  if @rcfile.empty?
    say "Welcome! Before you can use t, you'll first need to register an"
    say "application with Twitter. Just follow the steps below:"
    say "  1. Sign in to the Twitter Application Management site and click"
    say '     "Create New App".'
    say "  2. Complete the required fields and submit the form."
    say "     Note: Your application must have a unique name."
    say "  3. Go to the Permissions tab of your application, and change the"
    say '     Access setting to "Read, Write and Access direct messages".'
    say "  4. Go to the Keys and Access Tokens tab to view the consumer key"
    say "     and secret which you'll need to copy and paste below when"
    say "     prompted."
  else
    say "It looks like you've already registered an application with Twitter."
    say "To authorize a new account, just follow the steps below:"
    say "  1. Sign in to the Twitter Developer site."
    say "  2. Select the application for which you'd like to authorize an account."
    say "  3. Copy and paste the consumer key and secret below when prompted."
  end
  say
  ask "Press [Enter] to open the Twitter Developer site."
  say
  require "launchy"
  open_or_print("https://apps.twitter.com", dry_run: options["display-uri"])
  key = ask "Enter your API key:"
  secret = ask "Enter your API secret:"
  consumer = OAuth::Consumer.new(key, secret, site: Twitter::REST::Request::BASE_URL)
  request_token = consumer.get_request_token
  uri = generate_authorize_uri(consumer, request_token)
  say
  say "In a moment, you will be directed 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 and paste the supplied PIN below when prompted."
  say
  ask "Press [Enter] to open the Twitter app authorization page."
  say
  open_or_print(uri, dry_run: options["display-uri"])
  pin = ask "Enter the supplied PIN:"
  access_token = request_token.get_access_token(oauth_verifier: pin.chomp)
  oauth_response = access_token.get("/1.1/account/verify_credentials.json?skip_status=true")
  screen_name = oauth_response.body.match(/"screen_name"\s*:\s*"(.*?)"/).captures.first
  @rcfile[screen_name] = {
    key => {
      "username" => screen_name,
      "consumer_key" => key,
      "consumer_secret" => secret,
      "token" => access_token.token,
      "secret" => access_token.secret,
    },
  }
  @rcfile.active_profile = {"username" => screen_name, "consumer_key" => key}
  say "Authorization successful."
end
block(user, *users) click to toggle source
# File lib/t/cli.rb, line 115
def block(user, *users)
  blocked_users, number = fetch_users(users.unshift(user), options) do |users_to_block|
    client.block(users_to_block)
  end
  say "@#{@rcfile.active_profile[0]} blocked #{pluralize(number, 'user')}."
  say
  say "Run `#{File.basename($PROGRAM_NAME)} delete block #{blocked_users.collect { |blocked_user| "@#{blocked_user.screen_name}" }.join(' ')}` to unblock."
end
direct_messages() click to toggle source
# File lib/t/cli.rb, line 131
def direct_messages
  count = options["number"] || DEFAULT_NUM_RESULTS
  opts = {}
  opts[:include_entities] = !!options["decode_uris"]
  direct_messages = collect_with_count(count) do |count_opts|
    client.direct_messages_received(count_opts.merge(opts))
  end
  users = direct_messages.empty? ? [] : client.users(direct_messages.map(&:sender_id))
  users_hash = users.each_with_object({}) { |user, hash| hash[user.id] = user }

  direct_messages.reverse! if options["reverse"]
  if options["csv"]
    require "csv"
    say DIRECT_MESSAGE_HEADINGS.to_csv unless direct_messages.empty?
    direct_messages.each do |direct_message|
      sender = users_hash[direct_message.sender_id] || Twitter::NullObject.new
      say [direct_message.id, csv_formatted_time(direct_message), sender.screen_name, decode_full_text(direct_message, options["decode_uris"])].to_csv
    end
  elsif options["long"]
    array = direct_messages.collect do |direct_message|
      sender = users_hash[direct_message.sender_id] || Twitter::NullObject.new
      [direct_message.id, ls_formatted_time(direct_message), "@#{sender.screen_name}", decode_full_text(direct_message, options["decode_uris"]).gsub(/\n+/, " ")]
    end
    format = options["format"] || Array.new(DIRECT_MESSAGE_HEADINGS.size) { "%s" }
    print_table_with_headings(array, DIRECT_MESSAGE_HEADINGS, format)
  else
    direct_messages.each do |direct_message|
      sender = users_hash[direct_message.sender_id] || Twitter::NullObject.new
      print_message(sender.screen_name, direct_message.text)
    end
  end
end
direct_messages_sent() click to toggle source
# File lib/t/cli.rb, line 172
def direct_messages_sent
  count = options["number"] || DEFAULT_NUM_RESULTS
  opts = {}
  opts[:include_entities] = !!options["decode_uris"]
  direct_messages = collect_with_count(count) do |count_opts|
    client.direct_messages_sent(count_opts.merge(opts))
  end
  direct_messages.reverse! if options["reverse"]
  if options["csv"]
    require "csv"
    say DIRECT_MESSAGE_HEADINGS.to_csv unless direct_messages.empty?
    direct_messages.each do |direct_message|
      say [direct_message.id, csv_formatted_time(direct_message), direct_message.recipient.screen_name, decode_full_text(direct_message, options["decode_uris"])].to_csv
    end
  elsif options["long"]
    array = direct_messages.collect do |direct_message|
      [direct_message.id, ls_formatted_time(direct_message), "@#{direct_message.recipient.screen_name}", decode_full_text(direct_message, options["decode_uris"]).gsub(/\n+/, " ")]
    end
    format = options["format"] || Array.new(DIRECT_MESSAGE_HEADINGS.size) { "%s" }
    print_table_with_headings(array, DIRECT_MESSAGE_HEADINGS, format)
  else
    direct_messages.each do |direct_message|
      print_message(direct_message.recipient.screen_name, direct_message.text)
    end
  end
end
dm(user, message) click to toggle source
# File lib/t/cli.rb, line 202
def dm(user, message)
  require "t/core_ext/string"
  recipient = options["id"] ? client.user(user.to_i) : client.user(user.strip_ats)
  client.create_direct_message_event(recipient, message)
  say "Direct Message sent from @#{@rcfile.active_profile[0]} to @#{recipient.screen_name}."
end
does_contain(user_list, user = nil) click to toggle source
# File lib/t/cli.rb, line 212
def does_contain(user_list, user = nil)
  owner, list_name = extract_owner(user_list, options)
  user = if user.nil?
    @rcfile.active_profile[0]
  else
    require "t/core_ext/string"
    options["id"] ? client.user(user.to_i).screen_name : user.strip_ats
  end
  if client.list_member?(owner, list_name, user)
    say "Yes, #{list_name} contains @#{user}."
  else
    abort "No, #{list_name} does not contain @#{user}."
  end
end
does_follow(user1, user2 = nil) click to toggle source
# File lib/t/cli.rb, line 230
def does_follow(user1, user2 = nil)
  abort "No, you are not following yourself." if user2.nil? && @rcfile.active_profile[0].casecmp(user1).zero?
  abort "No, @#{user1} is not following themself." if user1 == user2
  require "t/core_ext/string"
  thread1 = if options["id"]
    Thread.new { client.user(user1.to_i).screen_name }
  else
    Thread.new { user1.strip_ats }
  end
  thread2 = if user2.nil?
    Thread.new { @rcfile.active_profile[0] }
  elsif options["id"]
    Thread.new { client.user(user2.to_i).screen_name }
  else
    Thread.new { user2.strip_ats }
  end
  user1 = thread1.value
  user2 = thread2.value
  if client.friendship?(user1, user2)
    say "Yes, @#{user1} follows @#{user2}."
  else
    abort "No, @#{user1} does not follow @#{user2}."
  end
end
favorite(status_id, *status_ids) click to toggle source
# File lib/t/cli.rb, line 257
def favorite(status_id, *status_ids)
  status_ids.unshift(status_id)
  status_ids.collect!(&:to_i)
  require "retryable"
  favorites = Retryable.retryable(tries: 3, on: Twitter::Error, sleep: 0) do
    client.favorite(status_ids)
  end
  number = favorites.length
  say "@#{@rcfile.active_profile[0]} favorited #{pluralize(number, 'tweet')}."
  say
  say "Run `#{File.basename($PROGRAM_NAME)} delete favorite #{status_ids.join(' ')}` to unfavorite."
end
favorites(user = nil) click to toggle source
# File lib/t/cli.rb, line 281
def favorites(user = nil)
  count = options["number"] || DEFAULT_NUM_RESULTS
  opts = {}
  opts[:exclude_replies] = true if options["exclude"] == "replies"
  opts[:include_entities] = !!options["decode_uris"]
  opts[:include_rts] = false if options["exclude"] == "retweets"
  opts[:max_id] = options["max_id"] if options["max_id"]
  opts[:since_id] = options["since_id"] if options["since_id"]
  if user
    require "t/core_ext/string"
    user = options["id"] ? user.to_i : user.strip_ats
  end
  tweets = collect_with_count(count) do |count_opts|
    client.favorites(user, count_opts.merge(opts))
  end
  print_tweets(tweets)
end
follow(user, *users) click to toggle source
# File lib/t/cli.rb, line 302
def follow(user, *users)
  followed_users, number = fetch_users(users.unshift(user), options) do |users_to_follow|
    client.follow(users_to_follow)
  end
  say "@#{@rcfile.active_profile[0]} is now following #{pluralize(number, 'more user')}."
  say
  say "Run `#{File.basename($PROGRAM_NAME)} unfollow #{followed_users.collect { |followed_user| "@#{followed_user.screen_name}" }.join(' ')}` to stop."
end
followers(user = nil) click to toggle source
# File lib/t/cli.rb, line 367
def followers(user = nil)
  if user
    require "t/core_ext/string"
    user = options["id"] ? user.to_i : user.strip_ats
  end
  follower_ids = client.follower_ids(user).to_a
  require "retryable"
  users = Retryable.retryable(tries: 3, on: Twitter::Error, sleep: 0) do
    client.users(follower_ids)
  end
  print_users(users)
end
followings(user = nil) click to toggle source
# File lib/t/cli.rb, line 319
def followings(user = nil)
  if user
    require "t/core_ext/string"
    user = options["id"] ? user.to_i : user.strip_ats
  end
  following_ids = client.friend_ids(user).to_a
  require "retryable"
  users = Retryable.retryable(tries: 3, on: Twitter::Error, sleep: 0) do
    client.users(following_ids)
  end
  print_users(users)
end
followings_following(user1, user2 = nil) click to toggle source
# File lib/t/cli.rb, line 340
def followings_following(user1, user2 = nil)
  require "t/core_ext/string"
  user1 = options["id"] ? user1.to_i : user1.strip_ats
  user2 = if user2.nil?
    @rcfile.active_profile[0]
  else
    options["id"] ? user2.to_i : user2.strip_ats
  end
  follower_ids = Thread.new { client.follower_ids(user1).to_a }
  following_ids = Thread.new { client.friend_ids(user2).to_a }
  followings_following_ids = follower_ids.value & following_ids.value
  require "retryable"
  users = Retryable.retryable(tries: 3, on: Twitter::Error, sleep: 0) do
    client.users(followings_following_ids)
  end
  print_users(users)
end
friends(user = nil) click to toggle source
# File lib/t/cli.rb, line 388
def friends(user = nil)
  user = if user
    require "t/core_ext/string"
    options["id"] ? user.to_i : user.strip_ats
  else
    client.verify_credentials.screen_name
  end
  following_ids = Thread.new { client.friend_ids(user).to_a }
  follower_ids = Thread.new { client.follower_ids(user).to_a }
  friend_ids = following_ids.value & follower_ids.value
  require "retryable"
  users = Retryable.retryable(tries: 3, on: Twitter::Error, sleep: 0) do
    client.users(friend_ids)
  end
  print_users(users)
end
groupies(user = nil) click to toggle source
# File lib/t/cli.rb, line 413
def groupies(user = nil)
  user = if user
    require "t/core_ext/string"
    options["id"] ? user.to_i : user.strip_ats
  else
    client.verify_credentials.screen_name
  end
  follower_ids = Thread.new { client.follower_ids(user).to_a }
  following_ids = Thread.new { client.friend_ids(user).to_a }
  groupie_ids = (follower_ids.value - following_ids.value)
  require "retryable"
  users = Retryable.retryable(tries: 3, on: Twitter::Error, sleep: 0) do
    client.users(groupie_ids)
  end
  print_users(users)
end
intersection(first_user, *users) click to toggle source
# File lib/t/cli.rb, line 440
def intersection(first_user, *users)
  users.push(first_user)
  # If only one user is specified, compare to the authenticated user
  users.push(@rcfile.active_profile[0]) if users.size == 1
  require "t/core_ext/string"
  options["id"] ? users.collect!(&:to_i) : users.collect!(&:strip_ats)
  sets = parallel_map(users) do |user|
    case options["type"]
    when "followings"
      client.friend_ids(user).to_a
    when "followers"
      client.follower_ids(user).to_a
    end
  end
  intersection = sets.reduce(:&)
  require "retryable"
  users = Retryable.retryable(tries: 3, on: Twitter::Error, sleep: 0) do
    client.users(intersection)
  end
  print_users(users)
end
leaders(user = nil) click to toggle source
# File lib/t/cli.rb, line 471
def leaders(user = nil)
  user = if user
    require "t/core_ext/string"
    options["id"] ? user.to_i : user.strip_ats
  else
    client.verify_credentials.screen_name
  end
  following_ids = Thread.new { client.friend_ids(user).to_a }
  follower_ids = Thread.new { client.follower_ids(user).to_a }
  leader_ids = (following_ids.value - follower_ids.value)
  require "retryable"
  users = Retryable.retryable(tries: 3, on: Twitter::Error, sleep: 0) do
    client.users(leader_ids)
  end
  print_users(users)
end
lists(user = nil) click to toggle source
# File lib/t/cli.rb, line 496
def lists(user = nil)
  lists = if user
    require "t/core_ext/string"
    user = options["id"] ? user.to_i : user.strip_ats
    client.lists(user)
  else
    client.lists
  end
  print_lists(lists)
end
matrix() click to toggle source
# File lib/t/cli.rb, line 508
def matrix
  opts = {count: MAX_SEARCH_RESULTS, include_entities: false}
  tweets = client.search("lang:ja", opts)
  tweets.each do |tweet|
    say(tweet.text.gsub(/[^\u3000\u3040-\u309f]/, "").reverse, %i[bold green on_black], false)
  end
end
mentions() click to toggle source
# File lib/t/cli.rb, line 523
def mentions
  count = options["number"] || DEFAULT_NUM_RESULTS
  opts = {}
  opts[:include_entities] = !!options["decode_uris"]
  tweets = collect_with_count(count) do |count_opts|
    client.mentions(count_opts.merge(opts))
  end
  print_tweets(tweets)
end
mute(user, *users) click to toggle source
# File lib/t/cli.rb, line 536
def mute(user, *users)
  muted_users, number = fetch_users(users.unshift(user), options) do |users_to_mute|
    client.mute(users_to_mute)
  end
  say "@#{@rcfile.active_profile[0]} muted #{pluralize(number, 'user')}."
  say
  say "Run `#{File.basename($PROGRAM_NAME)} delete mute #{muted_users.collect { |muted_user| "@#{muted_user.screen_name}" }.join(' ')}` to unmute."
end
muted() click to toggle source
# File lib/t/cli.rb, line 552
def muted
  muted_ids = client.muted_ids.to_a
  require "retryable"
  muted_users = Retryable.retryable(tries: 3, on: Twitter::Error, sleep: 0) do
    client.users(muted_ids)
  end
  print_users(muted_users)
end
open(user) click to toggle source
# File lib/t/cli.rb, line 566
def open(user)
  require "launchy"
  if options["id"]
    user = client.user(user.to_i)
    open_or_print(user.uri, dry_run: options["display-uri"])
  elsif options["status"]
    status = client.status(user.to_i, include_my_retweet: false)
    open_or_print(status.uri, dry_run: options["display-uri"])
  else
    require "t/core_ext/string"
    open_or_print("https://twitter.com/#{user.strip_ats}", dry_run: options["display-uri"])
  end
end
reach(tweet_id) click to toggle source
# File lib/t/cli.rb, line 581
def reach(tweet_id)
  require "t/core_ext/string"
  status_thread = Thread.new { client.status(tweet_id.to_i, include_my_retweet: false) }
  threads = client.retweeters_ids(tweet_id.to_i).collect do |retweeter_id|
    Thread.new(retweeter_id) do |user_id|
      client.follower_ids(user_id).to_a
    end
  end
  status = status_thread.value
  threads << Thread.new(status.user.id) do |user_id|
    client.follower_ids(user_id).to_a
  end
  reach = ::Set.new
  threads.each { |thread| reach += thread.value }
  reach.delete(status.user.id)
  say number_with_delimiter(reach.size)
end
reply(status_id, message = nil) click to toggle source
# File lib/t/cli.rb, line 603
def reply(status_id, message = nil)
  message = T::Editor.gets if message.to_s.empty?
  status = client.status(status_id.to_i, include_my_retweet: false)
  users = Array(status.user.screen_name)
  if options["all"]
    users += extract_mentioned_screen_names(status.full_text)
    users.uniq!
  end
  users.delete(@rcfile.active_profile[0])
  require "t/core_ext/string"
  users.collect!(&:prepend_at)
  opts = {in_reply_to_status_id: status.id, trim_user: true}
  add_location!(options, opts)
  reply = if options["file"]
    client.update_with_media("#{users.join(' ')} #{message}", File.new(File.expand_path(options["file"])), opts)
  else
    client.update("#{users.join(' ')} #{message}", opts)
  end
  say "Reply posted by @#{@rcfile.active_profile[0]} to #{users.join(' ')}."
  say
  say "Run `#{File.basename($PROGRAM_NAME)} delete status #{reply.id}` to delete."
end
report_spam(user, *users) click to toggle source
# File lib/t/cli.rb, line 628
def report_spam(user, *users)
  _, number = fetch_users(users.unshift(user), options) do |users_to_report|
    client.report_spam(users_to_report)
  end
  say "@#{@rcfile.active_profile[0]} reported #{pluralize(number, 'user')}."
end
retweet(status_id, *status_ids) click to toggle source
# File lib/t/cli.rb, line 637
def retweet(status_id, *status_ids)
  status_ids.unshift(status_id)
  status_ids.collect!(&:to_i)
  require "retryable"
  retweets = Retryable.retryable(tries: 3, on: Twitter::Error, sleep: 0) do
    client.retweet(status_ids, trim_user: true)
  end
  number = retweets.length
  say "@#{@rcfile.active_profile[0]} retweeted #{pluralize(number, 'tweet')}."
  say
  say "Run `#{File.basename($PROGRAM_NAME)} delete status #{retweets.collect(&:id).join(' ')}` to undo."
end
retweets(user = nil) click to toggle source
# File lib/t/cli.rb, line 659
def retweets(user = nil)
  count = options["number"] || DEFAULT_NUM_RESULTS
  opts = {}
  opts[:include_entities] = !!options["decode_uris"]
  tweets = if user
    require "t/core_ext/string"
    user = options["id"] ? user.to_i : user.strip_ats
    collect_with_count(count) do |count_opts|
      client.retweeted_by_user(user, count_opts.merge(opts))
    end
  else
    collect_with_count(count) do |count_opts|
      client.retweeted_by_me(count_opts.merge(opts))
    end
  end
  print_tweets(tweets)
end
retweets_of_me() click to toggle source
# File lib/t/cli.rb, line 685
def retweets_of_me
  count = options["number"] || DEFAULT_NUM_RESULTS
  opts = {}
  opts[:include_entities] = !!options["decode_uris"]
  tweets = collect_with_count(count) do |count_opts|
    client.retweets_of_me(count_opts.merge(opts))
  end
  print_tweets(tweets)
end
ruler() click to toggle source
# File lib/t/cli.rb, line 698
def ruler
  markings = "----|".chars.cycle.take(140).join
  say "#{' ' * options['indent'].to_i}#{markings}"
end
status(status_id) click to toggle source
# File lib/t/cli.rb, line 708
def status(status_id) # rubocop:disable Metrics/CyclomaticComplexity
  opts = {include_my_retweet: false}
  opts[:include_entities] = !!options["decode_uris"]
  status = client.status(status_id.to_i, opts)
  location = if status.place?
    if status.place.name? && status.place.attributes? && status.place.attributes[:street_address] && status.place.attributes[:locality] && status.place.attributes[:region] && status.place.country?
      [status.place.name, status.place.attributes[:street_address], status.place.attributes[:locality], status.place.attributes[:region], status.place.country].join(", ")
    elsif status.place.name? && status.place.attributes? && status.place.attributes[:locality] && status.place.attributes[:region] && status.place.country?
      [status.place.name, status.place.attributes[:locality], status.place.attributes[:region], status.place.country].join(", ")
    elsif status.place.full_name? && status.place.attributes? && status.place.attributes[:region] && status.place.country?
      [status.place.full_name, status.place.attributes[:region], status.place.country].join(", ")
    elsif status.place.full_name? && status.place.country?
      [status.place.full_name, status.place.country].join(", ")
    elsif status.place.full_name?
      status.place.full_name
    else
      status.place.name
    end
  elsif status.geo?
    reverse_geocode(status.geo)
  end
  status_headings = ["ID", "Posted at", "Screen name", "Text", "Retweets", "Favorites", "Source", "Location"]
  if options["csv"]
    require "csv"
    say status_headings.to_csv
    say [status.id, csv_formatted_time(status), status.user.screen_name, decode_full_text(status, options["decode_uris"]), status.retweet_count, status.favorite_count, strip_tags(status.source), location].to_csv
  elsif options["long"]
    array = [status.id, ls_formatted_time(status), "@#{status.user.screen_name}", decode_full_text(status, options["decode_uris"]).gsub(/\n+/, " "), status.retweet_count, status.favorite_count, strip_tags(status.source), location]
    format = options["format"] || Array.new(status_headings.size) { "%s" }
    print_table_with_headings([array], status_headings, format)
  else
    array = []
    array << ["ID", status.id.to_s]
    array << ["Text", decode_full_text(status, options["decode_uris"]).gsub(/\n+/, " ")]
    array << ["Screen name", "@#{status.user.screen_name}"]
    array << ["Posted at", "#{ls_formatted_time(status, :created_at, false)} (#{time_ago_in_words(status.created_at)} ago)"]
    array << ["Retweets", number_with_delimiter(status.retweet_count)]
    array << ["Favorites", number_with_delimiter(status.favorite_count)]
    array << ["Source", strip_tags(status.source)]
    array << ["Location", location] unless location.nil?
    print_table(array)
  end
end
timeline(user = nil) click to toggle source
# File lib/t/cli.rb, line 763
def timeline(user = nil)
  count = options["number"] || DEFAULT_NUM_RESULTS
  opts = {}
  opts[:exclude_replies] = true if options["exclude"] == "replies"
  opts[:include_entities] = !!options["decode_uris"]
  opts[:include_rts] = false if options["exclude"] == "retweets"
  opts[:max_id] = options["max_id"] if options["max_id"]
  opts[:since_id] = options["since_id"] if options["since_id"]
  if user
    require "t/core_ext/string"
    user = options["id"] ? user.to_i : user.strip_ats
    tweets = collect_with_count(count) do |count_opts|
      client.user_timeline(user, count_opts.merge(opts))
    end
  else
    tweets = collect_with_count(count) do |count_opts|
      client.home_timeline(count_opts.merge(opts))
    end
  end
  print_tweets(tweets)
end
trend_locations() click to toggle source
# File lib/t/cli.rb, line 802
def trend_locations
  places = client.trend_locations
  unless options["unsorted"]
    places = case options["sort"]
    when "country"
      places.sort_by { |place| place.country.downcase }
    when "parent"
      places.sort_by { |place| place.parent_id.to_i }
    when "type"
      places.sort_by { |place| place.place_type.downcase }
    when "woeid"
      places.sort_by { |place| place.woeid.to_i }
    else
      places.sort_by { |place| place.name.downcase }
    end
  end
  places.reverse! if options["reverse"]
  if options["csv"]
    require "csv"
    say TREND_HEADINGS.to_csv unless places.empty?
    places.each do |place|
      say [place.woeid, place.parent_id, place.place_type, place.name, place.country].to_csv
    end
  elsif options["long"]
    array = places.collect do |place|
      [place.woeid, place.parent_id, place.place_type, place.name, place.country]
    end
    format = options["format"] || Array.new(TREND_HEADINGS.size) { "%s" }
    print_table_with_headings(array, TREND_HEADINGS, format)
  else
    print_attribute(places, :name)
  end
end
unfollow(user, *users) click to toggle source
# File lib/t/cli.rb, line 839
def unfollow(user, *users)
  unfollowed_users, number = fetch_users(users.unshift(user), options) do |users_to_unfollow|
    client.unfollow(users_to_unfollow)
  end
  say "@#{@rcfile.active_profile[0]} is no longer following #{pluralize(number, 'user')}."
  say
  say "Run `#{File.basename($PROGRAM_NAME)} follow #{unfollowed_users.collect { |unfollowed_user| "@#{unfollowed_user.screen_name}" }.join(' ')}` to follow again."
end
update(message = nil) click to toggle source
# File lib/t/cli.rb, line 851
def update(message = nil)
  message = T::Editor.gets if message.to_s.empty?
  opts = {trim_user: true}
  add_location!(options, opts)
  status = if options["file"]
    client.update_with_media(message, File.new(File.expand_path(options["file"])), opts)
  else
    client.update(message, opts)
  end
  say "Tweet posted by @#{@rcfile.active_profile[0]}."
  say
  say "Run `#{File.basename($PROGRAM_NAME)} delete status #{status.id}` to delete."
end
users(user, *users) click to toggle source
# File lib/t/cli.rb, line 874
def users(user, *users)
  users.unshift(user)
  require "t/core_ext/string"
  options["id"] ? users.collect!(&:to_i) : users.collect!(&:strip_ats)
  require "retryable"
  users = Retryable.retryable(tries: 3, on: Twitter::Error, sleep: 0) do
    client.users(users)
  end
  print_users(users)
end
version() click to toggle source
# File lib/t/cli.rb, line 887
def version
  require "t/version"
  say T::Version
end
whoami() click to toggle source
# File lib/t/cli.rb, line 932
def whoami
  if @rcfile.active_profile && @rcfile.active_profile[0]
    user = @rcfile.active_profile[0]
    whois(user)
  else
    warn "You haven't authorized an account, run `t authorize` to get started."
  end
end
whois(user) click to toggle source
# File lib/t/cli.rb, line 899
def whois(user)
  require "t/core_ext/string"
  opts = {}
  opts[:include_entities] = !!options["decode_uris"]
  user = options["id"] ? user.to_i : user.strip_ats
  user = client.user(user, opts)
  if options["csv"] || options["long"]
    print_users([user])
  else
    array = []
    array << ["ID", user.id.to_s]
    array << ["Since", "#{ls_formatted_time(user, :created_at, false)} (#{time_ago_in_words(user.created_at)} ago)"]
    array << ["Last update", "#{decode_full_text(user.status, options['decode_uris']).gsub(/\n+/, ' ')} (#{time_ago_in_words(user.status.created_at)} ago)"] unless user.status.nil?
    array << ["Screen name", "@#{user.screen_name}"]
    array << [user.verified? ? "Name (Verified)" : "Name", user.name] unless user.name.nil? # rubocop:disable Metrics/BlockNesting
    array << ["Tweets", number_with_delimiter(user.statuses_count)]
    array << ["Favorites", number_with_delimiter(user.favorites_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)]
    array << ["Bio", user.description.gsub(/\n+/, " ")] unless user.description.nil?
    array << ["Location", user.location] unless user.location.nil?
    array << ["URL", user.website] unless user.website.nil?
    print_table(array)
  end
end

Private Instance Methods

add_location!(options, opts) click to toggle source
# File lib/t/cli.rb, line 988
def add_location!(options, opts)
  return nil unless options["location"]

  lat, lng = options["location"] == "location" ? [location.lat, location.lng] : options["location"].split(",").collect(&:to_f)
  opts.merge!(lat:, long: lng)
end
extract_mentioned_screen_names(text) click to toggle source
# File lib/t/cli.rb, line 958
def extract_mentioned_screen_names(text)
  valid_mention_preceding_chars = /(?:[^a-zA-Z0-9_!#$%&*@@]|^|RT:?)/o
  at_signs = /[@@]/
  valid_mentions = /
    (#{valid_mention_preceding_chars})  # $1: Preceeding character
    (#{at_signs})                       # $2: At mark
    ([a-zA-Z0-9_]{1,20})                # $3: Screen name
  /ox

  return [] unless text&.match?(at_signs)

  text.to_s.scan(valid_mentions).collect do |_, _, screen_name|
    screen_name
  end
end
generate_authorize_uri(consumer, request_token) click to toggle source
# File lib/t/cli.rb, line 974
def generate_authorize_uri(consumer, request_token)
  request = consumer.create_signed_request(:get, consumer.authorize_path, request_token, pin_auth_parameters)
  params = request["Authorization"].sub(/^OAuth\s+/, "").split(/,\s+/).collect do |param|
    key, value = param.split("=")
    value =~ /"(.*?)"/
    "#{key}=#{CGI.escape(Regexp.last_match[1])}"
  end.join("&")
  "#{Twitter::REST::Request::BASE_URL}#{request.path}?#{params}"
end
location() click to toggle source
# File lib/t/cli.rb, line 995
def location
  return @location if @location

  require "geokit"
  require "open-uri"
  ip_address = URI.open("http://checkip.dyndns.org/") do |body|
    /(?:\d{1,3}\.){3}\d{1,3}/.match(body.read)[0]
  end
  @location = Geokit::Geocoders::MultiGeocoder.geocode(ip_address)
end
pin_auth_parameters() click to toggle source
# File lib/t/cli.rb, line 984
def pin_auth_parameters
  {oauth_callback: "oob"}
end
reverse_geocode(geo) click to toggle source
# File lib/t/cli.rb, line 1006
def reverse_geocode(geo)
  require "geokit"
  geoloc = Geokit::Geocoders::MultiGeocoder.reverse_geocode(geo.coordinates)
  if geoloc.city && geoloc.state && geoloc.country
    [geoloc.city, geoloc.state, geoloc.country].join(", ")
  elsif geoloc.state && geoloc.country
    [geoloc.state, geoloc.country].join(", ")
  else
    geoloc.country
  end
end