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.
Files changed (71) hide show
  1. data/.rvmrc +1 -1
  2. data/.travis.yml +6 -0
  3. data/Gemfile +1 -4
  4. data/Gemfile.lock +43 -23
  5. data/Gemfile.travis +12 -0
  6. data/Rakefile +9 -0
  7. data/TODO +4 -1
  8. data/lib/twat.rb +30 -26
  9. data/lib/twat/argparse.rb +36 -79
  10. data/lib/twat/config.rb +22 -34
  11. data/lib/twat/endpoint.rb +11 -5
  12. data/lib/twat/endpoints/base.rb +39 -0
  13. data/lib/twat/endpoints/identica.rb +5 -1
  14. data/lib/twat/endpoints/twitter.rb +5 -1
  15. data/lib/twat/exceptions.rb +93 -46
  16. data/lib/twat/follow_mixin.rb +134 -0
  17. data/lib/twat/options.rb +28 -12
  18. data/lib/twat/subcommand.rb +34 -0
  19. data/lib/twat/subcommands/add.rb +17 -0
  20. data/lib/twat/subcommands/base.rb +122 -0
  21. data/lib/twat/subcommands/config.rb +15 -0
  22. data/lib/twat/subcommands/delete.rb +24 -0
  23. data/lib/twat/subcommands/finger.rb +23 -0
  24. data/lib/twat/subcommands/follow.rb +18 -0
  25. data/lib/twat/subcommands/follow_tag.rb +19 -0
  26. data/lib/twat/subcommands/list.rb +17 -0
  27. data/lib/twat/subcommands/mentions.rb +20 -0
  28. data/lib/twat/subcommands/set.rb +19 -0
  29. data/lib/twat/subcommands/track.rb +33 -0
  30. data/lib/twat/subcommands/update.rb +21 -0
  31. data/lib/twat/subcommands/update_config.rb +16 -0
  32. data/lib/twat/subcommands/version.rb +14 -0
  33. data/lib/twat/twatopt.rb +0 -0
  34. data/lib/twat/tweetstack.rb +38 -0
  35. data/lib/twat/version.rb +7 -0
  36. data/man/twat.1 +8 -5
  37. data/spec/argparse_spec.rb +48 -0
  38. data/spec/helpers/environment.rb +47 -0
  39. data/spec/helpers/fileutils.rb +18 -0
  40. data/spec/helpers/fixtures/core.rb +30 -0
  41. data/spec/helpers/fixtures/migrations.rb +13 -0
  42. data/spec/helpers/oauth.rb +15 -0
  43. data/spec/spec_helper.rb +13 -0
  44. data/spec/subcommands/add_spec.rb +61 -0
  45. data/spec/subcommands/config_spec.rb +13 -0
  46. data/spec/subcommands/delete_spec.rb +12 -0
  47. data/spec/subcommands/finger_spec.rb +41 -0
  48. data/spec/subcommands/follow_spec.rb +12 -0
  49. data/spec/subcommands/follow_tag_spec.rb +52 -0
  50. data/spec/subcommands/list_spec.rb +8 -0
  51. data/spec/subcommands/mentions_spec.rb +24 -0
  52. data/spec/subcommands/set_spec.rb +126 -0
  53. data/spec/subcommands/track_spec.rb +23 -0
  54. data/spec/subcommands/update_config_spec.rb +23 -0
  55. data/spec/subcommands/update_spec.rb +12 -0
  56. data/spec/subcommands/version_spec.rb +12 -0
  57. data/spec/twat_cli_spec.rb +102 -0
  58. data/spec/twat_spec.rb +12 -0
  59. data/twat.gemspec +6 -2
  60. metadata +135 -22
  61. data/lib/twat/actions.rb +0 -85
  62. data/lib/twat/actions/add.rb +0 -36
  63. data/lib/twat/actions/delete.rb +0 -14
  64. data/lib/twat/actions/follow.rb +0 -148
  65. data/lib/twat/actions/follow_user.rb +0 -11
  66. data/lib/twat/actions/setoption.rb +0 -14
  67. data/lib/twat/actions/show.rb +0 -12
  68. data/lib/twat/actions/tweet.rb +0 -14
  69. data/lib/twat/actions/updateconfig.rb +0 -9
  70. data/lib/twat/actions/user_feed.rb +0 -17
  71. data/lib/twat/actions/version.rb +0 -9
@@ -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
- {"&lt;" => "<", "&gt;" => ">", "&amp;" => "&", "&quot;" => '"', "&#39;" => '\'' }.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
@@ -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
@@ -1,14 +0,0 @@
1
- module Twat
2
- class Actions
3
-
4
- def delete
5
- if config.accounts.delete(opts[:account])
6
- config.save!
7
- puts "Successfully deleted"
8
- else
9
- puts "No such account"
10
- end
11
- end
12
-
13
- end
14
- end
@@ -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 ""
53
- when 139
54
- _print ""
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
@@ -1,11 +0,0 @@
1
- module Twat
2
- class Actions
3
-
4
- def follow_user(user = nil)
5
- twitter_auth
6
-
7
- Twitter.follow(user || opts[:user])
8
- end
9
-
10
- end
11
- end
@@ -1,14 +0,0 @@
1
- module Twat
2
- class Actions
3
-
4
- def setoption
5
- k, v = opts[:optval].split("=")
6
- raise RequiresOptVal unless v
7
- options = Options.new
8
- options.send(:"#{k}=", v)
9
-
10
- puts "Successfully set #{k} as #{v}"
11
- end
12
-
13
- end
14
- end
@@ -1,12 +0,0 @@
1
- module Twat
2
- class Actions
3
-
4
- def show
5
- twitter_auth
6
- Twitter.home_timeline(:count => opts[:count]).reverse.each do |tweet|
7
- format(tweet)
8
- end
9
- end
10
-
11
- end
12
- end
@@ -1,14 +0,0 @@
1
- module Twat
2
- class Actions
3
-
4
- def tweet
5
- twitter_auth
6
-
7
- raise TweetTooLong if opts.msg.length > 140
8
-
9
- Twitter.update(opts.msg)
10
- #puts opts.msg
11
- end
12
-
13
- end
14
- end
@@ -1,9 +0,0 @@
1
- module Twat
2
- class Actions
3
-
4
- def updateconfig
5
- config.update!
6
- end
7
-
8
- end
9
- end
@@ -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
@@ -1,9 +0,0 @@
1
- module Twat
2
- class Actions
3
-
4
- def version
5
- puts "twat: #{VERSION_MAJOR}.#{VERSION_MINOR}.#{VERSION_PATCH}"
6
- end
7
-
8
- end
9
- end