twat 0.6.3 → 0.9.2
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/.rvmrc +1 -1
- data/.travis.yml +6 -0
- data/Gemfile +1 -4
- data/Gemfile.lock +43 -23
- data/Gemfile.travis +12 -0
- data/Rakefile +9 -0
- data/TODO +4 -1
- data/lib/twat.rb +30 -26
- data/lib/twat/argparse.rb +36 -79
- data/lib/twat/config.rb +22 -34
- data/lib/twat/endpoint.rb +11 -5
- data/lib/twat/endpoints/base.rb +39 -0
- data/lib/twat/endpoints/identica.rb +5 -1
- data/lib/twat/endpoints/twitter.rb +5 -1
- data/lib/twat/exceptions.rb +93 -46
- data/lib/twat/follow_mixin.rb +134 -0
- data/lib/twat/options.rb +28 -12
- data/lib/twat/subcommand.rb +34 -0
- data/lib/twat/subcommands/add.rb +17 -0
- data/lib/twat/subcommands/base.rb +122 -0
- data/lib/twat/subcommands/config.rb +15 -0
- data/lib/twat/subcommands/delete.rb +24 -0
- data/lib/twat/subcommands/finger.rb +23 -0
- data/lib/twat/subcommands/follow.rb +18 -0
- data/lib/twat/subcommands/follow_tag.rb +19 -0
- data/lib/twat/subcommands/list.rb +17 -0
- data/lib/twat/subcommands/mentions.rb +20 -0
- data/lib/twat/subcommands/set.rb +19 -0
- data/lib/twat/subcommands/track.rb +33 -0
- data/lib/twat/subcommands/update.rb +21 -0
- data/lib/twat/subcommands/update_config.rb +16 -0
- data/lib/twat/subcommands/version.rb +14 -0
- data/lib/twat/twatopt.rb +0 -0
- data/lib/twat/tweetstack.rb +38 -0
- data/lib/twat/version.rb +7 -0
- data/man/twat.1 +8 -5
- data/spec/argparse_spec.rb +48 -0
- data/spec/helpers/environment.rb +47 -0
- data/spec/helpers/fileutils.rb +18 -0
- data/spec/helpers/fixtures/core.rb +30 -0
- data/spec/helpers/fixtures/migrations.rb +13 -0
- data/spec/helpers/oauth.rb +15 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/subcommands/add_spec.rb +61 -0
- data/spec/subcommands/config_spec.rb +13 -0
- data/spec/subcommands/delete_spec.rb +12 -0
- data/spec/subcommands/finger_spec.rb +41 -0
- data/spec/subcommands/follow_spec.rb +12 -0
- data/spec/subcommands/follow_tag_spec.rb +52 -0
- data/spec/subcommands/list_spec.rb +8 -0
- data/spec/subcommands/mentions_spec.rb +24 -0
- data/spec/subcommands/set_spec.rb +126 -0
- data/spec/subcommands/track_spec.rb +23 -0
- data/spec/subcommands/update_config_spec.rb +23 -0
- data/spec/subcommands/update_spec.rb +12 -0
- data/spec/subcommands/version_spec.rb +12 -0
- data/spec/twat_cli_spec.rb +102 -0
- data/spec/twat_spec.rb +12 -0
- data/twat.gemspec +6 -2
- metadata +135 -22
- data/lib/twat/actions.rb +0 -85
- data/lib/twat/actions/add.rb +0 -36
- data/lib/twat/actions/delete.rb +0 -14
- data/lib/twat/actions/follow.rb +0 -148
- data/lib/twat/actions/follow_user.rb +0 -11
- data/lib/twat/actions/setoption.rb +0 -14
- data/lib/twat/actions/show.rb +0 -12
- data/lib/twat/actions/tweet.rb +0 -14
- data/lib/twat/actions/updateconfig.rb +0 -9
- data/lib/twat/actions/user_feed.rb +0 -17
- data/lib/twat/actions/version.rb +0 -9
data/lib/twat/actions.rb
DELETED
@@ -1,85 +0,0 @@
|
|
1
|
-
module Twat
|
2
|
-
OAUTH_TOKENS = [ :oauth_token, :oauth_token_secret ]
|
3
|
-
CONSUMER_TOKENS = [ :consumer_key, :consumer_secret ]
|
4
|
-
|
5
|
-
|
6
|
-
class Actions
|
7
|
-
|
8
|
-
attr_accessor :config, :opts, :failcount
|
9
|
-
|
10
|
-
def method_missing(sym, *args, &block)
|
11
|
-
raise NoSuchCommand
|
12
|
-
end
|
13
|
-
|
14
|
-
private
|
15
|
-
|
16
|
-
def pad(n)
|
17
|
-
"%02d" % n
|
18
|
-
end
|
19
|
-
|
20
|
-
def reader
|
21
|
-
@reader ||= ReadlineNG::Reader.new
|
22
|
-
end
|
23
|
-
|
24
|
-
# Format a tweet all pretty like
|
25
|
-
def format(twt, idx = nil)
|
26
|
-
idx = pad(idx) if idx
|
27
|
-
text = deentitize(twt.text)
|
28
|
-
if config.colors?
|
29
|
-
buf = idx ? "#{idx.cyan}:" : ""
|
30
|
-
if twt.user.screen_name == config.account_name.to_s
|
31
|
-
buf += "#{twt.user.screen_name.bold.blue}: #{text}"
|
32
|
-
elsif text.mentions?(config.account_name)
|
33
|
-
buf += "#{twt.user.screen_name.bold.red}: #{text}"
|
34
|
-
else
|
35
|
-
buf += "#{twt.user.screen_name.bold.cyan}: #{text}"
|
36
|
-
end
|
37
|
-
else
|
38
|
-
buf = idx ? "#{idx}: " : ""
|
39
|
-
buf += "#{twt.user.screen_name}: #{text}"
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
def deentitize(text)
|
44
|
-
{"<" => "<", ">" => ">", "&" => "&", """ => '"', "'" => '\'' }.each do |k,v|
|
45
|
-
text.gsub!(k, v)
|
46
|
-
end
|
47
|
-
text
|
48
|
-
end
|
49
|
-
|
50
|
-
def twitter_auth
|
51
|
-
Twitter.configure do |twit|
|
52
|
-
config.account.each do |key, value|
|
53
|
-
twit.send("#{key}=", value)
|
54
|
-
end
|
55
|
-
config.endpoint.consumer_info.each do |key, value|
|
56
|
-
twit.send("#{key}=", value)
|
57
|
-
end
|
58
|
-
twit.endpoint = config.endpoint.url
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def account_name
|
63
|
-
@account_name ||=
|
64
|
-
if opts.include?(:account)
|
65
|
-
opts[:account]
|
66
|
-
else
|
67
|
-
config[:default]
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
def account
|
72
|
-
@account ||= config.accounts[account_name]
|
73
|
-
unless @account
|
74
|
-
raise NoSuchAccount
|
75
|
-
else
|
76
|
-
return @account
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
def beep
|
81
|
-
print "\a"
|
82
|
-
end
|
83
|
-
|
84
|
-
end
|
85
|
-
end
|
data/lib/twat/actions/add.rb
DELETED
@@ -1,36 +0,0 @@
|
|
1
|
-
module Twat
|
2
|
-
class Actions
|
3
|
-
|
4
|
-
# Add is somewhat of a special case, everything else hangs off config for
|
5
|
-
# it's magic, However we're forced to do it manually here- config doesn't
|
6
|
-
# know anything about it yet
|
7
|
-
def add
|
8
|
-
endpoint = Endpoint.new(opts[:endpoint])
|
9
|
-
v = endpoint.consumer_info.map do |key, value|
|
10
|
-
value
|
11
|
-
end
|
12
|
-
|
13
|
-
oauth_options = { :site => endpoint.url }
|
14
|
-
oauth_options.merge!(endpoint.oauth_options)
|
15
|
-
|
16
|
-
oauth = OAuth::Consumer.new( v[0], v[1], oauth_options )
|
17
|
-
token_request = oauth.get_request_token()
|
18
|
-
reader.puts_above "Please authenticate the application at #{token_request.authorize_url} , then enter pin"
|
19
|
-
pin = reader.get_line
|
20
|
-
begin
|
21
|
-
access_token = token_request.get_access_token(oauth_verifier: pin)
|
22
|
-
account_settings = {
|
23
|
-
oauth_token: access_token.token,
|
24
|
-
oauth_token_secret: access_token.secret,
|
25
|
-
endpoint: opts[:endpoint]
|
26
|
-
}
|
27
|
-
config.create_unless_exists!
|
28
|
-
config.accounts[opts[:account]] = account_settings
|
29
|
-
config.save!
|
30
|
-
rescue OAuth::Unauthorized
|
31
|
-
reader.puts_above "Couldn't authenticate you, did you enter the pin correctly?"
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
end
|
36
|
-
end
|
data/lib/twat/actions/delete.rb
DELETED
data/lib/twat/actions/follow.rb
DELETED
@@ -1,148 +0,0 @@
|
|
1
|
-
module Twat
|
2
|
-
POLLING_RESOLUTION = 20
|
3
|
-
|
4
|
-
class NoSuchTweet < Exception; end
|
5
|
-
|
6
|
-
class TweetStack
|
7
|
-
# A circularly linked list representing all of the tweets printed thus far,
|
8
|
-
# for the purposes of retrieving them after being printed
|
9
|
-
def initialize
|
10
|
-
@stack = {}
|
11
|
-
@_next = 0
|
12
|
-
end
|
13
|
-
|
14
|
-
def [] k
|
15
|
-
@stack[k]
|
16
|
-
end
|
17
|
-
|
18
|
-
def << v
|
19
|
-
@stack[nxt] = v
|
20
|
-
end
|
21
|
-
|
22
|
-
def include? k
|
23
|
-
@stack.keys.include?(k)
|
24
|
-
end
|
25
|
-
|
26
|
-
def last
|
27
|
-
# I see the irony
|
28
|
-
@_next
|
29
|
-
end
|
30
|
-
|
31
|
-
private
|
32
|
-
|
33
|
-
def nxt
|
34
|
-
if @_next == 99
|
35
|
-
@_next = 1
|
36
|
-
else
|
37
|
-
@_next += 1
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
end
|
42
|
-
|
43
|
-
|
44
|
-
class Actions
|
45
|
-
|
46
|
-
def initialize
|
47
|
-
@failcount = 0
|
48
|
-
|
49
|
-
def reader.filter
|
50
|
-
case @buf.length
|
51
|
-
when 140
|
52
|
-
_print "[31m"
|
53
|
-
when 139
|
54
|
-
_print "[39m"
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def fail_or_bail
|
60
|
-
if @failcount > 2
|
61
|
-
puts "3 consecutive failures, giving up"
|
62
|
-
else
|
63
|
-
@failcount += 1
|
64
|
-
return true
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
def follow
|
69
|
-
twitter_auth
|
70
|
-
failcount = 0
|
71
|
-
@tweetstack = TweetStack.new
|
72
|
-
|
73
|
-
# Get 5 tweets
|
74
|
-
tweets = Twitter.home_timeline(:count => 5)
|
75
|
-
while true do
|
76
|
-
begin
|
77
|
-
last_id = process_followed(tweets) if tweets.any?
|
78
|
-
(config.polling_interval * POLLING_RESOLUTION).times do
|
79
|
-
begin
|
80
|
-
reader.tick
|
81
|
-
reader.each_line { |inp| handle_input(inp) }
|
82
|
-
rescue TweetTooLong
|
83
|
-
puts "Too long".red
|
84
|
-
end
|
85
|
-
sleep 1.0/POLLING_RESOLUTION
|
86
|
-
end
|
87
|
-
tweets = Twitter.home_timeline(:since_id => last_id)
|
88
|
-
failcount = 0
|
89
|
-
rescue Interrupt
|
90
|
-
break
|
91
|
-
rescue Twitter::Error::ServiceUnavailable
|
92
|
-
break unless fail_or_bail
|
93
|
-
sleeptime = 60 * (@failcount + 1)
|
94
|
-
reader.puts_above "#{"(__-){".red}: the fail whale has been rolled out, sleeping for #{sleeptime} seconds"
|
95
|
-
sleep sleeptime
|
96
|
-
rescue Errno::ECONNRESET, Errno::ETIMEDOUT, SocketError
|
97
|
-
break unless fail_or_bail
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
private
|
103
|
-
|
104
|
-
# The handling of the shared variable in the follow code makes a shitton
|
105
|
-
# more sense in the context of a class (ala the subcommands branch). For
|
106
|
-
# now the kludginess must be tolerated
|
107
|
-
|
108
|
-
# @return last_id
|
109
|
-
def process_followed(tweets)
|
110
|
-
last_id = nil
|
111
|
-
tweets.reverse.each do |tweet|
|
112
|
-
id = @tweetstack << tweet
|
113
|
-
beep if config.beep? && tweet.text.mentions?(config.account_name)
|
114
|
-
reader.puts_above format(tweet, @tweetstack.last)
|
115
|
-
last_id = tweet.id
|
116
|
-
end
|
117
|
-
|
118
|
-
return last_id
|
119
|
-
end
|
120
|
-
|
121
|
-
def handle_input(inp)
|
122
|
-
case inp
|
123
|
-
when /[rR][tT] ([0-9]{1,2})/
|
124
|
-
begin
|
125
|
-
retweet($1.to_i)
|
126
|
-
rescue NoSuchTweet
|
127
|
-
print "No such tweet\n".red
|
128
|
-
end
|
129
|
-
when /follow (.*)/
|
130
|
-
follow_user($1)
|
131
|
-
when /test/
|
132
|
-
reader.puts_above "Testline!"
|
133
|
-
else
|
134
|
-
# Assume they want to tweet something
|
135
|
-
raise TweetTooLong if inp.length > 140
|
136
|
-
|
137
|
-
Twitter.update(inp)
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
def retweet(idx)
|
142
|
-
raise NoSuchTweet unless @tweetstack.include?(idx)
|
143
|
-
Twitter.retweet(@tweetstack[idx].id)
|
144
|
-
end
|
145
|
-
|
146
|
-
end
|
147
|
-
|
148
|
-
end
|
data/lib/twat/actions/show.rb
DELETED
data/lib/twat/actions/tweet.rb
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
module Twat
|
2
|
-
class Actions
|
3
|
-
|
4
|
-
def user_feed
|
5
|
-
twitter_auth
|
6
|
-
|
7
|
-
begin
|
8
|
-
Twitter.user_timeline(opts[:user], :count => opts[:count]).each do |tweet|
|
9
|
-
format(tweet)
|
10
|
-
end
|
11
|
-
rescue Twitter::NotFound
|
12
|
-
puts "#{opts[:user].bold.red} doesn't appear to be a valid user"
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
end
|
17
|
-
end
|