t 3.1.0 → 4.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/LICENSE.md +1 -1
- data/README.md +8 -9
- data/bin/t +21 -21
- data/lib/t/cli.rb +470 -463
- data/lib/t/collectable.rb +9 -8
- data/lib/t/core_ext/kernel.rb +3 -3
- data/lib/t/core_ext/string.rb +2 -2
- data/lib/t/delete.rb +44 -41
- data/lib/t/editor.rb +5 -5
- data/lib/t/identicon.rb +1 -1
- data/lib/t/list.rb +51 -51
- data/lib/t/printable.rb +69 -65
- data/lib/t/rcfile.rb +18 -17
- data/lib/t/requestable.rb +3 -2
- data/lib/t/search.rb +82 -82
- data/lib/t/set.rb +21 -21
- data/lib/t/stream.rb +67 -60
- data/lib/t/utils.rb +25 -25
- data/lib/t/version.rb +2 -2
- data/lib/t.rb +2 -2
- data/t.gemspec +22 -21
- metadata +29 -49
data/lib/t/collectable.rb
CHANGED
@@ -1,19 +1,19 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require 'set'
|
1
|
+
require "twitter"
|
2
|
+
require "retryable"
|
4
3
|
|
5
4
|
module T
|
6
5
|
module Collectable
|
7
6
|
MAX_NUM_RESULTS = 200
|
8
7
|
MAX_PAGE = 51
|
9
8
|
|
10
|
-
def collect_with_max_id(collection = [], max_id = nil, &
|
9
|
+
def collect_with_max_id(collection = [], max_id = nil, &)
|
11
10
|
tweets = Retryable.retryable(tries: 3, on: Twitter::Error, sleep: 0) do
|
12
11
|
yield(max_id)
|
13
12
|
end
|
14
13
|
return collection if tweets.nil?
|
14
|
+
|
15
15
|
collection += tweets
|
16
|
-
tweets.empty? ? collection.flatten : collect_with_max_id(collection, tweets.last.id - 1, &
|
16
|
+
tweets.empty? ? collection.flatten : collect_with_max_id(collection, tweets.last.id - 1, &)
|
17
17
|
end
|
18
18
|
|
19
19
|
def collect_with_count(count)
|
@@ -22,7 +22,7 @@ module T
|
|
22
22
|
collect_with_max_id do |max_id|
|
23
23
|
opts[:max_id] = max_id unless max_id.nil?
|
24
24
|
opts[:count] = count unless count >= MAX_NUM_RESULTS
|
25
|
-
if count
|
25
|
+
if count.positive?
|
26
26
|
tweets = yield opts
|
27
27
|
count -= tweets.length
|
28
28
|
tweets
|
@@ -30,13 +30,14 @@ module T
|
|
30
30
|
end.flatten.compact
|
31
31
|
end
|
32
32
|
|
33
|
-
def collect_with_page(collection = ::Set.new, page = 1, previous = nil, &
|
33
|
+
def collect_with_page(collection = ::Set.new, page = 1, previous = nil, &)
|
34
34
|
tweets = Retryable.retryable(tries: 3, on: Twitter::Error, sleep: 0) do
|
35
35
|
yield page
|
36
36
|
end
|
37
37
|
return collection if tweets.nil? || tweets == previous || page >= MAX_PAGE
|
38
|
+
|
38
39
|
collection += tweets
|
39
|
-
tweets.empty? ? collection.flatten : collect_with_page(collection, page + 1, tweets, &
|
40
|
+
tweets.empty? ? collection.flatten : collect_with_page(collection, page + 1, tweets, &)
|
40
41
|
end
|
41
42
|
end
|
42
43
|
end
|
data/lib/t/core_ext/kernel.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
module Kernel
|
2
|
-
def Bignum(arg, base = 0) # rubocop:disable MethodName
|
2
|
+
def Bignum(arg, base = 0) # rubocop:disable Naming/MethodName
|
3
3
|
Integer(arg, base)
|
4
4
|
end
|
5
5
|
|
6
|
-
def Fixnum(arg, base = 0) # rubocop:disable MethodName
|
6
|
+
def Fixnum(arg, base = 0) # rubocop:disable Naming/MethodName
|
7
7
|
Integer(arg, base)
|
8
8
|
end
|
9
9
|
|
10
|
-
def NilClass(_) # rubocop:disable MethodName
|
10
|
+
def NilClass(_) # rubocop:disable Naming/MethodName
|
11
11
|
nil
|
12
12
|
end
|
13
13
|
end
|
data/lib/t/core_ext/string.rb
CHANGED
data/lib/t/delete.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
1
|
+
require "thor"
|
2
|
+
require "twitter"
|
3
|
+
require "t/rcfile"
|
4
|
+
require "t/requestable"
|
5
|
+
require "t/utils"
|
6
6
|
|
7
7
|
module T
|
8
8
|
class Delete < Thor
|
@@ -16,9 +16,9 @@ module T
|
|
16
16
|
super
|
17
17
|
end
|
18
18
|
|
19
|
-
desc
|
20
|
-
method_option
|
21
|
-
method_option
|
19
|
+
desc "block USER [USER...]", "Unblock users."
|
20
|
+
method_option "id", aliases: "-i", type: :boolean, desc: "Specify input as Twitter user IDs instead of screen names."
|
21
|
+
method_option "force", aliases: "-f", type: :boolean
|
22
22
|
def block(user, *users)
|
23
23
|
unblocked_users, number = fetch_users(users.unshift(user), options) do |users_to_unblock|
|
24
24
|
client.unblock(users_to_unblock)
|
@@ -28,35 +28,37 @@ module T
|
|
28
28
|
say "Run `#{File.basename($PROGRAM_NAME)} block #{unblocked_users.collect { |unblocked_user| "@#{unblocked_user.screen_name}" }.join(' ')}` to block."
|
29
29
|
end
|
30
30
|
|
31
|
-
desc
|
32
|
-
method_option
|
31
|
+
desc "dm [DIRECT_MESSAGE_ID] [DIRECT_MESSAGE_ID...]", "Delete the last Direct Message sent."
|
32
|
+
method_option "force", aliases: "-f", type: :boolean
|
33
33
|
def dm(direct_message_id, *direct_message_ids)
|
34
34
|
direct_message_ids.unshift(direct_message_id)
|
35
|
-
require
|
35
|
+
require "t/core_ext/string"
|
36
36
|
direct_message_ids.collect!(&:to_i)
|
37
|
-
if options[
|
38
|
-
|
39
|
-
|
40
|
-
say "@#{@rcfile.active_profile[0]} deleted the direct message sent to @#{direct_message.recipient.screen_name}: \"#{direct_message.text}\""
|
41
|
-
end
|
37
|
+
if options["force"]
|
38
|
+
client.destroy_direct_message(*direct_message_ids)
|
39
|
+
say "@#{@rcfile.active_profile[0]} deleted #{direct_message_ids.size} direct message#{direct_message_ids.size == 1 ? '' : 's'}."
|
42
40
|
else
|
43
41
|
direct_message_ids.each do |direct_message_id_to_delete|
|
44
42
|
direct_message = client.direct_message(direct_message_id_to_delete)
|
45
|
-
next unless
|
43
|
+
next unless direct_message
|
44
|
+
|
45
|
+
recipient = client.user(direct_message.recipient_id)
|
46
|
+
next unless yes? "Are you sure you want to permanently delete the direct message to @#{recipient.screen_name}: \"#{direct_message.text}\"? [y/N]"
|
47
|
+
|
46
48
|
client.destroy_direct_message(direct_message_id_to_delete)
|
47
|
-
say "@#{@rcfile.active_profile[0]} deleted the direct message sent to @#{
|
49
|
+
say "@#{@rcfile.active_profile[0]} deleted the direct message sent to @#{recipient.screen_name}: \"#{direct_message.text}\""
|
48
50
|
end
|
49
51
|
end
|
50
52
|
end
|
51
|
-
map %w
|
53
|
+
map %w[d m] => :dm
|
52
54
|
|
53
|
-
desc
|
54
|
-
method_option
|
55
|
+
desc "favorite TWEET_ID [TWEET_ID...]", "Delete favorites."
|
56
|
+
method_option "force", aliases: "-f", type: :boolean
|
55
57
|
def favorite(status_id, *status_ids)
|
56
58
|
status_ids.unshift(status_id)
|
57
|
-
require
|
59
|
+
require "t/core_ext/string"
|
58
60
|
status_ids.collect!(&:to_i)
|
59
|
-
if options[
|
61
|
+
if options["force"]
|
60
62
|
tweets = client.unfavorite(status_ids)
|
61
63
|
tweets.each do |status|
|
62
64
|
say "@#{@rcfile.active_profile[0]} unfavorited @#{status.user.screen_name}'s status: \"#{status.full_text}\""
|
@@ -65,32 +67,32 @@ module T
|
|
65
67
|
status_ids.each do |status_id_to_unfavorite|
|
66
68
|
status = client.status(status_id_to_unfavorite, include_my_retweet: false)
|
67
69
|
next unless yes? "Are you sure you want to remove @#{status.user.screen_name}'s status: \"#{status.full_text}\" from your favorites? [y/N]"
|
70
|
+
|
68
71
|
client.unfavorite(status_id_to_unfavorite)
|
69
72
|
say "@#{@rcfile.active_profile[0]} unfavorited @#{status.user.screen_name}'s status: \"#{status.full_text}\""
|
70
73
|
end
|
71
74
|
end
|
72
75
|
end
|
73
|
-
map %w
|
76
|
+
map %w[fave favourite] => :favorite
|
74
77
|
|
75
|
-
desc
|
76
|
-
method_option
|
77
|
-
method_option
|
78
|
+
desc "list LIST", "Delete a list."
|
79
|
+
method_option "force", aliases: "-f", type: :boolean
|
80
|
+
method_option "id", aliases: "-i", type: :boolean, desc: "Specify list via ID instead of slug."
|
78
81
|
def list(list)
|
79
|
-
if options[
|
80
|
-
require
|
82
|
+
if options["id"]
|
83
|
+
require "t/core_ext/string"
|
81
84
|
list = list.to_i
|
82
85
|
end
|
83
86
|
list = client.list(list)
|
84
|
-
|
85
|
-
|
86
|
-
end
|
87
|
+
return if !options["force"] && !(yes? "Are you sure you want to permanently delete the list \"#{list.name}\"? [y/N]")
|
88
|
+
|
87
89
|
client.destroy_list(list)
|
88
90
|
say "@#{@rcfile.active_profile[0]} deleted the list \"#{list.name}\"."
|
89
91
|
end
|
90
92
|
|
91
|
-
desc
|
92
|
-
method_option
|
93
|
-
method_option
|
93
|
+
desc "mute USER [USER...]", "Unmute users."
|
94
|
+
method_option "id", aliases: "-i", type: :boolean, desc: "Specify input as Twitter user IDs instead of screen names."
|
95
|
+
method_option "force", aliases: "-f", type: :boolean
|
94
96
|
def mute(user, *users)
|
95
97
|
unmuted_users, number = fetch_users(users.unshift(user), options) do |users_to_unmute|
|
96
98
|
client.unmute(users_to_unmute)
|
@@ -100,7 +102,7 @@ module T
|
|
100
102
|
say "Run `#{File.basename($PROGRAM_NAME)} mute #{unmuted_users.collect { |unmuted_user| "@#{unmuted_user.screen_name}" }.join(' ')}` to mute."
|
101
103
|
end
|
102
104
|
|
103
|
-
desc
|
105
|
+
desc "account SCREEN_NAME [CONSUMER_KEY]", "delete account or consumer key from t"
|
104
106
|
def account(account, key = nil)
|
105
107
|
if key && @rcfile.profiles[account].keys.size > 1
|
106
108
|
@rcfile.delete_key(account, key)
|
@@ -109,13 +111,13 @@ module T
|
|
109
111
|
end
|
110
112
|
end
|
111
113
|
|
112
|
-
desc
|
113
|
-
method_option
|
114
|
+
desc "status TWEET_ID [TWEET_ID...]", "Delete Tweets."
|
115
|
+
method_option "force", aliases: "-f", type: :boolean
|
114
116
|
def status(status_id, *status_ids)
|
115
117
|
status_ids.unshift(status_id)
|
116
|
-
require
|
118
|
+
require "t/core_ext/string"
|
117
119
|
status_ids.collect!(&:to_i)
|
118
|
-
if options[
|
120
|
+
if options["force"]
|
119
121
|
tweets = client.destroy_status(status_ids, trim_user: true)
|
120
122
|
tweets.each do |status|
|
121
123
|
say "@#{@rcfile.active_profile[0]} deleted the Tweet: \"#{status.full_text}\""
|
@@ -124,11 +126,12 @@ module T
|
|
124
126
|
status_ids.each do |status_id_to_delete|
|
125
127
|
status = client.status(status_id_to_delete, include_my_retweet: false)
|
126
128
|
next unless yes? "Are you sure you want to permanently delete @#{status.user.screen_name}'s status: \"#{status.full_text}\"? [y/N]"
|
129
|
+
|
127
130
|
client.destroy_status(status_id_to_delete, trim_user: true)
|
128
131
|
say "@#{@rcfile.active_profile[0]} deleted the Tweet: \"#{status.full_text}\""
|
129
132
|
end
|
130
133
|
end
|
131
134
|
end
|
132
|
-
map %w
|
135
|
+
map %w[post tweet update] => :status
|
133
136
|
end
|
134
137
|
end
|
data/lib/t/editor.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "tempfile"
|
2
|
+
require "shellwords"
|
3
3
|
|
4
4
|
module T
|
5
5
|
class Editor
|
@@ -14,7 +14,7 @@ module T
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def tempfile
|
17
|
-
Tempfile.new(
|
17
|
+
Tempfile.new("TWEET_EDITMSG")
|
18
18
|
end
|
19
19
|
|
20
20
|
def edit(path)
|
@@ -22,11 +22,11 @@ module T
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def editor
|
25
|
-
ENV[
|
25
|
+
ENV["VISUAL"] || ENV["EDITOR"] || system_editor
|
26
26
|
end
|
27
27
|
|
28
28
|
def system_editor
|
29
|
-
RbConfig::CONFIG[
|
29
|
+
/mswin|mingw/.match?(RbConfig::CONFIG["host_os"]) ? "notepad" : "vi"
|
30
30
|
end
|
31
31
|
end
|
32
32
|
end
|
data/lib/t/identicon.rb
CHANGED
data/lib/t/list.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
1
|
+
require "thor"
|
2
|
+
require "twitter"
|
3
|
+
require "t/collectable"
|
4
|
+
require "t/printable"
|
5
|
+
require "t/rcfile"
|
6
|
+
require "t/requestable"
|
7
|
+
require "t/utils"
|
8
8
|
|
9
9
|
module T
|
10
10
|
class List < Thor
|
@@ -22,8 +22,8 @@ module T
|
|
22
22
|
super
|
23
23
|
end
|
24
24
|
|
25
|
-
desc
|
26
|
-
method_option
|
25
|
+
desc "add LIST USER [USER...]", "Add members to a list."
|
26
|
+
method_option "id", aliases: "-i", type: :boolean, desc: "Specify input as Twitter user IDs instead of screen names."
|
27
27
|
def add(list_name, user, *users)
|
28
28
|
added_users, number = fetch_users(users.unshift(user), options) do |users_to_add|
|
29
29
|
client.add_list_members(list_name, users_to_add)
|
@@ -31,63 +31,63 @@ module T
|
|
31
31
|
end
|
32
32
|
say "@#{@rcfile.active_profile[0]} added #{pluralize(number, 'member')} to the list \"#{list_name}\"."
|
33
33
|
say
|
34
|
-
if options[
|
34
|
+
if options["id"]
|
35
35
|
say "Run `#{File.basename($PROGRAM_NAME)} list remove --id #{list_name} #{added_users.join(' ')}` to undo."
|
36
36
|
else
|
37
37
|
say "Run `#{File.basename($PROGRAM_NAME)} list remove #{list_name} #{added_users.collect { |added_user| "@#{added_user}" }.join(' ')}` to undo."
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
-
desc
|
42
|
-
method_option
|
41
|
+
desc "create LIST [DESCRIPTION]", "Create a new list."
|
42
|
+
method_option "private", aliases: "-p", type: :boolean
|
43
43
|
def create(list_name, description = nil)
|
44
|
-
opts = description ? {description:
|
45
|
-
opts[:mode] =
|
44
|
+
opts = description ? {description:} : {}
|
45
|
+
opts[:mode] = "private" if options["private"]
|
46
46
|
client.create_list(list_name, opts)
|
47
47
|
say "@#{@rcfile.active_profile[0]} created the list \"#{list_name}\"."
|
48
48
|
end
|
49
49
|
|
50
|
-
desc
|
51
|
-
method_option
|
50
|
+
desc "information [USER/]LIST", "Retrieves detailed information about a Twitter list."
|
51
|
+
method_option "csv", aliases: "-c", type: :boolean, desc: "Output in CSV format."
|
52
52
|
def information(user_list)
|
53
53
|
owner, list_name = extract_owner(user_list, options)
|
54
54
|
list = client.list(owner, list_name)
|
55
|
-
if options[
|
56
|
-
require
|
57
|
-
say [
|
55
|
+
if options["csv"]
|
56
|
+
require "csv"
|
57
|
+
say ["ID", "Description", "Slug", "Screen name", "Created at", "Members", "Subscribers", "Following", "Mode", "URL"].to_csv
|
58
58
|
say [list.id, list.description, list.slug, list.user.screen_name, csv_formatted_time(list), list.member_count, list.subscriber_count, list.following?, list.mode, list.uri].to_csv
|
59
59
|
else
|
60
60
|
array = []
|
61
|
-
array << [
|
62
|
-
array << [
|
63
|
-
array << [
|
64
|
-
array << [
|
65
|
-
array << [
|
66
|
-
array << [
|
67
|
-
array << [
|
68
|
-
array << [
|
69
|
-
array << [
|
70
|
-
array << [
|
61
|
+
array << ["ID", list.id.to_s]
|
62
|
+
array << ["Description", list.description] unless list.description.nil?
|
63
|
+
array << ["Slug", list.slug]
|
64
|
+
array << ["Screen name", "@#{list.user.screen_name}"]
|
65
|
+
array << ["Created at", "#{ls_formatted_time(list, :created_at, false)} (#{time_ago_in_words(list.created_at)} ago)"]
|
66
|
+
array << ["Members", number_with_delimiter(list.member_count)]
|
67
|
+
array << ["Subscribers", number_with_delimiter(list.subscriber_count)]
|
68
|
+
array << ["Status", list.following? ? "Following" : "Not following"]
|
69
|
+
array << ["Mode", list.mode]
|
70
|
+
array << ["URL", list.uri]
|
71
71
|
print_table(array)
|
72
72
|
end
|
73
73
|
end
|
74
|
-
map %w
|
74
|
+
map %w[details] => :information
|
75
75
|
|
76
|
-
desc
|
77
|
-
method_option
|
78
|
-
method_option
|
79
|
-
method_option
|
80
|
-
method_option
|
81
|
-
method_option
|
82
|
-
method_option
|
76
|
+
desc "members [USER/]LIST", "Returns the members of a Twitter list."
|
77
|
+
method_option "csv", aliases: "-c", type: :boolean, desc: "Output in CSV format."
|
78
|
+
method_option "id", aliases: "-i", type: :boolean, desc: "Specify user via ID instead of screen name."
|
79
|
+
method_option "long", aliases: "-l", type: :boolean, desc: "Output in long format."
|
80
|
+
method_option "reverse", aliases: "-r", type: :boolean, desc: "Reverse the order of the sort."
|
81
|
+
method_option "sort", aliases: "-s", type: :string, enum: %w[favorites followers friends listed screen_name since tweets tweeted], default: "screen_name", desc: "Specify the order of the results.", banner: "ORDER"
|
82
|
+
method_option "unsorted", aliases: "-u", type: :boolean, desc: "Output is not sorted."
|
83
83
|
def members(user_list)
|
84
84
|
owner, list_name = extract_owner(user_list, options)
|
85
85
|
users = client.list_members(owner, list_name).to_a
|
86
86
|
print_users(users)
|
87
87
|
end
|
88
88
|
|
89
|
-
desc
|
90
|
-
method_option
|
89
|
+
desc "remove LIST USER [USER...]", "Remove members from a list."
|
90
|
+
method_option "id", aliases: "-i", type: :boolean, desc: "Specify input as Twitter user IDs instead of screen names."
|
91
91
|
def remove(list_name, user, *users)
|
92
92
|
removed_users, number = fetch_users(users.unshift(user), options) do |users_to_remove|
|
93
93
|
client.remove_list_members(list_name, users_to_remove)
|
@@ -95,31 +95,31 @@ module T
|
|
95
95
|
end
|
96
96
|
say "@#{@rcfile.active_profile[0]} removed #{pluralize(number, 'member')} from the list \"#{list_name}\"."
|
97
97
|
say
|
98
|
-
if options[
|
98
|
+
if options["id"]
|
99
99
|
say "Run `#{File.basename($PROGRAM_NAME)} list add --id #{list_name} #{removed_users.join(' ')}` to undo."
|
100
100
|
else
|
101
101
|
say "Run `#{File.basename($PROGRAM_NAME)} list add #{list_name} #{removed_users.collect { |removed_user| "@#{removed_user}" }.join(' ')}` to undo."
|
102
102
|
end
|
103
103
|
end
|
104
104
|
|
105
|
-
desc
|
106
|
-
method_option
|
107
|
-
method_option
|
108
|
-
method_option
|
109
|
-
method_option
|
110
|
-
method_option
|
111
|
-
method_option
|
112
|
-
method_option
|
105
|
+
desc "timeline [USER/]LIST", "Show tweet timeline for members of the specified list."
|
106
|
+
method_option "csv", aliases: "-c", type: :boolean, desc: "Output in CSV format."
|
107
|
+
method_option "decode_uris", aliases: "-d", type: :boolean, desc: "Decodes t.co URLs into their original form."
|
108
|
+
method_option "id", aliases: "-i", type: :boolean, desc: "Specify user via ID instead of screen name."
|
109
|
+
method_option "long", aliases: "-l", type: :boolean, desc: "Output in long format."
|
110
|
+
method_option "number", aliases: "-n", type: :numeric, default: DEFAULT_NUM_RESULTS, desc: "Limit the number of results."
|
111
|
+
method_option "relative_dates", aliases: "-a", type: :boolean, desc: "Show relative dates."
|
112
|
+
method_option "reverse", aliases: "-r", type: :boolean, desc: "Reverse the order of the sort."
|
113
113
|
def timeline(user_list)
|
114
114
|
owner, list_name = extract_owner(user_list, options)
|
115
|
-
count = options[
|
115
|
+
count = options["number"] || DEFAULT_NUM_RESULTS
|
116
116
|
opts = {}
|
117
|
-
opts[:include_entities] = !!options[
|
117
|
+
opts[:include_entities] = !!options["decode_uris"]
|
118
118
|
tweets = collect_with_count(count) do |count_opts|
|
119
119
|
client.list_timeline(owner, list_name, count_opts.merge(opts))
|
120
120
|
end
|
121
121
|
print_tweets(tweets)
|
122
122
|
end
|
123
|
-
map %w
|
123
|
+
map %w[tl] => :timeline
|
124
124
|
end
|
125
125
|
end
|